diff options
256 files changed, 10635 insertions, 8217 deletions
diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 7f0bef02..59df7737 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml | |||
@@ -4,6 +4,7 @@ packages: | |||
4 | - eudev-dev | 4 | - eudev-dev |
5 | - gdk-pixbuf-dev | 5 | - gdk-pixbuf-dev |
6 | - json-c-dev | 6 | - json-c-dev |
7 | - libdisplay-info-dev | ||
7 | - libevdev-dev | 8 | - libevdev-dev |
8 | - libinput-dev | 9 | - libinput-dev |
9 | - libseat-dev | 10 | - libseat-dev |
@@ -12,16 +13,18 @@ packages: | |||
12 | - mesa-dev | 13 | - mesa-dev |
13 | - meson | 14 | - meson |
14 | - pango-dev | 15 | - pango-dev |
16 | - pcre2-dev | ||
15 | - pixman-dev | 17 | - pixman-dev |
16 | - scdoc | 18 | - scdoc |
17 | - wayland-dev | 19 | - wayland-dev |
18 | - wayland-protocols | 20 | - wayland-protocols |
19 | - xcb-util-image-dev | 21 | - xcb-util-image-dev |
20 | - xcb-util-wm-dev | 22 | - xcb-util-wm-dev |
21 | - xwayland | 23 | - xwayland-dev |
24 | - hwdata-dev | ||
22 | sources: | 25 | sources: |
23 | - https://github.com/swaywm/sway | 26 | - https://github.com/swaywm/sway |
24 | - https://github.com/swaywm/wlroots | 27 | - https://gitlab.freedesktop.org/wlroots/wlroots.git |
25 | tasks: | 28 | tasks: |
26 | - wlroots: | | 29 | - wlroots: | |
27 | cd wlroots | 30 | cd wlroots |
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index a8f1dfed..9972c01a 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml | |||
@@ -3,12 +3,14 @@ packages: | |||
3 | - cairo | 3 | - cairo |
4 | - gdk-pixbuf2 | 4 | - gdk-pixbuf2 |
5 | - json-c | 5 | - json-c |
6 | - libdisplay-info | ||
6 | - libegl | 7 | - libegl |
7 | - libinput | 8 | - libinput |
8 | - libxcb | 9 | - libxcb |
9 | - libxkbcommon | 10 | - libxkbcommon |
10 | - meson | 11 | - meson |
11 | - pango | 12 | - pango |
13 | - pcre2 | ||
12 | - scdoc | 14 | - scdoc |
13 | - wayland | 15 | - wayland |
14 | - wayland-protocols | 16 | - wayland-protocols |
@@ -16,9 +18,10 @@ packages: | |||
16 | - xcb-util-wm | 18 | - xcb-util-wm |
17 | - xorg-xwayland | 19 | - xorg-xwayland |
18 | - seatd | 20 | - seatd |
21 | - hwdata | ||
19 | sources: | 22 | sources: |
20 | - https://github.com/swaywm/sway | 23 | - https://github.com/swaywm/sway |
21 | - https://github.com/swaywm/wlroots | 24 | - https://gitlab.freedesktop.org/wlroots/wlroots.git |
22 | tasks: | 25 | tasks: |
23 | - wlroots: | | 26 | - wlroots: | |
24 | cd wlroots | 27 | cd wlroots |
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 1a3c8512..29c6312a 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml | |||
@@ -4,6 +4,7 @@ packages: | |||
4 | - devel/json-c | 4 | - devel/json-c |
5 | - devel/libevdev | 5 | - devel/libevdev |
6 | - devel/meson | 6 | - devel/meson |
7 | - devel/pcre2 | ||
7 | - devel/pkgconf | 8 | - devel/pkgconf |
8 | - graphics/cairo | 9 | - graphics/cairo |
9 | - graphics/gdk-pixbuf2 | 10 | - graphics/gdk-pixbuf2 |
@@ -19,14 +20,17 @@ packages: | |||
19 | - devel/libudev-devd | 20 | - devel/libudev-devd |
20 | - graphics/libdrm | 21 | - graphics/libdrm |
21 | - graphics/mesa-libs | 22 | - graphics/mesa-libs |
23 | - sysutils/libdisplay-info | ||
22 | - sysutils/seatd | 24 | - sysutils/seatd |
23 | - x11/libinput | 25 | - x11/libinput |
24 | - x11/libX11 | 26 | - x11/libX11 |
25 | - x11/pixman | 27 | - x11/pixman |
26 | - x11/xcb-util-wm | 28 | - x11/xcb-util-wm |
29 | - x11-servers/xwayland-devel | ||
30 | - misc/hwdata | ||
27 | sources: | 31 | sources: |
28 | - https://github.com/swaywm/sway | 32 | - https://github.com/swaywm/sway |
29 | - https://github.com/swaywm/wlroots | 33 | - https://gitlab.freedesktop.org/wlroots/wlroots.git |
30 | tasks: | 34 | tasks: |
31 | - setup: | | 35 | - setup: | |
32 | cd sway | 36 | cd sway |
diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 24ea869d..00000000 --- a/.clang-format +++ /dev/null | |||
@@ -1,18 +0,0 @@ | |||
1 | BasedOnStyle: LLVM | ||
2 | IndentWidth: 4 | ||
3 | TabWidth: 4 | ||
4 | UseTab: Always | ||
5 | BreakBeforeBraces: Attach | ||
6 | AllowShortIfStatementsOnASingleLine: false | ||
7 | IndentCaseLabels: false | ||
8 | SortIncludes: false | ||
9 | ColumnLimit: 80 | ||
10 | AlignAfterOpenBracket: DontAlign | ||
11 | BinPackParameters: true | ||
12 | BinPackArguments: true | ||
13 | ContinuationIndentWidth: 8 | ||
14 | AllowAllParametersOfDeclarationOnNextLine: false | ||
15 | AllowShortLoopsOnASingleLine: true | ||
16 | ReflowComments: false | ||
17 | AllowAllArgumentsOnNextLine: false | ||
18 | AlignOperands: DontAlign | ||
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8542b7b9..eba606e6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md | |||
@@ -18,6 +18,7 @@ labels: 'bug' | |||
18 | - **Debug Log:** | 18 | - **Debug Log:** |
19 | - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com. | 19 | - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com. |
20 | - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway. | 20 | - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway. |
21 | - Attach the **full** file, do not truncate it. | ||
21 | 22 | ||
22 | - **Configuration File:** | 23 | - **Configuration File:** |
23 | - Please try to produce with the default configuration. | 24 | - Please try to produce with the default configuration. |
@@ -6,6 +6,7 @@ bin/ | |||
6 | test/ | 6 | test/ |
7 | build/ | 7 | build/ |
8 | build-*/ | 8 | build-*/ |
9 | .cache/ | ||
9 | .lvimrc | 10 | .lvimrc |
10 | config-debug | 11 | config-debug |
11 | wayland-*-protocol.* | 12 | wayland-*-protocol.* |
diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000..35ee25b0 --- /dev/null +++ b/.mailmap | |||
@@ -0,0 +1 @@ | |||
Ronan Pigott <ronan@rjp.ie> <rpigott@berkeley.edu> | |||
diff --git a/README.ar.md b/README.ar.md new file mode 100644 index 00000000..4f9bf828 --- /dev/null +++ b/README.ar.md | |||
@@ -0,0 +1,91 @@ | |||
1 | # sway | ||
2 | |||
3 | sway | ||
4 | |||
5 | هو مدير للمجموعات المركبة لـ[Wayland] متواÙÙ‚ مع [i3] - | ||
6 | |||
7 | إقرأ [الأسئلة الشائعة](https://github.com/swaywm/sway/wiki) | ||
8 | |||
9 | انضم الى [قناة IRC](https://web.libera.chat/gamja/?channels=#sway) | ||
10 | |||
11 | |||
12 | ## تواقيع الإصدار | ||
13 | تٌوقع الإصدارات بـواسطة [E88F5E48] Ùˆ تÙنشر على [GitHub](https://github.com/swaywm/sway/releases) | ||
14 | |||
15 | ## التثبيت | ||
16 | |||
17 | ### بإستخدام الØزم | ||
18 | |||
19 | يتوÙر Sway للعديد من التوزيعات، Øاول تثبيت Øزمة "sway" لتوزيعتك | ||
20 | ### التجميع من المصدر | ||
21 | إطلع على [صÙØØ© الويكي هذه](https://github.com/swaywm/sway/wiki/Development-Setup) إذا أردت بناء الـHEAD من sway Ùˆ wlroots لأغراض الÙØص والتطوير | ||
22 | |||
23 | تثبيت اللوازم: | ||
24 | * meson \* | ||
25 | * [wlroots] | ||
26 | * wayland | ||
27 | * wayland-protocols \* | ||
28 | * pcre2 | ||
29 | * json-c | ||
30 | * pango | ||
31 | * cairo | ||
32 | * gdk-pixbuf2 (optional: system tray) | ||
33 | * [scdoc] (optional: man pages) \* | ||
34 | * git (optional: version info) \* | ||
35 | |||
36 | _\* Compile-time dep_ | ||
37 | |||
38 | Ù†ÙØ° هذه الأوامر: | ||
39 | |||
40 | meson build/ | ||
41 | ninja -C build/ | ||
42 | sudo ninja -C build/ install | ||
43 | |||
44 | ## الإعدادات | ||
45 | |||
46 | إذا كنت بالÙعل تستخدم i3ØŒ Ùعليك نسخ إعدادات i3 لديك إلى `~/.config/sway/config` وسو٠تعمل تلقائياً. | ||
47 | |||
48 | Ùˆ إلا عليك نسخ مل٠الإعدادات النموذج إلى `config/sway/config` الموجود عادةً ÙÙŠ `/etc/sway/config.` | ||
49 | |||
50 | |||
51 | ## التشغيل | ||
52 | |||
53 | شغل `sway` بإستخدام أمر TTY. | ||
54 | قد يعمل بعض مدراء العرض مع أنهم غير مدعومون من sway | ||
55 | (gdm مثلاً يعمل بشكل جيد إلى Øد ما) | ||
56 | |||
57 | [en]: https://github.com/swaywm/sway#readme | ||
58 | [ar]: README.ar.md | ||
59 | [cs]: README.cs.md | ||
60 | [de]: README.de.md | ||
61 | [dk]: README.dk.md | ||
62 | [es]: README.es.md | ||
63 | [fr]: README.fr.md | ||
64 | [ge]: README.ge.md | ||
65 | [gr]: README.gr.md | ||
66 | [hi]: README.hi.md | ||
67 | [hu]: README.hu.md | ||
68 | [ir]: README.ir.md | ||
69 | [it]: README.it.md | ||
70 | [ja]: README.ja.md | ||
71 | [ko]: README.ko.md | ||
72 | [nl]: README.nl.md | ||
73 | [no]: README.no.md | ||
74 | [pl]: README.pl.md | ||
75 | [pt]: README.pt.md | ||
76 | [ro]: README.ro.md | ||
77 | [ru]: README.ru.md | ||
78 | [sv]: README.sv.md | ||
79 | [tr]: README.tr.md | ||
80 | [uk]: README.uk.md | ||
81 | [zh-CN]: README.zh-CN.md | ||
82 | [zh-TW]: README.zh-TW.md | ||
83 | [i3]: https://i3wm.org/ | ||
84 | [Wayland]: http://wayland.freedesktop.org/ | ||
85 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
86 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway | ||
87 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
88 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
89 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
90 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
91 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.cs.md b/README.cs.md new file mode 100644 index 00000000..41efba54 --- /dev/null +++ b/README.cs.md | |||
@@ -0,0 +1,89 @@ | |||
1 | # sway | ||
2 | |||
3 | [English][en] - **[ÄŒesky][cs]** - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [Svenska][sv] - [Ελληνικά][gr] - [हिनà¥à¤¦à¥€][hi] - [Magyar][hu] - [Ùارسی][ir] - [Italiano][it] - [日本語][ja] - [í•œêµì–´][ko] - [Nederlands][nl] - [Polski][pl] - [Português][pt] - [Română][ro] - [РуÑÑкий][ru] - [Türkçe][tr] - [УкраїнÑька][uk] - [ä¸æ–‡-简体][zh-CN] - [ä¸æ–‡-ç¹é«”][zh-TW] | ||
4 | |||
5 | sway je s [i3] kompatibilnà [Wayland] kompozitor. PÅ™eÄtÄ›te si [FAQ]. PÅ™ipojte se na | ||
6 | [IRC kanál][IRC channel] \(#sway na irc.libera.chat). | ||
7 | |||
8 | ## Podpisy vydánà | ||
9 | |||
10 | Vydánà jsou podepsána [E88F5E48] a publikována [na GitHubu][GitHub releases]. | ||
11 | |||
12 | ## Instalace | ||
13 | |||
14 | ### Z balÃÄků | ||
15 | |||
16 | Sway je dostupný ve spoustÄ› distribucÃ. Zkuste nainstalovat balÃÄek "sway" ve vaÅ¡Ã | ||
17 | distribuci. | ||
18 | |||
19 | ### Kompilace ze zdrojových kódů | ||
20 | |||
21 | PodÃvejte se na [tuto stránku wiki][Development setup], pokud chcete sestavit HEAD | ||
22 | sway a wlroots pro testovánà nebo vývoj. | ||
23 | |||
24 | Nainstalujte závislosti: | ||
25 | |||
26 | * meson \* | ||
27 | * [wlroots] | ||
28 | * wayland | ||
29 | * wayland-protocols \* | ||
30 | * pcre2 | ||
31 | * json-c | ||
32 | * pango | ||
33 | * cairo | ||
34 | * gdk-pixbuf2 (volitelné: oznamovacà oblast) | ||
35 | * [scdoc] (volitelné: manuálové stránky) \* | ||
36 | * git (volitelné: informace o verzi) \* | ||
37 | |||
38 | _\* Závislost pouze pro sestavenÃ_ | ||
39 | |||
40 | SpusÅ¥te tyto pÅ™Ãkazy: | ||
41 | |||
42 | meson build/ | ||
43 | ninja -C build/ | ||
44 | sudo ninja -C build/ install | ||
45 | |||
46 | ## Konfigurace | ||
47 | |||
48 | Pokud již použÃváte i3, zkopÃrujte svou konfiguraci i3 do `~/.config/sway/config` | ||
49 | a ta bude ihned fungovat. Jinak zkopÃrujte do `~/.config/sway/config` ukázkový | ||
50 | konfiguraÄnà soubor. Ten se obvykle nacházà v `/etc/sway/config`. | ||
51 | Pro vÃce informacà o konfiguraci spusÅ¥te `man 5 sway`. | ||
52 | |||
53 | ## Spuštěnà | ||
54 | |||
55 | Spusťte `sway` z TTY. Některé správce zobrazenà mohou fungovat, ale nejsou | ||
56 | podporovány sway (je známo, že gdm funguje docela dobře). | ||
57 | |||
58 | [en]: https://github.com/swaywm/sway#readme | ||
59 | [cs]: README.cs.md | ||
60 | [de]: README.de.md | ||
61 | [dk]: README.dk.md | ||
62 | [es]: README.es.md | ||
63 | [fr]: README.fr.md | ||
64 | [sv]: README.sv.md | ||
65 | [gr]: README.gr.md | ||
66 | [hi]: README.hi.md | ||
67 | [hu]: README.hu.md | ||
68 | [ir]: README.ir.md | ||
69 | [it]: README.it.md | ||
70 | [ja]: README.ja.md | ||
71 | [ko]: README.ko.md | ||
72 | [nl]: README.nl.md | ||
73 | [pl]: README.pl.md | ||
74 | [pt]: README.pt.md | ||
75 | [ro]: README.ro.md | ||
76 | [ru]: README.ru.md | ||
77 | [tr]: README.tr.md | ||
78 | [uk]: README.uk.md | ||
79 | [zh-CN]: README.zh-CN.md | ||
80 | [zh-TW]: README.zh-TW.md | ||
81 | [i3]: https://i3wm.org/ | ||
82 | [Wayland]: http://wayland.freedesktop.org/ | ||
83 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
84 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway | ||
85 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
86 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
87 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
88 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
89 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.de.md b/README.de.md index 142c738c..68b411d9 100644 --- a/README.de.md +++ b/README.de.md | |||
@@ -1,30 +1,30 @@ | |||
1 | # Sway | 1 | # Sway |
2 | Sway ist ein [i3](https://i3wm.org/)-kompatibler [Wayland](http://wayland.freedesktop.org/)-Compositor. Lies die [FAQ](https://github.com/swaywm/sway/wiki). Tritt dem [IRC Channel](https://web.libera.chat/?channels=#sway) bei (#sway on irc.libera.chat; Englisch). | 2 | Sway ist ein [i3](https://i3wm.org/)-kompatibler [Wayland](http://wayland.freedesktop.org/)-Compositor. Lies die [FAQ](https://github.com/swaywm/sway/wiki). Tritt dem [IRC Channel](https://web.libera.chat/gamja/?channels=#sway) bei (#sway on irc.libera.chat; Englisch). |
3 | 3 | ||
4 | ## Signaturen | 4 | ## Signaturen |
5 | Jedes Release wird mit dem PGP-Schlüssel [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) signiert und auf GitHub veröffentlicht. | 5 | Jedes Release wird mit dem PGP-Schlüssel [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) signiert und [auf GitHub](https://github.com/swaywm/sway/releases) veröffentlicht. |
6 | 6 | ||
7 | ## Installation | 7 | ## Installation |
8 | ### Mit der Paketverwaltung | ||
9 | Sway kann in vielen Distributionen direkt durch die Paketverwaltung installiert werden. Das Paket sollte "sway" heißen. Falls es kein solches Paket gibt, kannst du im [Wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) (englisch) nach mehr Informationen bezüglich deiner Distribution suchen. | ||
10 | 8 | ||
11 | Falls du sway für deine eigene Distribution als Paket bereitstellen möchtest, solltest du die Entwickler per IRC oder E-Mail (sir@cmpwn.com) kontaktieren. | 9 | ### Über die Paketverwaltung |
10 | |||
11 | Sway kann in vielen Distributionen direkt durch die Paketverwaltung installiert werden. Versuche einfach das Packet "sway" zu installieren. | ||
12 | 12 | ||
13 | ### Quellcode selbst kompilieren | 13 | ### Quellcode selbst kompilieren |
14 | 14 | ||
15 | sway benötigt die folgenden Pakete: | 15 | sway benötigt die folgenden Pakete: |
16 | 16 | ||
17 | * meson\* | 17 | * meson\* |
18 | * [wlroots](https://github.com/swaywm/wlroots) | 18 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
19 | * wayland | 19 | * wayland |
20 | * wayland-protocols\* | 20 | * wayland-protocols\* |
21 | * pcre | 21 | * pcre2 |
22 | * json-c | 22 | * json-c |
23 | * pango | 23 | * pango |
24 | * cairo | 24 | * cairo |
25 | * gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt) | 25 | * gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt) |
26 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc)\* (Optional, wird für die Dokumentation (Man Pages) benötigt) | 26 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (Optional, wird für die Dokumentation (Man Pages) benötigt)\* |
27 | * git\* | 27 | * git (Optional: Versionsinfo)\* |
28 | 28 | ||
29 | _\*Werden nur während des Kompilierens benötigt_ | 29 | _\*Werden nur während des Kompilierens benötigt_ |
30 | 30 | ||
@@ -34,12 +34,6 @@ Führe die folgenden Befehle aus: | |||
34 | ninja -C build | 34 | ninja -C build |
35 | sudo ninja -C build install | 35 | sudo ninja -C build install |
36 | 36 | ||
37 | Falls dein System nicht logind benutzt, musst du sway noch die passenden Berechtigungen geben: | ||
38 | |||
39 | sudo chmod a+s /usr/local/bin/sway | ||
40 | |||
41 | Sway läuft nur in der Startphase mit Root-Rechten. | ||
42 | |||
43 | ## Konfiguration | 37 | ## Konfiguration |
44 | 38 | ||
45 | Falls du von i3 migrierst, kannst du deine Konfigurationsdatei nach `~/.config/sway/config` kopieren und die Einstellungen sollten ohne Weiteres funktionieren. Ansonsten kannst du die Beispielkonfiguration, die normalerweise in `/etc/sway/config` liegt, nach `~/.config/sway/config` kopieren. Die Dokumentation zur Konfigurationsdatei findest du in `man 5 sway`. | 39 | Falls du von i3 migrierst, kannst du deine Konfigurationsdatei nach `~/.config/sway/config` kopieren und die Einstellungen sollten ohne Weiteres funktionieren. Ansonsten kannst du die Beispielkonfiguration, die normalerweise in `/etc/sway/config` liegt, nach `~/.config/sway/config` kopieren. Die Dokumentation zur Konfigurationsdatei findest du in `man 5 sway`. |
diff --git a/README.dk.md b/README.dk.md index 79a0df93..5ce94cde 100644 --- a/README.dk.md +++ b/README.dk.md | |||
@@ -1,41 +1,43 @@ | |||
1 | # Sway | 1 | # Sway |
2 | 2 | ||
3 | Sway er en [i3](https://i3wm.org/)-kompatibel [Wayland](http://wayland.freedesktop.org/) compositor. | 3 | Sway er en [i3]-kompatibel [Wayland] compositor. Læs [Ofte stillede spørgsmål]. |
4 | Læs [Ofte stillede spørgsmål](https://github.com/swaywm/sway/wiki). | 4 | Deltag på [IRC kanalen][IRC kanal] \(#sway på irc.libera.chat). |
5 | Deltag på [IRC kanalen](https://web.libera.chat/?channels=#sway) (#sway på irc.libera.chat). | ||
6 | 5 | ||
7 | ## Udgivelses Signaturer | 6 | ## Udgivelses Signaturer |
8 | 7 | ||
9 | Udgivelser er signeret med [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) | 8 | Udgivelser er signeret med [E88F5E48] og publiceret [på GitHub][GitHub |
10 | og publiseret på [GitHub](https://github.com/swaywm/sway/releases). | 9 | releases]. |
11 | 10 | ||
12 | ## Installation | 11 | ## Installation |
13 | 12 | ||
14 | ### Fra Pakker | 13 | ### Fra pakker |
15 | 14 | ||
16 | Sway er tilgængelig i mange distributioner. Prøv at installere pakken "sway". Hvis den ikke er tilgængelig, så tjek [denne wiki-side](https://github.com/swaywm/sway/wiki/Unsupported-packages) | 15 | Sway er tilgængelig i mange distributioner. Prøv at installere "sway" pakken |
17 | for information om installation til din(e) distribution(er). | 16 | fra din. |
18 | 17 | ||
19 | Hvis du er interesseret i at lave en Sway pakke til din distribution, burde du besøge IRC | 18 | Hvis du er interesseret i at pakke Sway til din distribution, kan du tage forbi |
20 | kanalen eller sende en e-mail til sir@cmpwn.com for rådgivning. | 19 | IRC kanalen eller sende en email til sir@cmpwn.com for rådgivning. |
21 | 20 | ||
22 | ### Kompilering fra kildekode | 21 | ### Kompilering fra kildekode |
23 | 22 | ||
24 | Installation afhænger af følgende programmer: | 23 | Se [denne wiki-side][Opsætning til udvikling] hvis du vil bygge HEAD af sway og |
24 | wlroots til test eller udvikling. | ||
25 | |||
26 | Installationsafhængigheder: | ||
25 | 27 | ||
26 | * meson \* | 28 | * meson \* |
27 | * [wlroots](https://github.com/swaywm/wlroots) | 29 | * [wlroots] |
28 | * wayland | 30 | * wayland |
29 | * wayland-protocols \* | 31 | * wayland-protocols \* |
30 | * pcre | 32 | * pcre2 |
31 | * json-c | 33 | * json-c |
32 | * pango | 34 | * pango |
33 | * cairo | 35 | * cairo |
34 | * gdk-pixbuf2 (valgfrit tillæg: system tray) | 36 | * gdk-pixbuf2 (valgfrit: system tray) |
35 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (valgfrit tillæg: man pages) \* | 37 | * [scdoc] (valgfrit: man pages) \* |
36 | * git \* | 38 | * git \* |
37 | 39 | ||
38 | _\*Kompiler krav_ | 40 | _\*Kompileringsafhængighed_ |
39 | 41 | ||
40 | Kør følgende kommandoer: | 42 | Kør følgende kommandoer: |
41 | 43 | ||
@@ -43,21 +45,24 @@ Kør følgende kommandoer: | |||
43 | ninja -C build | 45 | ninja -C build |
44 | sudo ninja -C build install | 46 | sudo ninja -C build install |
45 | 47 | ||
46 | På systemer uden 'logind', behøver du at sætte ejerens bruger-id for Sways eksekverbare filer - såkaldt SUID (Set owner User ID): | ||
47 | |||
48 | sudo chmod a+s /usr/local/bin/sway | ||
49 | |||
50 | Sway vil frasige sig 'root' tilladelser kort efter opstart | ||
51 | |||
52 | ## Konfiguration | 48 | ## Konfiguration |
53 | 49 | ||
54 | Hvis du allerede bruger i3, bør du kopiere din i3-konfiguration til `~/.config/sway/config` og | 50 | Hvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til |
55 | det vil bare fungerer. Ellers skal du kopiere eksempel konfigurations filen til | 51 | `~/.config/sway/config`. Ellers skal du kopiere eksempelkonfigurationsfilen til |
56 | `~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`. | 52 | `~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`. Kør |
57 | Kør `man 5 sway` for at få oplysninger om konfigurationen. | 53 | `man 5 sway` for at få oplysninger om konfigurationen. |
58 | 54 | ||
59 | ## Kører | 55 | ## Eksekvering |
60 | 56 | ||
61 | Kør `sway` fra en TTY. Nogle display managers fungerer muligvis, men understøttes ikke af | 57 | Kør `sway` fra en TTY. Nogle display managers kan fungere, men Sway yder ikke |
62 | Sway (gdm er kendt for at fungere temmelig godt). | 58 | support til dem (gdm er kendt for at fungere temmelig godt). |
63 | 59 | ||
60 | [i3]: https://i3wm.org/ | ||
61 | [Wayland]: http://wayland.freedesktop.org/ | ||
62 | [Ofte stillede spørgsmål]: https://github.com/swaywm/sway/wiki | ||
63 | [IRC kanal]: https://web.libera.chat/gamja/?channels=#sway | ||
64 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
65 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
66 | [Opsætning til udvikling]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
67 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
68 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.es.md b/README.es.md index 838778d4..1f1657df 100644 --- a/README.es.md +++ b/README.es.md | |||
@@ -1,7 +1,7 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con [i3](https://i3wm.org/). | 3 | sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con [i3](https://i3wm.org/). |
4 | Lea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](https://web.libera.chat/?channels=#sway) (#sway on | 4 | Lea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway on |
5 | irc.libera.chat). | 5 | irc.libera.chat). |
6 | 6 | ||
7 | ## Firmas de las versiones | 7 | ## Firmas de las versiones |
@@ -25,10 +25,10 @@ escriba un email a sir@cmpwn.com | |||
25 | Instale las dependencias: | 25 | Instale las dependencias: |
26 | 26 | ||
27 | * meson \* | 27 | * meson \* |
28 | * [wlroots](https://github.com/swaywm/wlroots) | 28 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
29 | * wayland | 29 | * wayland |
30 | * wayland-protocols \* | 30 | * wayland-protocols \* |
31 | * pcre | 31 | * pcre2 |
32 | * json-c | 32 | * json-c |
33 | * pango | 33 | * pango |
34 | * cairo | 34 | * cairo |
@@ -44,12 +44,6 @@ Desde su consola, ejecute las órdenes: | |||
44 | ninja -C build | 44 | ninja -C build |
45 | sudo ninja -C build install | 45 | sudo ninja -C build install |
46 | 46 | ||
47 | En sistemas sin `logind`, necesitará cambiar los permisos del archivo compilado de sway: | ||
48 | |||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | |||
51 | Sway abandonará los permisos de super-usuario al poco de arrancar. | ||
52 | |||
53 | ## Configuración | 47 | ## Configuración |
54 | 48 | ||
55 | Si ya utiliza i3, copie su archivo de configuración de i3 a `~/.config/sway/config` y | 49 | Si ya utiliza i3, copie su archivo de configuración de i3 a `~/.config/sway/config` y |
diff --git a/README.fr.md b/README.fr.md index 86d434e5..7752fc70 100644 --- a/README.fr.md +++ b/README.fr.md | |||
@@ -35,7 +35,7 @@ Installez les dépendances : | |||
35 | * [wlroots] | 35 | * [wlroots] |
36 | * wayland | 36 | * wayland |
37 | * wayland-protocols \* | 37 | * wayland-protocols \* |
38 | * pcre | 38 | * pcre2 |
39 | * json-c | 39 | * json-c |
40 | * pango | 40 | * pango |
41 | * cairo | 41 | * cairo |
@@ -51,12 +51,6 @@ Exécutez ces commandes : | |||
51 | ninja -C build | 51 | ninja -C build |
52 | sudo ninja -C build install | 52 | sudo ninja -C build install |
53 | 53 | ||
54 | Sur les systèmes sans logind, vous devez suid le binaire de sway : | ||
55 | |||
56 | sudo chmod a+s /usr/local/bin/sway | ||
57 | |||
58 | Sway se débarassera des permissions *root* peu de temps après le démarrage. | ||
59 | |||
60 | ## Configuration | 54 | ## Configuration |
61 | 55 | ||
62 | Si vous utilisez déjà i3, copiez votre configuration i3 vers | 56 | Si vous utilisez déjà i3, copiez votre configuration i3 vers |
@@ -74,10 +68,10 @@ bien fonctionner). | |||
74 | [Wayland]: http://wayland.freedesktop.org/ | 68 | [Wayland]: http://wayland.freedesktop.org/ |
75 | [i3]: https://i3wm.org/ | 69 | [i3]: https://i3wm.org/ |
76 | [FAQ]: https://github.com/swaywm/sway/wiki | 70 | [FAQ]: https://github.com/swaywm/sway/wiki |
77 | [canal IRC]: https://web.libera.chat/?channels=#sway | 71 | [canal IRC]: https://web.libera.chat/gamja/?channels=#sway |
78 | [abdelq]: https://github.com/abdelq | 72 | [abdelq]: https://github.com/abdelq |
79 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | 73 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 |
80 | [versions GitHub]: https://github.com/swaywm/sway/releases | 74 | [versions GitHub]: https://github.com/swaywm/sway/releases |
81 | [Configuration de développement]: https://github.com/swaywm/sway/wiki/Development-Setup | 75 | [Configuration de développement]: https://github.com/swaywm/sway/wiki/Development-Setup |
82 | [wlroots]: https://github.com/swaywm/wlroots | 76 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots |
83 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | 77 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc |
diff --git a/README.ge.md b/README.ge.md new file mode 100644 index 00000000..bb8b9a34 --- /dev/null +++ b/README.ge.md | |||
@@ -0,0 +1,61 @@ | |||
1 | # sway | ||
2 | |||
3 | sway áƒáƒ ის [i3]-თáƒáƒ•áƒ¡áƒ”ბáƒáƒ“ი [Wayland]-ის კáƒáƒ›áƒžáƒáƒ–იტáƒáƒ ი. მეტი ინფáƒáƒ მáƒáƒªáƒ˜áƒ˜áƒ¡áƒ—ვის იხილეთ | ||
4 | [FAQ]. დáƒáƒ£áƒ™áƒáƒ•áƒ¨áƒ˜áƒ დით [IRC áƒáƒ ხს][IRC channel] \(#sway irc.libera.chat-ზე). | ||
5 | |||
6 | ## გáƒáƒ›áƒáƒ¨áƒ•áƒ”ბის ხელმáƒáƒ¬áƒ”რები | ||
7 | |||
8 | გáƒáƒ›áƒáƒ¨áƒ•áƒ”ბები ხელმáƒáƒ¬áƒ”რილირ[E88F5E48]-ით დრგáƒáƒ›áƒáƒ¥áƒ•áƒ”ყნებულირ[GitHub-ზე][GitHub releases]. | ||
9 | |||
10 | ## ინსტáƒáƒšáƒáƒªáƒ˜áƒ | ||
11 | |||
12 | ### რეპáƒáƒ–იტáƒáƒ იიდáƒáƒœ | ||
13 | |||
14 | Sway áƒáƒ ის ხელმისáƒáƒ¬áƒ•áƒ“áƒáƒ›áƒ˜ ბევრი დისტრიბუტáƒáƒªáƒ˜áƒ˜áƒ¡áƒ—ვის. ცáƒáƒ“ეთ "sway" პáƒáƒ™áƒ”ტის ინსტáƒáƒšáƒáƒªáƒ˜áƒ თქვენთვის. | ||
15 | |||
16 | ### კáƒáƒ“ის კáƒáƒ›áƒžáƒ˜áƒšáƒáƒªáƒ˜áƒ | ||
17 | |||
18 | იხილეთ [ეს ვიკი გვერდი][Development setup] თუ გინდáƒáƒ— რáƒáƒ› áƒáƒáƒ¬áƒ§áƒáƒ— sway დრwlroots სáƒáƒ¢áƒ”სტáƒáƒ“ áƒáƒœ დეველáƒáƒžáƒ›áƒ”ნტისთვის. | ||
19 | |||
20 | დáƒáƒáƒ˜áƒœáƒ¡áƒ¢áƒáƒšáƒ˜áƒ ეთ დáƒáƒ›áƒáƒ™áƒ˜áƒ“ებულებები: | ||
21 | |||
22 | * meson \* | ||
23 | * [wlroots] | ||
24 | * wayland | ||
25 | * wayland-protocols \* | ||
26 | * pcre2 | ||
27 | * json-c | ||
28 | * pango | ||
29 | * cairo | ||
30 | * gdk-pixbuf2 (áƒáƒ¡áƒ”ვე áƒáƒ ჩევითიáƒ: system tray) | ||
31 | * [scdoc] (áƒáƒ¡áƒ”ვე áƒáƒ ჩევითიáƒ: man pages) \* | ||
32 | * git (áƒáƒ¡áƒ”ვე áƒáƒ ჩევითიáƒ: version info) \* | ||
33 | |||
34 | _\* Compile-time dep_ | ||
35 | |||
36 | გáƒáƒ£áƒ¨áƒ•áƒ˜áƒ— ეს ბრძáƒáƒœáƒ”ბები: | ||
37 | |||
38 | meson build/ | ||
39 | ninja -C build/ | ||
40 | sudo ninja -C build/ install | ||
41 | |||
42 | ## კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ | ||
43 | |||
44 | თუ უკვე იყენებთ i3-ს, მáƒáƒ¨áƒ˜áƒœ დáƒáƒáƒ™áƒáƒžáƒ˜áƒ ე i3 კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ დრჩáƒáƒ¡áƒ•áƒ˜ `~/.config/sway/config` | ||
45 | დრუპრáƒáƒ‘ლემáƒáƒ“ იმუშáƒáƒ•áƒ”ბს პირდáƒáƒžáƒ˜áƒ . წინáƒáƒáƒ¦áƒ›áƒ“ეგ შემთხვევáƒáƒ¨áƒ˜ კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ˜áƒ¡ ნიმუში ჩáƒáƒáƒ™áƒáƒžáƒ˜áƒ ეთ áƒáƒ¥: `~/.config/sway/config`. კáƒáƒ›áƒžáƒ˜áƒ’ურáƒáƒªáƒ˜áƒ˜áƒ¡ ნიმუში ხშირშემთხვევáƒáƒ¨áƒ˜ áƒáƒ ის `/etc/sway/config`. | ||
46 | გáƒáƒ£áƒ¨áƒ•áƒ˜ `man 5 sway` კáƒáƒœáƒžáƒ˜áƒ’ურáƒáƒªáƒ˜áƒáƒ–ე ინფáƒáƒ მáƒáƒªáƒ˜áƒ˜áƒ¡ მისáƒáƒ¦áƒ”ბáƒáƒ“. | ||
47 | |||
48 | ## გáƒáƒ¨áƒ•áƒ”ბრ| ||
49 | |||
50 | გáƒáƒ£áƒ¨áƒ•áƒ˜ `sway` TTY-ისთვის. ზáƒáƒ’იერთმრლáƒáƒ’ინ მენეჯერმრშეიძლებრიმუშáƒáƒ•áƒáƒ¡, მáƒáƒ’რáƒáƒ› áƒáƒ | ||
51 | áƒáƒ ის მხáƒáƒ დáƒáƒáƒ”რილი sway-სგáƒáƒœ (რáƒáƒ’áƒáƒ ც წესი კáƒáƒ გáƒáƒ“ მუშáƒáƒáƒ‘ს gdm). | ||
52 | |||
53 | [i3]: https://i3wm.org/ | ||
54 | [Wayland]: http://wayland.freedesktop.org/ | ||
55 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
56 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway | ||
57 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
58 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
59 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
60 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
61 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.gr.md b/README.gr.md new file mode 100644 index 00000000..d697f78e --- /dev/null +++ b/README.gr.md | |||
@@ -0,0 +1,67 @@ | |||
1 | # Sway | ||
2 | |||
3 | Το sway Îνα [i3]-συμβατό [Wayland] compositor. Διαβάστε το [FAQ]. Μπείτε στο | ||
4 | [IRC channel] \(#sway on irc.libera.chat). | ||
5 | |||
6 | ## ΥπογÏαφÎÏ‚ δημοσιεÏσεων | ||
7 | |||
8 | Οι εκδόσεις είναι υπογεÏαμμÎνες με [E88F5E48] και δημοσιευμÎνες [στο GitHub][GitHub releases]. | ||
9 | |||
10 | ## Εγκατάσταση | ||
11 | |||
12 | ### Από πακÎτα | ||
13 | |||
14 | Το Sway είναι διαθÎσιμο σε πολλά distributions. Δοκιμάστε εγκαταστώντας το "sway" package για | ||
15 | το δικό σας. | ||
16 | |||
17 | Εάν ενδιαφÎÏεστε για packaging του sway για το distribution σας, να πάτε στο IRC | ||
18 | channel ή στείλτε Îνα email στο sir@cmpwn.com για συμβουλÎÏ‚. | ||
19 | |||
20 | ### Compiling από πηγή | ||
21 | |||
22 | ΤσεκάÏετε [αυτό το wiki page][Development setup] εάμα θÎλετε να κάνετε build το HEAD του | ||
23 | sway και wlroots γιά τεστάÏισμα ή development. | ||
24 | |||
25 | Εγκατάσταση των dependencies: | ||
26 | |||
27 | * meson \* | ||
28 | * [wlroots] | ||
29 | * wayland | ||
30 | * wayland-protocols \* | ||
31 | * pcre2 | ||
32 | * json-c | ||
33 | * pango | ||
34 | * cairo | ||
35 | * gdk-pixbuf2 (Ï€ÏοαιÏετικό: system tray) | ||
36 | * [scdoc] (Ï€ÏοαιÏετικό: man pages) \* | ||
37 | * git (Ï€ÏοαιÏετικό: πληÏοφοÏίες εκδώσεων) \* | ||
38 | |||
39 | _\*Compile-time dep_ | ||
40 | |||
41 | ΤÏÎξτε αυτά τα commands: | ||
42 | |||
43 | meson build/ | ||
44 | ninja -C build/ | ||
45 | sudo ninja -C build/ install | ||
46 | |||
47 | ## Configuration | ||
48 | |||
49 | Εάν ήδη χÏησιμοποιήτε το i3, αντιγÏάψτε το i3 config σας στο `~/.config/sway/config` και | ||
50 | θα δουλÎψει out of the box. Αλλιώς, αντιγÏάψτε το sample configuration αÏχείο στο | ||
51 | `~/.config/sway/config`. Το οποίο συνήθως βÏίσκεται στο `/etc/sway/config`. | ||
52 | Κάντε run `man 5 sway` για πληÏοφοÏίες Ï„Î¿Ï configuration. | ||
53 | |||
54 | ## ΤÏÎχοντας | ||
55 | |||
56 | ΤÏÎξτε `sway` από Îνα TTY. ΜεÏίκα display managers μποÏεί να δουλÎψουν αλλά δÎν είναι συμβατά με | ||
57 | το sway (το gdm γνωÏίζεται να δουλÎβει σχετικά καλά). | ||
58 | |||
59 | [i3]: https://i3wm.org/ | ||
60 | [Wayland]: http://wayland.freedesktop.org/ | ||
61 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
62 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway | ||
63 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
64 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
65 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
66 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
67 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc \ No newline at end of file | ||
diff --git a/README.hi.md b/README.hi.md new file mode 100644 index 00000000..eae5e90a --- /dev/null +++ b/README.hi.md | |||
@@ -0,0 +1,63 @@ | |||
1 | # sway | ||
2 | |||
3 | sway à¤à¤• [i3](https://i3wm.org/)-अनà¥à¤•à¥‚ल | ||
4 | [Wayland](https://wayland.freedesktop.org/) Compositor है। | ||
5 | [FAQ](https://github.com/swaywm/sway/wiki) पढिये। [IRC | ||
6 | Channel](https://web.libera.chat/gamja/?channels=#sway) | ||
7 | (irc.libera.chat पर #sway) में à¤à¥€ जà¥à¤¡à¤¿à¤¯à¥‡à¥¤ | ||
8 | |||
9 | ## रिलीज हसà¥à¤¤à¤¾à¤•à¥à¤·à¤° | ||
10 | |||
11 | रिलीजें | ||
12 | [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) | ||
13 | से साइन होतें हैं और [Github पर](https://github.com/swaywm/sway/releases) पà¥à¤°à¤•à¤¾à¤¶à¤¿à¤¤ होते हैं। | ||
14 | |||
15 | ## इंसà¥à¤Ÿà¥Œà¤²à¥‡à¤¶à¤¨ | ||
16 | |||
17 | ### पैकेजों के दà¥à¤µà¤¾à¤°à¤¾ | ||
18 | |||
19 | Sway कई distributions में उपà¥à¤²à¤¬à¥à¤§ है। आप अपने में "sway" नामक पैकेज इंसà¥à¤Ÿà¥Œà¤² करके देख | ||
20 | सकते हैं। | ||
21 | |||
22 | ### Source से compile करके | ||
23 | |||
24 | यदि आप परीकà¥à¤·à¤£ और विकास के लिठsway और wlroots के नवीनतम संसà¥à¤•à¤°à¤£ बनाना | ||
25 | चाहते हैं, तो [यह विकी | ||
26 | पृषà¥à¤ ](https://github.com/swaywm/sway/wiki/Development-Setup) देखें। | ||
27 | |||
28 | निरà¥à¤à¤°à¤¤à¤¾à¤à¤‚: | ||
29 | |||
30 | * meson \* | ||
31 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) | ||
32 | * wayland | ||
33 | * wayland-protocols \* | ||
34 | * pcre2 | ||
35 | * json-c | ||
36 | * pango | ||
37 | * cairo | ||
38 | * gdk-pixbuf (वैकलà¥à¤ªà¤¿à¤•: system tray के लिये) | ||
39 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (वैकलà¥à¤ªà¤¿à¤•: man पृषà¥à¤ ों के लिये) | ||
40 | \* | ||
41 | * git (वैकलà¥à¤ªà¤¿à¤•: संसà¥à¤•à¤°à¤£ जानने के लिये) | ||
42 | |||
43 | _\* Compilation के समय आवशà¥à¤¯à¤•_ | ||
44 | |||
45 | ये commands चलाà¤à¤‚: | ||
46 | |||
47 | meson build/ | ||
48 | ninja -C build/ | ||
49 | sudo ninja -C build/ install | ||
50 | |||
51 | ## Configuration | ||
52 | |||
53 | अगर आप पहले से ही i3 का उपयोग करते हैं तो अपने i3 config को | ||
54 | `~/.config/sway/config` में copy कर लीजिये और वह बिना किसी परिवरà¥à¤¤à¤¨ के काम | ||
55 | करेगा। अनà¥à¤¯à¤¥à¤¾, नमूने configuration file को `~/.config/sway/config` में copy | ||
56 | कर लीजिये। यह सामानà¥à¤¯à¤¤à¤ƒ `/etc/sway/config` में पाया जाता है। `man 5 | ||
57 | sway` से आप configuration के बारे में जानकारी पà¥à¤°à¤¾à¤ªà¥à¤¤ कर सकते हैं। | ||
58 | |||
59 | ## चलाना | ||
60 | |||
61 | आप à¤à¤• tty से `sway` को चला सकते हैं। कà¥à¤› display managers काम करते हैं परनà¥à¤¤à¥ ये | ||
62 | sway के दà¥à¤µà¤¾à¤°à¤¾ समरà¥à¤¥à¤¿à¤¤ नहीं है (gdm के बारे में जाना गया है कि वह सही काम करता | ||
63 | है)। | ||
diff --git a/README.hu.md b/README.hu.md index f673627a..82ca6785 100644 --- a/README.hu.md +++ b/README.hu.md | |||
@@ -28,7 +28,7 @@ TelepÃtsd a függÅ‘ségeket: | |||
28 | * [wlroots] | 28 | * [wlroots] |
29 | * wayland | 29 | * wayland |
30 | * wayland-protocols \* | 30 | * wayland-protocols \* |
31 | * pcre | 31 | * pcre2 |
32 | * json-c | 32 | * json-c |
33 | * pango | 33 | * pango |
34 | * cairo | 34 | * cairo |
@@ -44,13 +44,6 @@ Futtasd ezeket a parancsokat: | |||
44 | ninja -C build | 44 | ninja -C build |
45 | sudo ninja -C build install | 45 | sudo ninja -C build install |
46 | 46 | ||
47 | Ha `logind` nélküli rendszert használsz, akkor be kell állÃtanod a `suid` bitet | ||
48 | a futtaható állományon: | ||
49 | |||
50 | sudo chmod a+s /usr/local/bin/sway | ||
51 | |||
52 | A Sway indulás után nem sokkal el fogja engedni a root jogosultságait. | ||
53 | |||
54 | ## Konfiguráció | 47 | ## Konfiguráció |
55 | 48 | ||
56 | Ha elÅ‘zÅ‘leg i3-mat használtál, akkor átmásolhatod az i3 beállÃtásaidat a | 49 | Ha elÅ‘zÅ‘leg i3-mat használtál, akkor átmásolhatod az i3 beállÃtásaidat a |
@@ -69,9 +62,9 @@ gdm-ről ismeretes, hogy egész jól működik.) | |||
69 | [i3]: https://i3wm.org/ | 62 | [i3]: https://i3wm.org/ |
70 | [Wayland]: http://wayland.freedesktop.org/ | 63 | [Wayland]: http://wayland.freedesktop.org/ |
71 | [FAQ]: https://github.com/swaywm/sway/wiki | 64 | [FAQ]: https://github.com/swaywm/sway/wiki |
72 | [IRC channel]: https://web.libera.chat/?channels=#sway | 65 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway |
73 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | 66 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 |
74 | [GitHub releases]: https://github.com/swaywm/sway/releases | 67 | [GitHub releases]: https://github.com/swaywm/sway/releases |
75 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | 68 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup |
76 | [wlroots]: https://github.com/swaywm/wlroots | 69 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots |
77 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | 70 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc |
diff --git a/README.ir.md b/README.ir.md new file mode 100644 index 00000000..a485a405 --- /dev/null +++ b/README.ir.md | |||
@@ -0,0 +1,54 @@ | |||
1 | # sway | ||
2 | |||
3 | ‏sway یک کامپوزیتور الهام گرÙته از [i3](https://i3wm.org/) بر روی [Wayland](http://wayland.freedesktop.org/) است. [سوال‌های متداول](https://github.com/swaywm/sway/wiki) را بخوانید. در [کانال | ||
4 | IRC](http://web.libera.chat/gamja/?channels=sway&uio=d4) عضو شوید (‎#sway‏ در | ||
5 | irc.libera.chat). | ||
6 | |||
7 | برای Øمایت از تیم توسعه sway به [صÙØÙ‡ | ||
8 | Patreon با نام کاربری SirCmpwn](https://patreon.com/sircmpwn) مراجعه کنید. | ||
9 | |||
10 | ## امضای نسخه‌ها | ||
11 | |||
12 | امضای نسخه‌ها با [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) در [GitHub](https://github.com/swaywm/sway/releases) منتشر می‌شود. | ||
13 | |||
14 | ## شیوه نصب | ||
15 | |||
16 | ### از بسته‌های رسمی | ||
17 | |||
18 | ‏sway در بسته‌های رسمی توزیع‌های مختل٠وجود دارد. بسته «sway» را نصب کنید. در صورتی Ú©Ù‡ بسته رسمی وجود نداشت، برای آگاهی بیشتر درباره نصب روی توزیعتان به این [صÙØÙ‡ راهنما](https://github.com/swaywm/sway/wiki/Unsupported-packages) مراجعه کنید. | ||
19 | |||
20 | اگر به ایجاد بسته sway برای توزیعتان علاقه‌مند هستید، از کانال IRC استÙاده کنید یا به sir@cmpwn.com ایمیل بزنید. | ||
21 | |||
22 | ### کامپایل کردن کد | ||
23 | |||
24 | چنانچه می‌خواهید آخرین نسخه کد sway Ùˆ wlroots را برای آزمایش یا توسعه بسازید به این [صÙØÙ‡ راهنما](https://github.com/swaywm/sway/wiki/Development-Setup) مراجعه کنید. | ||
25 | |||
26 | بسته‌های مورد نیاز: | ||
27 | |||
28 | * meson \* | ||
29 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) | ||
30 | * wayland | ||
31 | * wayland-protocols \* | ||
32 | * pcre2 | ||
33 | * json-c | ||
34 | * pango | ||
35 | * cairo | ||
36 | * gdk-pixbuf2 (انتخابی: برای system tray) | ||
37 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (انتخابی: برای صÙØه‌های راهنما) \* | ||
38 | * git (انتخابی: برای اطلاع در خصوص نسخه‌ها) \* | ||
39 | |||
40 | _\*نیازمندی‌های زمان کامپایل برنامه_ | ||
41 | |||
42 | این Ùرمان‌ها را اجرا کنید: | ||
43 | |||
44 | meson build | ||
45 | ninja -C build | ||
46 | sudo ninja -C build install | ||
47 | |||
48 | ### شخصی سازی و تنظیمات | ||
49 | |||
50 | اگر در Øال Øاضر از i3 استÙاده می‌کنید، تنظیمات i3 خودتان را در Ùایل ‪`~/.config/sway/config`‬ Ú©Ù¾ÛŒ کنید Ùˆ بدون نیاز به تغییر کار خواهد کرد. در غیر این‌صورت، Ùایل نمونه تنظیمات را استÙاده کنید. این Ùایل عموما در ‪`/etc/sway/config`‬ قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید. | ||
51 | |||
52 | ## اجرا | ||
53 | |||
54 | در Ù…Øیط TTY کاÙیست `sway` را اجرا کنید. ممکن است ابزارهای مدیریت نمایشگری نیز برای این کار وجود داشته باشند اما از طر٠sway پشتیبانی نمی‌شوند (gdm عملکرد خوبی در این زمینه دارد). | ||
diff --git a/README.it.md b/README.it.md new file mode 100644 index 00000000..82bb5783 --- /dev/null +++ b/README.it.md | |||
@@ -0,0 +1,66 @@ | |||
1 | # sway | ||
2 | |||
3 | sway è un compositore di [Wayland] compatibile con [i3]. Leggi le [FAQ]. | ||
4 | Unisciti al [canale di IRC] \(#sway su irc.libera.chat). | ||
5 | |||
6 | ## Firma delle versioni | ||
7 | |||
8 | Le versioni sono firmate con la chiave [E88F5E48] e sono pubblicate | ||
9 | [su GitHub][GitHub releases]. | ||
10 | |||
11 | ## Installazione | ||
12 | |||
13 | ### Da un pacchetto | ||
14 | |||
15 | Sway è disponibile in molte distribuzioni, prova a installare il pacchetto | ||
16 | "sway" per la tua. | ||
17 | |||
18 | ### Compilazione dei sorgenti | ||
19 | |||
20 | Consulta [questa pagina del wiki][Development setup] se vuoi compilare l'HEAD | ||
21 | di sway e wlroots per testarli o contribuire allo sviluppo. | ||
22 | |||
23 | Installa le dipendenze: | ||
24 | |||
25 | * meson \* | ||
26 | * [wlroots] | ||
27 | * wayland | ||
28 | * wayland-protocols \* | ||
29 | * pcre | ||
30 | * json-c | ||
31 | * pango | ||
32 | * cairo | ||
33 | * gdk-pixbuf2 (opzionale: area di notifica) | ||
34 | * [scdoc] (opzionale: pagine del manuale) \* | ||
35 | * git (opzionale: informazioni sulla versione) \* | ||
36 | |||
37 | _\* Dipendenza necessaria per la compilazione_ | ||
38 | |||
39 | Esegui questi comandi: | ||
40 | |||
41 | meson build/ | ||
42 | ninja -C build/ | ||
43 | sudo ninja -C build/ install | ||
44 | |||
45 | ## Configurazione | ||
46 | |||
47 | Se hai già usato i3, copia il tuo file di configurazione in | ||
48 | `~/.config/sway/config` e sway funzionerà immediatamente. Altrimenti, copia il | ||
49 | file d'esempio in `~/.config/sway/config`, generalmente è situato in | ||
50 | `/etc/sway/config`. Consulta `man 5 sway` per ulteriori informazioni sulla | ||
51 | configurazione. | ||
52 | |||
53 | ## Esecuzione | ||
54 | |||
55 | Lancia `sway` da un TTY. Alcuni gestori d'accesso potrebbero funzionare ma non | ||
56 | sono supportati da sway (gdm funziona abbastanza bene). | ||
57 | |||
58 | [i3]: https://i3wm.org/ | ||
59 | [Wayland]: http://wayland.freedesktop.org/ | ||
60 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
61 | [canale di IRC]: https://web.libera.chat/gamja/?channels=#sway | ||
62 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
63 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
64 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
65 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
66 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.ja.md b/README.ja.md index 9e7daee1..4e9a9971 100644 --- a/README.ja.md +++ b/README.ja.md | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | Swayã¯[i3](https://i3wm.org/)互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚ | 3 | Swayã¯[i3](https://i3wm.org/)互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚ |
4 | [FAQ](https://github.com/swaywm/sway/wiki)ã‚‚åˆã‚ã›ã¦ã”覧ãã ã•ã„。 | 4 | [FAQ](https://github.com/swaywm/sway/wiki)ã‚‚åˆã‚ã›ã¦ã”覧ãã ã•ã„。 |
5 | [IRC ãƒãƒ£ãƒ³ãƒãƒ«](https://web.libera.chat/?channels=#sway) (irc.libera.chatã®#sway)ã‚‚ã‚ã‚Šã¾ã™ã€‚ | 5 | [IRC ãƒãƒ£ãƒ³ãƒãƒ«](https://web.libera.chat/gamja/?channels=#sway) (irc.libera.chatã®#sway)ã‚‚ã‚ã‚Šã¾ã™ã€‚ |
6 | 6 | ||
7 | [![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) | 7 | [![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) |
8 | 8 | ||
@@ -27,18 +27,18 @@ Swayã¯æ²¢å±±ã®ãƒ‡ã‚£ã‚¹ãƒˆãƒªãƒ“ューションã§æä¾›ã•ã‚Œã¦ã„ã¾ã™ã€‚" | |||
27 | 次ã®ä¾å˜ãƒ‘ッケージをインストールã—ã¦ãã ã•ã„: | 27 | 次ã®ä¾å˜ãƒ‘ッケージをインストールã—ã¦ãã ã•ã„: |
28 | 28 | ||
29 | * meson \* | 29 | * meson \* |
30 | * [wlroots](https://github.com/swaywm/wlroots) | 30 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
31 | * wayland | 31 | * wayland |
32 | * wayland-protocols \* | 32 | * wayland-protocols \* |
33 | * pcre | 33 | * pcre2 |
34 | * json-c | 34 | * json-c |
35 | * pango | 35 | * pango |
36 | * cairo | 36 | * cairo |
37 | * gdk-pixbuf2 (システムイコンã§å¿…è¦ã§ã™) | 37 | * gdk-pixbuf2 (ä»»æ„: システムイコンã§å¿…è¦ã§ã™) |
38 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (manã§å¿…è¦ã§ã™) \* | 38 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (ä»»æ„: manã§å¿…è¦ã§ã™) \* |
39 | * git \* | 39 | * git (ä»»æ„: ãƒãƒ¼ã‚¸ãƒ§ãƒ³æƒ…å ±ã§å¿…è¦ã§ã™) \* |
40 | 40 | ||
41 | _\*コンパイルã®æ™‚_ | 41 | _\*コンパイル時ã®ä¾å˜_ |
42 | 42 | ||
43 | 次ã®ã‚³ãƒžãƒ³ãƒ‰ã‚’実行ã—ã¦ãã ã•ã„: | 43 | 次ã®ã‚³ãƒžãƒ³ãƒ‰ã‚’実行ã—ã¦ãã ã•ã„: |
44 | 44 | ||
@@ -46,12 +46,6 @@ _\*コンパイルã®æ™‚_ | |||
46 | ninja -C build | 46 | ninja -C build |
47 | sudo ninja -C build install | 47 | sudo ninja -C build install |
48 | 48 | ||
49 | logindを使用ã—ã¦ã„ãªã„システムã§ã¯ã€ãƒã‚¤ãƒŠãƒªã«suidã‚’è¨å®šã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™: | ||
50 | |||
51 | sudo chmod a+s /usr/local/bin/sway | ||
52 | |||
53 | swayã¯èµ·å‹•å¾Œã€ã™ãã«root許å¯ã‚’è½ã¨ã—ã¾ã™ã€‚ | ||
54 | |||
55 | ## è¨å®š | 49 | ## è¨å®š |
56 | 50 | ||
57 | æ—¢ã«i3を使用ã—ã¦ã„ã‚‹å ´åˆã¯ã€i3ã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’`~/.config/sway/config`ã«ã‚³ãƒ”ーã™ã‚Œã°å‹•ãã¾ã™ã€‚ãã†ã§ãªã„å ´åˆã¯ã€ã‚µãƒ³ãƒ—ルã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’`~/.config/sway/config`ã«ã‚³ãƒ”ーã—ã¦ãã ã•ã„。サンプルã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã¯ã€é€šå¸¸`/etc/sway/config`ã«ã‚ã‚Šã¾ã™ã€‚`man 5 sway`を実行ã™ã‚‹ã“ã¨ã§ã€è¨å®šã«é–¢ã™ã‚‹æƒ…å ±ã‚’è¦‹ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ | 51 | æ—¢ã«i3を使用ã—ã¦ã„ã‚‹å ´åˆã¯ã€i3ã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’`~/.config/sway/config`ã«ã‚³ãƒ”ーã™ã‚Œã°å‹•ãã¾ã™ã€‚ãã†ã§ãªã„å ´åˆã¯ã€ã‚µãƒ³ãƒ—ルã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’`~/.config/sway/config`ã«ã‚³ãƒ”ーã—ã¦ãã ã•ã„。サンプルã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã¯ã€é€šå¸¸`/etc/sway/config`ã«ã‚ã‚Šã¾ã™ã€‚`man 5 sway`を実行ã™ã‚‹ã“ã¨ã§ã€è¨å®šã«é–¢ã™ã‚‹æƒ…å ±ã‚’è¦‹ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ |
diff --git a/README.ko.md b/README.ko.md index 4d5619de..e086c174 100644 --- a/README.ko.md +++ b/README.ko.md | |||
@@ -1,7 +1,7 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | sway는 [i3](https://i3wm.org/)-호환 [Wayland](http://wayland.freedesktop.org/) ì»´í¬ì§€í„°ìž…니다. | 3 | sway는 [i3](https://i3wm.org/)-호환 [Wayland](http://wayland.freedesktop.org/) ì»´í¬ì§€í„°ìž…니다. |
4 | [FAQ](https://github.com/swaywm/sway/wiki)를 ì½ì–´ë³´ì„¸ìš”. [IRC 채ë„](https://web.libera.chat/?channels=#sway) (#sway on irc.libera.chat)ë„ ìžˆìŠµë‹ˆë‹¤. | 4 | [FAQ](https://github.com/swaywm/sway/wiki)를 ì½ì–´ë³´ì„¸ìš”. [IRC 채ë„](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat)ë„ ìžˆìŠµë‹ˆë‹¤. |
5 | 5 | ||
6 | ## 릴리즈 서명 | 6 | ## 릴리즈 서명 |
7 | 7 | ||
@@ -24,10 +24,10 @@ IRC 채ë„ì„ ë°©ë¬¸í•˜ê±°ë‚˜ sir@cmpwn.com으로 ì´ë©”ì¼ì„ ë³´ë‚´ ìƒë‹´ ë°› | |||
24 | ë‹¤ìŒ ì˜ì¡´ íŒ¨í‚¤ì§€ë“¤ì„ ì„¤ì¹˜í•´ 주세요: | 24 | ë‹¤ìŒ ì˜ì¡´ íŒ¨í‚¤ì§€ë“¤ì„ ì„¤ì¹˜í•´ 주세요: |
25 | 25 | ||
26 | * meson \* | 26 | * meson \* |
27 | * [wlroots](https://github.com/swaywm/wlroots) | 27 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
28 | * wayland | 28 | * wayland |
29 | * wayland-protocols \* | 29 | * wayland-protocols \* |
30 | * pcre | 30 | * pcre2 |
31 | * json-c | 31 | * json-c |
32 | * pango | 32 | * pango |
33 | * cairo | 33 | * cairo |
@@ -43,12 +43,6 @@ _\*ì»´íŒŒì¼ ë–„ í•„ìš”_ | |||
43 | ninja -C build | 43 | ninja -C build |
44 | sudo ninja -C build install | 44 | sudo ninja -C build install |
45 | 45 | ||
46 | logind를 ì‚¬ìš©í•˜ê³ ìžˆì§€ 않는 시스템ì—서는, ë°”ì´ë„ˆë¦¬ì— suid를 ì„¤ì •í• í•„ìš”ê°€ 있습니다: | ||
47 | |||
48 | sudo chmod a+s /usr/local/bin/sway | ||
49 | |||
50 | Sway는 시작 í›„ì— root ê¶Œí•œì„ dropí• ê²ƒ 입니다. | ||
51 | |||
52 | ## ì„¤ì • | 46 | ## ì„¤ì • |
53 | 47 | ||
54 | i3를 ì´ë¯¸ 사용 중ì´ë¼ë©´, i3 configì„ `~/.config/sway/config`ë¡œ 복사하세요. | 48 | i3를 ì´ë¯¸ 사용 중ì´ë¼ë©´, i3 configì„ `~/.config/sway/config`ë¡œ 복사하세요. |
@@ -1,6 +1,6 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | **[English][en]** - [日本語][ja] - [Français][fr] - [УкраїнÑька][uk] - [Español][es] - [Polski][pl] - [ä¸æ–‡-简体][zh-CN] - [Deutsch][de] - [Nederlands][nl] - [РуÑÑкий][ru] - [ä¸æ–‡-ç¹é«”][zh-TW] - [Português][pt] - [Dansk][dk] - [í•œêµì–´][ko] - [Română][ro] - [Magyar][hu] - [Türkçe][tr] | 3 | **[English][en]** - [عربي][ar] - [ÄŒesky][cs] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქáƒáƒ თული][ge] - [Ελληνικά][gr] - [हिनà¥à¤¦à¥€][hi] - [Magyar][hu] - [Ùارسی][ir] - [Italiano][it] - [日本語][ja] - [í•œêµì–´][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [РуÑÑкий][ru] - [Svenska][sv] - [Türkçe][tr] - [УкраїнÑька][uk] - [ä¸æ–‡-简体][zh-CN] - [ä¸æ–‡-ç¹é«”][zh-TW] |
4 | 4 | ||
5 | sway is an [i3]-compatible [Wayland] compositor. Read the [FAQ]. Join the | 5 | sway is an [i3]-compatible [Wayland] compositor. Read the [FAQ]. Join the |
6 | [IRC channel] \(#sway on irc.libera.chat). | 6 | [IRC channel] \(#sway on irc.libera.chat). |
@@ -16,9 +16,6 @@ Releases are signed with [E88F5E48] and published [on GitHub][GitHub releases]. | |||
16 | Sway is available in many distributions. Try installing the "sway" package for | 16 | Sway is available in many distributions. Try installing the "sway" package for |
17 | yours. | 17 | yours. |
18 | 18 | ||
19 | If you're interested in packaging sway for your distribution, stop by the IRC | ||
20 | channel or shoot an email to sir@cmpwn.com for advice. | ||
21 | |||
22 | ### Compiling from Source | 19 | ### Compiling from Source |
23 | 20 | ||
24 | Check out [this wiki page][Development setup] if you want to build the HEAD of | 21 | Check out [this wiki page][Development setup] if you want to build the HEAD of |
@@ -30,15 +27,16 @@ Install dependencies: | |||
30 | * [wlroots] | 27 | * [wlroots] |
31 | * wayland | 28 | * wayland |
32 | * wayland-protocols \* | 29 | * wayland-protocols \* |
33 | * pcre | 30 | * pcre2 |
34 | * json-c | 31 | * json-c |
35 | * pango | 32 | * pango |
36 | * cairo | 33 | * cairo |
37 | * gdk-pixbuf2 (optional: system tray) | 34 | * gdk-pixbuf2 (optional: additional image formats for system tray) |
35 | * [swaybg] (optional: wallpaper) | ||
38 | * [scdoc] (optional: man pages) \* | 36 | * [scdoc] (optional: man pages) \* |
39 | * git (optional: version info) \* | 37 | * git (optional: version info) \* |
40 | 38 | ||
41 | _\*Compile-time dep_ | 39 | _\* Compile-time dep_ |
42 | 40 | ||
43 | Run these commands: | 41 | Run these commands: |
44 | 42 | ||
@@ -46,12 +44,6 @@ Run these commands: | |||
46 | ninja -C build/ | 44 | ninja -C build/ |
47 | sudo ninja -C build/ install | 45 | sudo ninja -C build/ install |
48 | 46 | ||
49 | On systems without logind, you need to suid the sway binary: | ||
50 | |||
51 | sudo chmod a+s /usr/local/bin/sway | ||
52 | |||
53 | Sway will drop root permissions shortly after startup. | ||
54 | |||
55 | ## Configuration | 47 | ## Configuration |
56 | 48 | ||
57 | If you already use i3, then copy your i3 config to `~/.config/sway/config` and | 49 | If you already use i3, then copy your i3 config to `~/.config/sway/config` and |
@@ -65,28 +57,38 @@ Run `sway` from a TTY. Some display managers may work but are not supported by | |||
65 | sway (gdm is known to work fairly well). | 57 | sway (gdm is known to work fairly well). |
66 | 58 | ||
67 | [en]: https://github.com/swaywm/sway#readme | 59 | [en]: https://github.com/swaywm/sway#readme |
68 | [ja]: https://github.com/swaywm/sway/blob/master/README.ja.md | 60 | [ar]: README.ar.md |
69 | [fr]: https://github.com/swaywm/sway/blob/master/README.fr.md | 61 | [cs]: README.cs.md |
70 | [uk]: https://github.com/swaywm/sway/blob/master/README.uk.md | 62 | [de]: README.de.md |
71 | [es]: https://github.com/swaywm/sway/blob/master/README.es.md | 63 | [dk]: README.dk.md |
72 | [pl]: https://github.com/swaywm/sway/blob/master/README.pl.md | 64 | [es]: README.es.md |
73 | [zh-CN]: https://github.com/swaywm/sway/blob/master/README.zh-CN.md | 65 | [fr]: README.fr.md |
74 | [de]: https://github.com/swaywm/sway/blob/master/README.de.md | 66 | [ge]: README.ge.md |
75 | [nl]: https://github.com/swaywm/sway/blob/master/README.nl.md | 67 | [gr]: README.gr.md |
76 | [ru]: https://github.com/swaywm/sway/blob/master/README.ru.md | 68 | [hi]: README.hi.md |
77 | [zh-TW]: https://github.com/swaywm/sway/blob/master/README.zh-TW.md | 69 | [hu]: README.hu.md |
78 | [pt]: https://github.com/swaywm/sway/blob/master/README.pt.md | 70 | [ir]: README.ir.md |
79 | [dk]: https://github.com/swaywm/sway/blob/master/README.dk.md | 71 | [it]: README.it.md |
80 | [ko]: https://github.com/swaywm/sway/blob/master/README.ko.md | 72 | [ja]: README.ja.md |
81 | [ro]: https://github.com/swaywm/sway/blob/master/README.ro.md | 73 | [ko]: README.ko.md |
82 | [hu]: https://github.com/swaywm/sway/blob/master/README.hu.md | 74 | [nl]: README.nl.md |
83 | [tr]: https://github.com/swaywm/sway/blob/master/README.tr.md | 75 | [no]: README.no.md |
76 | [pl]: README.pl.md | ||
77 | [pt]: README.pt.md | ||
78 | [ro]: README.ro.md | ||
79 | [ru]: README.ru.md | ||
80 | [sv]: README.sv.md | ||
81 | [tr]: README.tr.md | ||
82 | [uk]: README.uk.md | ||
83 | [zh-CN]: README.zh-CN.md | ||
84 | [zh-TW]: README.zh-TW.md | ||
84 | [i3]: https://i3wm.org/ | 85 | [i3]: https://i3wm.org/ |
85 | [Wayland]: http://wayland.freedesktop.org/ | 86 | [Wayland]: http://wayland.freedesktop.org/ |
86 | [FAQ]: https://github.com/swaywm/sway/wiki | 87 | [FAQ]: https://github.com/swaywm/sway/wiki |
87 | [IRC channel]: https://web.libera.chat/?channels=#sway | 88 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway |
88 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | 89 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 |
89 | [GitHub releases]: https://github.com/swaywm/sway/releases | 90 | [GitHub releases]: https://github.com/swaywm/sway/releases |
90 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | 91 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup |
91 | [wlroots]: https://github.com/swaywm/wlroots | 92 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots |
93 | [swaybg]: https://github.com/swaywm/swaybg/ | ||
92 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | 94 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc |
diff --git a/README.nl.md b/README.nl.md index a82a3d4c..bf1ea975 100644 --- a/README.nl.md +++ b/README.nl.md | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | Sway is een [i3](https://i3wm.org/)-compatibele [Wayland](http://wayland.freedesktop.org/) compositor. | 3 | Sway is een [i3](https://i3wm.org/)-compatibele [Wayland](http://wayland.freedesktop.org/) compositor. |
4 | Lees de [FAQ](https://github.com/swaywm/sway/wiki). Word lid van het [IRC | 4 | Lees de [FAQ](https://github.com/swaywm/sway/wiki). Word lid van het [IRC |
5 | kanaal](https://web.libera.chat/?channels=#sway) (#sway op | 5 | kanaal](https://web.libera.chat/gamja/?channels=#sway) (#sway op |
6 | irc.libera.chat). | 6 | irc.libera.chat). |
7 | 7 | ||
8 | ## Releasehandtekeningen | 8 | ## Releasehandtekeningen |
@@ -25,10 +25,10 @@ kanaal of stuur een e-mail naar sir@cmpwn.com voor advies. | |||
25 | Afhankelijkheden installeren: | 25 | Afhankelijkheden installeren: |
26 | 26 | ||
27 | * meson \* | 27 | * meson \* |
28 | * [wlroots](https://github.com/swaywm/wlroots) | 28 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
29 | * wayland | 29 | * wayland |
30 | * wayland-protocols \* | 30 | * wayland-protocols \* |
31 | * pcre | 31 | * pcre2 |
32 | * json-c | 32 | * json-c |
33 | * pango | 33 | * pango |
34 | * cairo | 34 | * cairo |
@@ -44,12 +44,6 @@ Voer deze opdrachten uit: | |||
44 | ninja -C build | 44 | ninja -C build |
45 | sudo ninja -C build install | 45 | sudo ninja -C build install |
46 | 46 | ||
47 | Op systemen zonder logind, moet je bij het binaire bestand het suid bit instellen: | ||
48 | |||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | |||
51 | Sway zal root-rechten kort na het opstarten loslaten. | ||
52 | |||
53 | ## Configuratie | 47 | ## Configuratie |
54 | 48 | ||
55 | Als je al i3 gebruikt, kopieer dan je i3-configuratie naar `~/.config/sway/config` en | 49 | Als je al i3 gebruikt, kopieer dan je i3-configuratie naar `~/.config/sway/config` en |
diff --git a/README.no.md b/README.no.md new file mode 100644 index 00000000..35d6e320 --- /dev/null +++ b/README.no.md | |||
@@ -0,0 +1,68 @@ | |||
1 | # Sway | ||
2 | |||
3 | Sway er en [i3]-kompatibel [Wayland] compositor. Les [Ofte stilte spørsmål]. | ||
4 | Delta på [IRC kanalen][IRC kanal] \(#sway på irc.libera.chat). | ||
5 | |||
6 | ## Utgivelses Signaturer | ||
7 | |||
8 | Utgivelser er signert med [E88F5E48] og publisert [på GitHub][GitHub | ||
9 | releases]. | ||
10 | |||
11 | ## Installasjon | ||
12 | |||
13 | ### Fra system pakker | ||
14 | |||
15 | Sway er tilgjengelig i mange distribusjoner. Prøv å installere "sway" pakken | ||
16 | fra din distro sine repoer. | ||
17 | |||
18 | Er du interessert i å pakke Sway for din distribusjon kan du ta turen innom | ||
19 | IRC-kanalen eller send en e-post til sir@cmpwn.com for råd. | ||
20 | |||
21 | ### Kompilering fra kildekode | ||
22 | |||
23 | Se [denne wiki-siden][Oppsetting for utvikling] hvis du vil bygge fra HEAD grenen av sway og | ||
24 | wlroots for testing eller utvikling. | ||
25 | |||
26 | Installasjonsavhengigheter: | ||
27 | |||
28 | * meson \* | ||
29 | * [wlroots] | ||
30 | * wayland | ||
31 | * wayland-protocols \* | ||
32 | * pcre2 | ||
33 | * json-c | ||
34 | * pango | ||
35 | * cairo | ||
36 | * gdk-pixbuf2 (valgfritt: system tray) | ||
37 | * [scdoc] (valgfritt: man pages) \* | ||
38 | * git \* | ||
39 | |||
40 | _\*Kompileringsavhengigheter_ | ||
41 | |||
42 | Kjør følgende kommandoer: | ||
43 | |||
44 | meson build | ||
45 | ninja -C build | ||
46 | sudo ninja -C build install | ||
47 | |||
48 | ## Konfigurasjon | ||
49 | |||
50 | Hvis du allerede bruker i3 kan du bare kopiere din i3 konfigurasjon til | ||
51 | `~/.config/sway/config`. Ellers skal du kopiere eksempel konfigurasjonsfilen til | ||
52 | `~/.config/sway/config`. Eksempel filen er normalt plasert i `/etc/sway/config`. Kjør | ||
53 | `man 5 sway` for å få oplysninger om konfigurasjonen. | ||
54 | |||
55 | ## Utførelse | ||
56 | |||
57 | Kjør `sway` fra en TTY. Noen display managers kan fungere, men Sway har ikke | ||
58 | støtte for dem (gdm er kjent for å fungere ganske bra). | ||
59 | |||
60 | [i3]: https://i3wm.org/ | ||
61 | [Wayland]: http://wayland.freedesktop.org/ | ||
62 | [Ofte stilte spørsmål]: https://github.com/swaywm/sway/wiki | ||
63 | [IRC kanal]: https://web.libera.chat/gamja/?channels=#sway | ||
64 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
65 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
66 | [Oppsetting for utvikling]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
67 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
68 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.pl.md b/README.pl.md index f981c741..65b3c3a1 100644 --- a/README.pl.md +++ b/README.pl.md | |||
@@ -1,7 +1,7 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z [i3](https://i3wm.org/). | 3 | sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z [i3](https://i3wm.org/). |
4 | Przeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](https://web.libera.chat/?channels=#sway) | 4 | Przeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](https://web.libera.chat/gamja/?channels=#sway) |
5 | (#sway na irc.libera.chat). | 5 | (#sway na irc.libera.chat). |
6 | 6 | ||
7 | ## Podpisy cyfrowe wydań | 7 | ## Podpisy cyfrowe wydań |
@@ -25,10 +25,10 @@ adres sir@cmpwn.com w celu uzyskania wskazówek. | |||
25 | Zainstaluj zależności: | 25 | Zainstaluj zależności: |
26 | 26 | ||
27 | * meson \* | 27 | * meson \* |
28 | * [wlroots](https://github.com/swaywm/wlroots) | 28 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
29 | * wayland | 29 | * wayland |
30 | * wayland-protocols \* | 30 | * wayland-protocols \* |
31 | * pcre | 31 | * pcre2 |
32 | * json-c | 32 | * json-c |
33 | * pango | 33 | * pango |
34 | * cairo | 34 | * cairo |
@@ -44,12 +44,6 @@ Wykonaj następujące polecenia: | |||
44 | ninja -C build | 44 | ninja -C build |
45 | sudo ninja -C build install | 45 | sudo ninja -C build install |
46 | 46 | ||
47 | Na systemach bez logind należy wykonać polecenie suid na pliku wykonywalnym sway: | ||
48 | |||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | |||
51 | Sway pozbędzie się uprawnień roota tuż po wystartowaniu. | ||
52 | |||
53 | ## Konfiguracja | 47 | ## Konfiguracja |
54 | 48 | ||
55 | Jeśli już korzystasz z i3, skopiuj swoją konfigurację i3 do katalogu `~/.config/sway/config` i | 49 | Jeśli już korzystasz z i3, skopiuj swoją konfigurację i3 do katalogu `~/.config/sway/config` i |
diff --git a/README.pt.md b/README.pt.md index c33cdf04..c1611a31 100644 --- a/README.pt.md +++ b/README.pt.md | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | O sway é um compositor do [Wayland](http://wayland.freedesktop.org/) compatÃvel com o [i3](https://i3wm.org/). | 3 | O sway é um compositor do [Wayland](http://wayland.freedesktop.org/) compatÃvel com o [i3](https://i3wm.org/). |
4 | Leia o [FAQ](https://github.com/swaywm/sway/wiki). Junte-se ao [canal do | 4 | Leia o [FAQ](https://github.com/swaywm/sway/wiki). Junte-se ao [canal do |
5 | IRC](https://web.libera.chat/?channels=#sway) (#sway em | 5 | IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway em |
6 | irc.libera.chat). | 6 | irc.libera.chat). |
7 | 7 | ||
8 | ## Assinatura das versões | 8 | ## Assinatura das versões |
@@ -27,10 +27,10 @@ Verifique [essa página da wiki](https://github.com/swaywm/sway/wiki/Development | |||
27 | Instale as dependências: | 27 | Instale as dependências: |
28 | 28 | ||
29 | * meson \* | 29 | * meson \* |
30 | * [wlroots](https://github.com/swaywm/wlroots) | 30 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
31 | * wayland | 31 | * wayland |
32 | * wayland-protocols \* | 32 | * wayland-protocols \* |
33 | * pcre | 33 | * pcre2 |
34 | * json-c | 34 | * json-c |
35 | * pango | 35 | * pango |
36 | * cairo | 36 | * cairo |
@@ -46,12 +46,6 @@ Execute esses comandos: | |||
46 | ninja -C build | 46 | ninja -C build |
47 | sudo ninja -C build install | 47 | sudo ninja -C build install |
48 | 48 | ||
49 | Em sistemas sem logind, você precisa preparar o binário do sway: | ||
50 | |||
51 | sudo chmod a+s /usr/local/bin/sway | ||
52 | |||
53 | O sway perderá as privilégios de de root logo após o inÃcio do sistema. | ||
54 | |||
55 | ## Configuração | 49 | ## Configuração |
56 | 50 | ||
57 | Se você já utiliza o i3, então copie os seus arquivos de configuração para `~/.config/sway/config` e | 51 | Se você já utiliza o i3, então copie os seus arquivos de configuração para `~/.config/sway/config` e |
diff --git a/README.ro.md b/README.ro.md index f3f4a32b..a3559a8b 100644 --- a/README.ro.md +++ b/README.ro.md | |||
@@ -1,7 +1,7 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | sway este un compositor pentru [Wayland](http://wayland.freedesktop.org/) compatibil cu [i3](https://i3wm.org/). | 3 | sway este un compositor pentru [Wayland](http://wayland.freedesktop.org/) compatibil cu [i3](https://i3wm.org/). |
4 | Citiți [FAQ](https://github.com/swaywm/sway/wiki)-ul. Connectați-vă la canalul nostru [IRC](https://web.libera.chat/?channels=#sway) (#sway pe irc.libera.chat). | 4 | Citiți [FAQ](https://github.com/swaywm/sway/wiki)-ul. Connectați-vă la canalul nostru [IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway pe irc.libera.chat). |
5 | 5 | ||
6 | ## Semnarea digitală | 6 | ## Semnarea digitală |
7 | 7 | ||
@@ -22,10 +22,10 @@ Dacă sunteți interesați in a crea pachete pentru distribuția voastră, infor | |||
22 | Dependențe pentru instalare: | 22 | Dependențe pentru instalare: |
23 | 23 | ||
24 | * meson \* | 24 | * meson \* |
25 | * [wlroots](https://github.com/swaywm/wlroots) | 25 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
26 | * wayland | 26 | * wayland |
27 | * wayland-protocols \* | 27 | * wayland-protocols \* |
28 | * pcre | 28 | * pcre2 |
29 | * json-c | 29 | * json-c |
30 | * pango | 30 | * pango |
31 | * cairo | 31 | * cairo |
@@ -43,14 +43,6 @@ Rulați aceste comenzi: | |||
43 | sudo ninja -C build install | 43 | sudo ninja -C build install |
44 | ``` | 44 | ``` |
45 | 45 | ||
46 | Pe sisteme fără logind, trebuie să folosiți următoarea comandă pentru a marca binarul de Sway ca suid: | ||
47 | |||
48 | ``` | ||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | ``` | ||
51 | |||
52 | Imediat după pornire, Sway va renunța la permisiunile de root. | ||
53 | |||
54 | ## Configurare | 46 | ## Configurare |
55 | 47 | ||
56 | Dacă folosiți deja i3, copiați fișierul de configurare din i3 în `~/.config/sway/config`, și va funcționa fără a necesita nici o modificare. In caz contrar, copiați exemplul de configurare (disponibil de obicei în `/etc/sway/config`) în `~/.config/sway/config`. | 48 | Dacă folosiți deja i3, copiați fișierul de configurare din i3 în `~/.config/sway/config`, și va funcționa fără a necesita nici o modificare. In caz contrar, copiați exemplul de configurare (disponibil de obicei în `/etc/sway/config`) în `~/.config/sway/config`. |
diff --git a/README.ru.md b/README.ru.md index 257578a8..edc0eda7 100644 --- a/README.ru.md +++ b/README.ru.md | |||
@@ -29,7 +29,7 @@ sway и wlroots Ð´Ð»Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ разработки. | |||
29 | * [wlroots] | 29 | * [wlroots] |
30 | * wayland | 30 | * wayland |
31 | * wayland-protocols \* | 31 | * wayland-protocols \* |
32 | * pcre | 32 | * pcre2 |
33 | * json-c | 33 | * json-c |
34 | * pango | 34 | * pango |
35 | * cairo | 35 | * cairo |
@@ -45,12 +45,6 @@ _\*ЗавиÑимоÑти Ð´Ð»Ñ Ñборки_ | |||
45 | ninja -C build | 45 | ninja -C build |
46 | sudo ninja -C build install | 46 | sudo ninja -C build install |
47 | 47 | ||
48 | Ðа ÑиÑтемах без logind вам понадобитÑÑ Ð´Ð¾Ð±Ð°Ð²Ð¸Ñ‚ÑŒ suid к файлу программы sway: | ||
49 | |||
50 | sudo chmod a+s /usr/local/bin/sway | ||
51 | |||
52 | sway ÑброÑит root-права при запуÑке. | ||
53 | |||
54 | ## ÐаÑтройка | 48 | ## ÐаÑтройка |
55 | 49 | ||
56 | ЕÑли вы уже иÑпользуете i3, Ñкопируйте ваш конфигурационный файл i3 в `~/.config/sway/config`, и | 50 | ЕÑли вы уже иÑпользуете i3, Ñкопируйте ваш конфигурационный файл i3 в `~/.config/sway/config`, и |
@@ -66,9 +60,9 @@ sway (gdm работает довольно неплохо). | |||
66 | [i3]: https://i3wm.org/ | 60 | [i3]: https://i3wm.org/ |
67 | [Wayland]: http://wayland.freedesktop.org/ | 61 | [Wayland]: http://wayland.freedesktop.org/ |
68 | [FAQ]: https://github.com/swaywm/sway/wiki | 62 | [FAQ]: https://github.com/swaywm/sway/wiki |
69 | [IRC channel]: https://web.libera.chat/?channels=#sway | 63 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway |
70 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | 64 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 |
71 | [GitHub releases]: https://github.com/swaywm/sway/releases | 65 | [GitHub releases]: https://github.com/swaywm/sway/releases |
72 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | 66 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup |
73 | [wlroots]: https://github.com/swaywm/wlroots | 67 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots |
74 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | 68 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc |
diff --git a/README.sv.md b/README.sv.md new file mode 100644 index 00000000..c50ca068 --- /dev/null +++ b/README.sv.md | |||
@@ -0,0 +1,83 @@ | |||
1 | # sway | ||
2 | |||
3 | [English][en] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - **[Svenska][sv]** - [Ελληνικά][gr] - [Magyar][hu] - [Ùارسی][ir] - [Italiano][it] - [日本語][ja] - [í•œêµì–´][ko] - [Nederlands][nl] - [Polski][pl] - [Português][pt] - [Română][ro] - [РуÑÑкий][ru] - [Türkçe][tr] - [УкраїнÑька][uk] - [ä¸æ–‡-简体][zh-CN] - [ä¸æ–‡-ç¹é«”][zh-TW] | ||
4 | |||
5 | sway är en [i3]-kompatibel [Wayland] compositor. Läs våran [FAQ]-sida. Gå med i vår | ||
6 | [IRC-kanal] \(#sway på irc.libera.chat). | ||
7 | |||
8 | ## Utgåvosignaturer | ||
9 | |||
10 | Utgåvor är signerade med [E88F5E48] och publicerade på [GitHub][GitHub releases]. | ||
11 | |||
12 | ## Installering | ||
13 | |||
14 | ### Med pakethanterare | ||
15 | |||
16 | Sway är tillgänglig i många distributioner. Prova att installera "sway" med din distributions pakethanterare. | ||
17 | |||
18 | ### Genom att kompilera från källkod | ||
19 | |||
20 | Kolla in [denna wiki-sida][Development setup] om du vill bygga sway och wlroots HEAD för testning eller utveckling. | ||
21 | |||
22 | Installera paket som sway behöver: | ||
23 | |||
24 | * meson \* | ||
25 | * [wlroots] | ||
26 | * wayland | ||
27 | * wayland-protocols \* | ||
28 | * pcre2 | ||
29 | * json-c | ||
30 | * pango | ||
31 | * cairo | ||
32 | * gdk-pixbuf2 (valbar: systembricka) | ||
33 | * [scdoc] (valbar: manualer) \* | ||
34 | * git (valbar: versioninfo) \* | ||
35 | |||
36 | _\* Krav för kompilering_ | ||
37 | |||
38 | Kör dessa kommandon: | ||
39 | |||
40 | meson build/ | ||
41 | ninja -C build/ | ||
42 | sudo ninja -C build/ install | ||
43 | |||
44 | ## Konfiguration | ||
45 | |||
46 | Ifall du redan använder i3 så kan du kopiera din konfigurationsfil till `~/.config/sway/config` och det kommer då att fungera som det ska. | ||
47 | Kopiera annars exemplarkonfigurationsfilen till `~/.config/sway/config`. Den ligger oftast i `/etc/sway/config`. | ||
48 | Kör `man 5 sway` för mer information kring konfigurationen. | ||
49 | |||
50 | ## Att köra sway | ||
51 | |||
52 | Kör `sway` från en TTY. Vissa inloggningahanterare kan fungera men inte vara stödda av sway (gdm ska fungera hyfsat bra). | ||
53 | |||
54 | [en]: https://github.com/swaywm/sway#readme | ||
55 | [de]: README.de.md | ||
56 | [dk]: README.dk.md | ||
57 | [es]: README.es.md | ||
58 | [fr]: README.fr.md | ||
59 | [sv]: README.sv.md | ||
60 | [gr]: README.gr.md | ||
61 | [hu]: README.hu.md | ||
62 | [ir]: README.ir.md | ||
63 | [it]: README.it.md | ||
64 | [ja]: README.ja.md | ||
65 | [ko]: README.ko.md | ||
66 | [nl]: README.nl.md | ||
67 | [pl]: README.pl.md | ||
68 | [pt]: README.pt.md | ||
69 | [ro]: README.ro.md | ||
70 | [ru]: README.ru.md | ||
71 | [tr]: README.tr.md | ||
72 | [uk]: README.uk.md | ||
73 | [zh-CN]: README.zh-CN.md | ||
74 | [zh-TW]: README.zh-TW.md | ||
75 | [i3]: https://i3wm.org/ | ||
76 | [Wayland]: http://wayland.freedesktop.org/ | ||
77 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
78 | [IRC-kanal]: https://web.libera.chat/gamja/?channels=#sway | ||
79 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
80 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
81 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
82 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
83 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.tr.md b/README.tr.md index ff603838..40de1474 100644 --- a/README.tr.md +++ b/README.tr.md | |||
@@ -26,7 +26,7 @@ Aşağıdaki bağımlılıkları yükleyin: | |||
26 | * [wlroots] | 26 | * [wlroots] |
27 | * wayland | 27 | * wayland |
28 | * wayland-protocols \* | 28 | * wayland-protocols \* |
29 | * pcre | 29 | * pcre2 |
30 | * json-c | 30 | * json-c |
31 | * pango | 31 | * pango |
32 | * cairo | 32 | * cairo |
@@ -42,12 +42,6 @@ _\*Derleme-anı bağımlılıkları_ | |||
42 | ninja -C build | 42 | ninja -C build |
43 | sudo ninja -C build install | 43 | sudo ninja -C build install |
44 | 44 | ||
45 | logind olmayan sistemlerde, sway ikilisine (binary) izin vermeniz (suid) gerekir: | ||
46 | |||
47 | sudo chmod a+s /usr/local/bin/sway | ||
48 | |||
49 | Sway, başlangıçtan kısa bir süre sonra kök(root) izinlerini bırakacaktır. | ||
50 | |||
51 | ## Yapılandırma | 45 | ## Yapılandırma |
52 | 46 | ||
53 | Zaten i3 kullanıyorsanız, i3 yapılandırmanızı `~/.config/sway/config` konumuna kopyalayın ve kutudan çıktığı gibi çalışacaktır. Aksi takdirde, örnek yapılandırma dosyasını `~/.config/sway/config` konumuna kopyalayın. Genellikle `/etc/sway/config` konumunda bulunur. | 47 | Zaten i3 kullanıyorsanız, i3 yapılandırmanızı `~/.config/sway/config` konumuna kopyalayın ve kutudan çıktığı gibi çalışacaktır. Aksi takdirde, örnek yapılandırma dosyasını `~/.config/sway/config` konumuna kopyalayın. Genellikle `/etc/sway/config` konumunda bulunur. |
@@ -60,9 +54,9 @@ TTY'den `sway` çalıştırın. Bazı görüntü yöneticileriyle(display manag | |||
60 | [i3]: https://i3wm.org/ | 54 | [i3]: https://i3wm.org/ |
61 | [Wayland]: http://wayland.freedesktop.org/ | 55 | [Wayland]: http://wayland.freedesktop.org/ |
62 | [FAQ]: https://github.com/swaywm/sway/wiki | 56 | [FAQ]: https://github.com/swaywm/sway/wiki |
63 | [IRC channel]: https://web.libera.chat/?channels=#sway | 57 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway |
64 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | 58 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 |
65 | [GitHub releases]: https://github.com/swaywm/sway/releases | 59 | [GitHub releases]: https://github.com/swaywm/sway/releases |
66 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | 60 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup |
67 | [wlroots]: https://github.com/swaywm/wlroots | 61 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots |
68 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | 62 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc |
diff --git a/README.uk.md b/README.uk.md index 204fc40b..33359cff 100644 --- a/README.uk.md +++ b/README.uk.md | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | Sway це ÑуміÑний з [i3](https://i3wm.org/) композитор [Wayland](http://wayland.freedesktop.org/). | 3 | Sway це ÑуміÑний з [i3](https://i3wm.org/) композитор [Wayland](http://wayland.freedesktop.org/). |
4 | ОзнайомтеÑÑŒ з [ЧаПами](https://github.com/swaywm/sway/wiki). ПриєднуйтеÑÑŒ до [Ñпільноти в | 4 | ОзнайомтеÑÑŒ з [ЧаПами](https://github.com/swaywm/sway/wiki). ПриєднуйтеÑÑŒ до [Ñпільноти в |
5 | IRC](https://web.libera.chat/?channels=#sway) (#sway на | 5 | IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway на |
6 | irc.libera.chat). | 6 | irc.libera.chat). |
7 | 7 | ||
8 | ## Підтримка українÑькою мовою | 8 | ## Підтримка українÑькою мовою |
@@ -36,10 +36,10 @@ Sway доÑтупний у багатьох диÑтрибутивах Linux (а | |||
36 | Ð’Ñтановіть залежноÑÑ‚Ñ–: | 36 | Ð’Ñтановіть залежноÑÑ‚Ñ–: |
37 | 37 | ||
38 | * meson \* | 38 | * meson \* |
39 | * [wlroots](https://github.com/swaywm/wlroots) | 39 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
40 | * wayland | 40 | * wayland |
41 | * wayland-protocols \* | 41 | * wayland-protocols \* |
42 | * pcre | 42 | * pcre2 |
43 | * json-c | 43 | * json-c |
44 | * pango | 44 | * pango |
45 | * cairo | 45 | * cairo |
@@ -55,12 +55,6 @@ _\*Лише Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¿Ñ–Ð»Ñції_ | |||
55 | ninja -C build | 55 | ninja -C build |
56 | sudo ninja -C build install | 56 | sudo ninja -C build install |
57 | 57 | ||
58 | Ðа ÑиÑтемах без logind, необхідно вÑтановити біт SUID на виконуваний файл sway: | ||
59 | |||
60 | sudo chmod a+s /usr/local/bin/sway | ||
61 | |||
62 | Sway втратить права доÑтупу root незабаром піÑÐ»Ñ Ð·Ð°Ð¿ÑƒÑку. | ||
63 | |||
64 | ## ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ | 58 | ## ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ |
65 | 59 | ||
66 | Якщо ви вже викориÑтовуєте i3, Ñкопіюйте Ñвій файл налаштувань | 60 | Якщо ви вже викориÑтовуєте i3, Ñкопіюйте Ñвій файл налаштувань |
diff --git a/README.zh-CN.md b/README.zh-CN.md index 3f25b6d9..a6f4518a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md | |||
@@ -1,63 +1,50 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | sway 是和 [i3](https://i3wm.org/) 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor. | 3 | sway 是和 [i3](https://i3wm.org/) 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor。 |
4 | 阅读 [FAQ](https://github.com/swaywm/sway/wiki). åŠ å…¥ [IRC | 4 | [查看FAQ](https://github.com/swaywm/sway/wiki)/ [åŠ å…¥IRC频é“](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat) |
5 | 频é“](https://web.libera.chat/?channels=#sway) (#sway on | ||
6 | irc.libera.chat). | ||
7 | 5 | ||
8 | ## å‘布ç¾å | 6 | ## å‘è¡Œç¾å |
9 | 7 | ||
10 | å‘布是以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) ç¾å | 8 | æ¯ä¸ªå‘行版都以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 的密钥ç¾å并å‘布在 [GitHub](https://github.com/swaywm/sway/releases)上。 |
11 | 并å‘布在 [GitHub](https://github.com/swaywm/sway/releases). | ||
12 | 9 | ||
13 | ## 安装 | 10 | ## 安装 |
14 | 11 | ||
15 | ### ä»Žè½¯ä»¶åŒ…ä¸ | 12 | ### 从包管ç†å™¨å®‰è£… |
16 | 13 | ||
17 | Sway 在很多å‘行版ä¸å¯ç”¨. å°è¯•åœ¨ä½ çš„å‘行版ä¸å®‰è£… "sway" 包. | 14 | Sway 在很多å‘行版ä¸å¯ç”¨ã€‚请å°è¯•åœ¨ä½ çš„å‘行版ä¸å®‰è£… `sway` 。 |
18 | 如果这ä¸å¯ç”¨, 请到 [æ¤ wiki 页](https://github.com/swaywm/sway/wiki/Unsupported-packages) | ||
19 | æ£€æŸ¥é’ˆå¯¹ä½ çš„å‘行版关于安装的信æ¯. | ||
20 | 15 | ||
21 | å¦‚æžœä½ æœ‰å…´è¶£ç»™ä½ çš„å‘行版打包 sway, åœä¸‹æ¥åˆ° IRC 频é“或者å‘邮件至 sir@cmpwn.com 获å–建议. | 16 | ### 从æºç 编译 |
22 | 17 | ||
23 | ### 从æºä»£ç 编译 | 18 | 如果想è¦æž„建最新版swayå’Œwlroots用以测试和开å‘,请查看 [æ¤wiki页é¢](https://github.com/swaywm/sway/wiki/Development-Setup) |
24 | 19 | ||
25 | 安装ä¾èµ–: | 20 | 安装如下ä¾èµ–: |
26 | 21 | ||
27 | * meson \* | 22 | * meson \* |
28 | * [wlroots](https://github.com/swaywm/wlroots) | 23 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
29 | * wayland | 24 | * wayland |
30 | * wayland-protocols \* | 25 | * wayland-protocols \* |
31 | * pcre | 26 | * pcre2 |
32 | * json-c | 27 | * json-c |
33 | * pango | 28 | * pango |
34 | * cairo | 29 | * cairo |
35 | * gdk-pixbuf2 (å¯é€‰çš„: system tray) | 30 | * gdk-pixbuf2 (å¯é€‰çš„: system tray) |
36 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (å¯é€‰çš„: man pages) \* | 31 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (å¯é€‰: man pages) \* |
37 | * git \* | 32 | * git \* |
38 | 33 | ||
39 | _\*编译时ä¾èµ–_ | 34 | _\*编译时ä¾èµ–_ |
40 | 35 | ||
41 | è¿è¡Œè¿™äº›å‘½ä»¤: | 36 | è¿è¡Œå¦‚下命令: |
42 | 37 | ||
43 | meson build | 38 | meson build/ |
44 | ninja -C build | 39 | ninja -C build/ |
45 | sudo ninja -C build install | 40 | sudo ninja -C build/ install |
46 | |||
47 | 在没有 logind 的系统上, ä½ éœ€è¦ç»™ sway 二进制设置 suid: | ||
48 | |||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | |||
51 | Sway 将会在å¯åŠ¨åŽå°½å¿«ä¸¢æŽ‰ root æƒé™. | ||
52 | 41 | ||
53 | ## é…ç½® | 42 | ## é…ç½® |
54 | 43 | ||
55 | å¦‚æžœä½ å·²ç»åœ¨ä½¿ç”¨ i3, 接下æ¥å¤åˆ¶ä½ çš„ i3 é…置到 `~/.config/sway/config` | 44 | å¦‚æžœä½ å·²ç»åœ¨ä½¿ç”¨i3,直接å¤åˆ¶i3é…置文件到 `~/.config/sway/config`,这是开箱å³ç”¨çš„ã€‚æˆ–è€…ï¼Œä½ å¯ä»¥å¤åˆ¶é…ç½®æ ·ä¾‹åˆ°`~/.config/sway/config`。它通常ä½äºŽ `/etc/sway/config`。 |
56 | 它å¯ä»¥ç›´æŽ¥å·¥ä½œ. 或者, å¤åˆ¶æ ·æœ¬é…置文件到 | 45 | è¿è¡Œ `man 5 sway` 获å–关于é…置的更多信æ¯ã€‚ |
57 | `~/.config/sway/config`. 它通常ä½äºŽ `/etc/sway/config`. | ||
58 | è¿è¡Œ `man 5 sway` 获å–关于é…置的信æ¯. | ||
59 | 46 | ||
60 | ## è¿è¡Œ | 47 | ## è¿è¡Œ |
61 | 48 | ||
62 | 从 TTY ä¸è¿è¡Œ `sway` . æŸäº›æ˜¾ç¤ºç®¡ç†å™¨å¯èƒ½ä¼šå·¥ä½œä½†å¹¶ä¸è¢« sway æ”¯æŒ | 49 | 从 TTY ä¸è¿è¡Œ `sway`。 æŸäº›æ˜¾ç¤ºç®¡ç†å™¨ï¼ˆDisplay Manager)也许å¯ä»¥å·¥ä½œä½†ä¸è¢« sway 支æŒã€‚ |
63 | (已知的 gdm 工作得éžå¸¸å¥½). | 50 | (已知 gdm 工作得éžå¸¸å¥½)。 |
diff --git a/README.zh-TW.md b/README.zh-TW.md index 4834ded2..2de2f63f 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | sway 是一個與 [i3](https://i3wm.org/) 相容的 [Wayland](http://wayland.freedesktop.org/) compositor。 | 3 | sway 是一個與 [i3](https://i3wm.org/) 相容的 [Wayland](http://wayland.freedesktop.org/) compositor。 |
4 | 閱讀 [FAQ](https://github.com/swaywm/sway/wiki)。 åŠ å…¥ [IRC | 4 | 閱讀 [FAQ](https://github.com/swaywm/sway/wiki)。 åŠ å…¥ [IRC |
5 | é »é“](https://web.libera.chat/?channels=#sway) (#sway on | 5 | é »é“](https://web.libera.chat/gamja/?channels=#sway) (#sway on |
6 | irc.libera.chat) | 6 | irc.libera.chat) |
7 | 7 | ||
8 | ## ç™¼è¡Œç°½ç« | 8 | ## ç™¼è¡Œç°½ç« |
@@ -25,10 +25,10 @@ Sway 在許多發行版都有æä¾›ã€‚è«‹è‡ªå·±å˜—è©¦æ–¼ä½ çš„ç™¼è¡Œç‰ˆå®‰è£ ã€ | |||
25 | 相ä¾å¥—件: | 25 | 相ä¾å¥—件: |
26 | 26 | ||
27 | * meson \* | 27 | * meson \* |
28 | * [wlroots](https://github.com/swaywm/wlroots) | 28 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
29 | * wayland | 29 | * wayland |
30 | * wayland-protocols \* | 30 | * wayland-protocols \* |
31 | * pcre | 31 | * pcre2 |
32 | * json-c | 32 | * json-c |
33 | * pango | 33 | * pango |
34 | * cairo | 34 | * cairo |
@@ -44,12 +44,6 @@ _\*ç·¨è¯æ™‚相ä¾_ | |||
44 | ninja -C build | 44 | ninja -C build |
45 | sudo ninja -C build install | 45 | sudo ninja -C build install |
46 | 46 | ||
47 | 在沒有 logind çš„ç³»çµ±ä¸Šï¼Œä½ éœ€è¦ç‚º sway çš„åŸ·è¡Œæª”åŠ ä¸Š suid。 | ||
48 | |||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | |||
51 | Sway 在啟動ä¸ä¹…後就會放棄 root 權é™ã€‚ | ||
52 | |||
53 | ## è¨å®šæª” | 47 | ## è¨å®šæª” |
54 | 48 | ||
55 | å¦‚æžœä½ å·²ç¶“åœ¨ä½¿ç”¨ i3ï¼Œä½ å¯ä»¥ç›´æŽ¥å°‡ä½ çš„ i3 è¨å®šæª”複製到 `~/.config/sway/config` 然後就能直接使用。 | 49 | å¦‚æžœä½ å·²ç¶“åœ¨ä½¿ç”¨ i3ï¼Œä½ å¯ä»¥ç›´æŽ¥å°‡ä½ çš„ i3 è¨å®šæª”複製到 `~/.config/sway/config` 然後就能直接使用。 |
diff --git a/client/pool-buffer.c b/client/pool-buffer.c index ea31edd3..c47c40eb 100644 --- a/client/pool-buffer.c +++ b/client/pool-buffer.c | |||
@@ -1,50 +1,42 @@ | |||
1 | #define _POSIX_C_SOURCE 200809 | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <cairo.h> | 2 | #include <cairo.h> |
3 | #include <errno.h> | ||
4 | #include <fcntl.h> | 4 | #include <fcntl.h> |
5 | #include <pango/pangocairo.h> | 5 | #include <pango/pangocairo.h> |
6 | #include <stdio.h> | 6 | #include <stdio.h> |
7 | #include <stdlib.h> | 7 | #include <stdlib.h> |
8 | #include <string.h> | 8 | #include <string.h> |
9 | #include <sys/mman.h> | 9 | #include <sys/mman.h> |
10 | #include <time.h> | ||
10 | #include <unistd.h> | 11 | #include <unistd.h> |
11 | #include <wayland-client.h> | 12 | #include <wayland-client.h> |
12 | #include "config.h" | 13 | #include "config.h" |
13 | #include "pool-buffer.h" | 14 | #include "pool-buffer.h" |
14 | #include "util.h" | 15 | #include "util.h" |
15 | 16 | ||
16 | static int create_pool_file(size_t size, char **name) { | 17 | static int anonymous_shm_open(void) { |
17 | static const char template[] = "sway-client-XXXXXX"; | 18 | int retries = 100; |
18 | const char *path = getenv("XDG_RUNTIME_DIR"); | 19 | |
19 | if (path == NULL) { | 20 | do { |
20 | fprintf(stderr, "XDG_RUNTIME_DIR is not set\n"); | 21 | // try a probably-unique name |
21 | return -1; | 22 | struct timespec ts; |
22 | } | 23 | clock_gettime(CLOCK_MONOTONIC, &ts); |
23 | 24 | pid_t pid = getpid(); | |
24 | size_t name_size = strlen(template) + 1 + strlen(path) + 1; | 25 | char name[50]; |
25 | *name = malloc(name_size); | 26 | snprintf(name, sizeof(name), "/sway-%x-%x", |
26 | if (*name == NULL) { | 27 | (unsigned int)pid, (unsigned int)ts.tv_nsec); |
27 | fprintf(stderr, "allocation failed\n"); | 28 | |
28 | return -1; | 29 | // shm_open guarantees that O_CLOEXEC is set |
29 | } | 30 | int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); |
30 | snprintf(*name, name_size, "%s/%s", path, template); | 31 | if (fd >= 0) { |
31 | 32 | shm_unlink(name); | |
32 | int fd = mkstemp(*name); | 33 | return fd; |
33 | if (fd < 0) { | 34 | } |
34 | return -1; | ||
35 | } | ||
36 | |||
37 | if (!sway_set_cloexec(fd, true)) { | ||
38 | close(fd); | ||
39 | return -1; | ||
40 | } | ||
41 | 35 | ||
42 | if (ftruncate(fd, size) < 0) { | 36 | --retries; |
43 | close(fd); | 37 | } while (retries > 0 && errno == EEXIST); |
44 | return -1; | ||
45 | } | ||
46 | 38 | ||
47 | return fd; | 39 | return -1; |
48 | } | 40 | } |
49 | 41 | ||
50 | static void buffer_release(void *data, struct wl_buffer *wl_buffer) { | 42 | static void buffer_release(void *data, struct wl_buffer *wl_buffer) { |
@@ -62,17 +54,20 @@ static struct pool_buffer *create_buffer(struct wl_shm *shm, | |||
62 | uint32_t stride = width * 4; | 54 | uint32_t stride = width * 4; |
63 | size_t size = stride * height; | 55 | size_t size = stride * height; |
64 | 56 | ||
65 | char *name; | 57 | int fd = anonymous_shm_open(); |
66 | int fd = create_pool_file(size, &name); | 58 | if (fd == -1) { |
67 | assert(fd != -1); | 59 | return NULL; |
60 | } | ||
61 | if (ftruncate(fd, size) < 0) { | ||
62 | close(fd); | ||
63 | return NULL; | ||
64 | } | ||
68 | void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | 65 | void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
69 | struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); | 66 | struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); |
70 | buf->buffer = wl_shm_pool_create_buffer(pool, 0, | 67 | buf->buffer = wl_shm_pool_create_buffer(pool, 0, |
71 | width, height, stride, format); | 68 | width, height, stride, format); |
72 | wl_shm_pool_destroy(pool); | 69 | wl_shm_pool_destroy(pool); |
73 | close(fd); | 70 | close(fd); |
74 | unlink(name); | ||
75 | free(name); | ||
76 | 71 | ||
77 | buf->size = size; | 72 | buf->size = size; |
78 | buf->width = width; | 73 | buf->width = width; |
diff --git a/common/gesture.c b/common/gesture.c new file mode 100644 index 00000000..272aa837 --- /dev/null +++ b/common/gesture.c | |||
@@ -0,0 +1,332 @@ | |||
1 | #include "gesture.h" | ||
2 | |||
3 | #include <math.h> | ||
4 | #include <stdarg.h> | ||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <string.h> | ||
8 | #include "list.h" | ||
9 | #include "log.h" | ||
10 | #include "stringop.h" | ||
11 | |||
12 | const uint8_t GESTURE_FINGERS_ANY = 0; | ||
13 | |||
14 | char *gesture_parse(const char *input, struct gesture *output) { | ||
15 | // Clear output in case of failure | ||
16 | output->type = GESTURE_TYPE_NONE; | ||
17 | output->fingers = GESTURE_FINGERS_ANY; | ||
18 | output->directions = GESTURE_DIRECTION_NONE; | ||
19 | |||
20 | // Split input type, fingers and directions | ||
21 | list_t *split = split_string(input, ":"); | ||
22 | if (split->length < 1 || split->length > 3) { | ||
23 | return format_str( | ||
24 | "expected <gesture>[:<fingers>][:direction], got %s", | ||
25 | input); | ||
26 | } | ||
27 | |||
28 | // Parse gesture type | ||
29 | if (strcmp(split->items[0], "hold") == 0) { | ||
30 | output->type = GESTURE_TYPE_HOLD; | ||
31 | } else if (strcmp(split->items[0], "pinch") == 0) { | ||
32 | output->type = GESTURE_TYPE_PINCH; | ||
33 | } else if (strcmp(split->items[0], "swipe") == 0) { | ||
34 | output->type = GESTURE_TYPE_SWIPE; | ||
35 | } else { | ||
36 | return format_str("expected hold|pinch|swipe, got %s", | ||
37 | (const char *)split->items[0]); | ||
38 | } | ||
39 | |||
40 | // Parse optional arguments | ||
41 | if (split->length > 1) { | ||
42 | char *next = split->items[1]; | ||
43 | |||
44 | // Try to parse as finger count (1-9) | ||
45 | if (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') { | ||
46 | output->fingers = atoi(next); | ||
47 | |||
48 | // Move to next if available | ||
49 | next = split->length == 3 ? split->items[2] : NULL; | ||
50 | } else if (split->length == 3) { | ||
51 | // Fail here if argument can only be finger count | ||
52 | return format_str("expected 1-9, got %s", next); | ||
53 | } | ||
54 | |||
55 | // If there is an argument left, try to parse as direction | ||
56 | if (next) { | ||
57 | list_t *directions = split_string(next, "+"); | ||
58 | |||
59 | for (int i = 0; i < directions->length; ++i) { | ||
60 | const char *item = directions->items[i]; | ||
61 | if (strcmp(item, "any") == 0) { | ||
62 | continue; | ||
63 | } else if (strcmp(item, "up") == 0) { | ||
64 | output->directions |= GESTURE_DIRECTION_UP; | ||
65 | } else if (strcmp(item, "down") == 0) { | ||
66 | output->directions |= GESTURE_DIRECTION_DOWN; | ||
67 | } else if (strcmp(item, "left") == 0) { | ||
68 | output->directions |= GESTURE_DIRECTION_LEFT; | ||
69 | } else if (strcmp(item, "right") == 0) { | ||
70 | output->directions |= GESTURE_DIRECTION_RIGHT; | ||
71 | } else if (strcmp(item, "inward") == 0) { | ||
72 | output->directions |= GESTURE_DIRECTION_INWARD; | ||
73 | } else if (strcmp(item, "outward") == 0) { | ||
74 | output->directions |= GESTURE_DIRECTION_OUTWARD; | ||
75 | } else if (strcmp(item, "clockwise") == 0) { | ||
76 | output->directions |= GESTURE_DIRECTION_CLOCKWISE; | ||
77 | } else if (strcmp(item, "counterclockwise") == 0) { | ||
78 | output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; | ||
79 | } else { | ||
80 | return format_str("expected direction, got %s", item); | ||
81 | } | ||
82 | } | ||
83 | list_free_items_and_destroy(directions); | ||
84 | } | ||
85 | } // if optional args | ||
86 | |||
87 | list_free_items_and_destroy(split); | ||
88 | |||
89 | return NULL; | ||
90 | } | ||
91 | |||
92 | const char *gesture_type_string(enum gesture_type type) { | ||
93 | switch (type) { | ||
94 | case GESTURE_TYPE_NONE: | ||
95 | return "none"; | ||
96 | case GESTURE_TYPE_HOLD: | ||
97 | return "hold"; | ||
98 | case GESTURE_TYPE_PINCH: | ||
99 | return "pinch"; | ||
100 | case GESTURE_TYPE_SWIPE: | ||
101 | return "swipe"; | ||
102 | } | ||
103 | |||
104 | return NULL; | ||
105 | } | ||
106 | |||
107 | const char *gesture_direction_string(enum gesture_direction direction) { | ||
108 | switch (direction) { | ||
109 | case GESTURE_DIRECTION_NONE: | ||
110 | return "none"; | ||
111 | case GESTURE_DIRECTION_UP: | ||
112 | return "up"; | ||
113 | case GESTURE_DIRECTION_DOWN: | ||
114 | return "down"; | ||
115 | case GESTURE_DIRECTION_LEFT: | ||
116 | return "left"; | ||
117 | case GESTURE_DIRECTION_RIGHT: | ||
118 | return "right"; | ||
119 | case GESTURE_DIRECTION_INWARD: | ||
120 | return "inward"; | ||
121 | case GESTURE_DIRECTION_OUTWARD: | ||
122 | return "outward"; | ||
123 | case GESTURE_DIRECTION_CLOCKWISE: | ||
124 | return "clockwise"; | ||
125 | case GESTURE_DIRECTION_COUNTERCLOCKWISE: | ||
126 | return "counterclockwise"; | ||
127 | } | ||
128 | |||
129 | return NULL; | ||
130 | } | ||
131 | |||
132 | // Helper to turn combination of directions flags into string representation. | ||
133 | static char *gesture_directions_to_string(uint32_t directions) { | ||
134 | char *result = NULL; | ||
135 | |||
136 | for (uint8_t bit = 0; bit < 32; bit++) { | ||
137 | uint32_t masked = directions & (1 << bit); | ||
138 | if (masked) { | ||
139 | const char *name = gesture_direction_string(masked); | ||
140 | |||
141 | if (!name) { | ||
142 | name = "unknown"; | ||
143 | } | ||
144 | |||
145 | if (!result) { | ||
146 | result = strdup(name); | ||
147 | } else { | ||
148 | char *new = format_str("%s+%s", result, name); | ||
149 | free(result); | ||
150 | result = new; | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | if(!result) { | ||
156 | return strdup("any"); | ||
157 | } | ||
158 | |||
159 | return result; | ||
160 | } | ||
161 | |||
162 | char *gesture_to_string(struct gesture *gesture) { | ||
163 | char *directions = gesture_directions_to_string(gesture->directions); | ||
164 | char *result = format_str("%s:%u:%s", | ||
165 | gesture_type_string(gesture->type), | ||
166 | gesture->fingers, directions); | ||
167 | free(directions); | ||
168 | return result; | ||
169 | } | ||
170 | |||
171 | bool gesture_check(struct gesture *target, enum gesture_type type, uint8_t fingers) { | ||
172 | // Check that gesture type matches | ||
173 | if (target->type != type) { | ||
174 | return false; | ||
175 | } | ||
176 | |||
177 | // Check that finger count matches | ||
178 | if (target->fingers != GESTURE_FINGERS_ANY && target->fingers != fingers) { | ||
179 | return false; | ||
180 | } | ||
181 | |||
182 | return true; | ||
183 | } | ||
184 | |||
185 | bool gesture_match(struct gesture *target, struct gesture *to_match, bool exact) { | ||
186 | // Check type and fingers | ||
187 | if (!gesture_check(target, to_match->type, to_match->fingers)) { | ||
188 | return false; | ||
189 | } | ||
190 | |||
191 | // Enforce exact matches ... | ||
192 | if (exact && target->directions != to_match->directions) { | ||
193 | return false; | ||
194 | } | ||
195 | |||
196 | // ... or ensure all target directions are matched | ||
197 | return (target->directions & to_match->directions) == target->directions; | ||
198 | } | ||
199 | |||
200 | bool gesture_equal(struct gesture *a, struct gesture *b) { | ||
201 | return a->type == b->type | ||
202 | && a->fingers == b->fingers | ||
203 | && a->directions == b->directions; | ||
204 | } | ||
205 | |||
206 | // Return count of set bits in directions bit field. | ||
207 | static uint8_t gesture_directions_count(uint32_t directions) { | ||
208 | uint8_t count = 0; | ||
209 | for (; directions; directions >>= 1) { | ||
210 | count += directions & 1; | ||
211 | } | ||
212 | return count; | ||
213 | } | ||
214 | |||
215 | // Compare direction bit count of two direction. | ||
216 | static int8_t gesture_directions_compare(uint32_t a, uint32_t b) { | ||
217 | return gesture_directions_count(a) - gesture_directions_count(b); | ||
218 | } | ||
219 | |||
220 | // Compare two direction based on their direction bit count | ||
221 | int8_t gesture_compare(struct gesture *a, struct gesture *b) { | ||
222 | return gesture_directions_compare(a->directions, b->directions); | ||
223 | } | ||
224 | |||
225 | void gesture_tracker_begin(struct gesture_tracker *tracker, | ||
226 | enum gesture_type type, uint8_t fingers) { | ||
227 | tracker->type = type; | ||
228 | tracker->fingers = fingers; | ||
229 | |||
230 | tracker->dx = 0.0; | ||
231 | tracker->dy = 0.0; | ||
232 | tracker->scale = 1.0; | ||
233 | tracker->rotation = 0.0; | ||
234 | |||
235 | sway_log(SWAY_DEBUG, "begin tracking %s:%u:? gesture", | ||
236 | gesture_type_string(type), fingers); | ||
237 | } | ||
238 | |||
239 | bool gesture_tracker_check(struct gesture_tracker *tracker, enum gesture_type type) { | ||
240 | return tracker->type == type; | ||
241 | } | ||
242 | |||
243 | void gesture_tracker_update(struct gesture_tracker *tracker, | ||
244 | double dx, double dy, double scale, double rotation) { | ||
245 | if (tracker->type == GESTURE_TYPE_HOLD) { | ||
246 | sway_assert(false, "hold does not update."); | ||
247 | return; | ||
248 | } | ||
249 | |||
250 | tracker->dx += dx; | ||
251 | tracker->dy += dy; | ||
252 | |||
253 | if (tracker->type == GESTURE_TYPE_PINCH) { | ||
254 | tracker->scale = scale; | ||
255 | tracker->rotation += rotation; | ||
256 | } | ||
257 | |||
258 | sway_log(SWAY_DEBUG, "update tracking %s:%u:? gesture: %f %f %f %f", | ||
259 | gesture_type_string(tracker->type), | ||
260 | tracker->fingers, | ||
261 | tracker->dx, tracker->dy, | ||
262 | tracker->scale, tracker->rotation); | ||
263 | } | ||
264 | |||
265 | void gesture_tracker_cancel(struct gesture_tracker *tracker) { | ||
266 | sway_log(SWAY_DEBUG, "cancel tracking %s:%u:? gesture", | ||
267 | gesture_type_string(tracker->type), tracker->fingers); | ||
268 | |||
269 | tracker->type = GESTURE_TYPE_NONE; | ||
270 | } | ||
271 | |||
272 | struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) { | ||
273 | struct gesture *result = calloc(1, sizeof(struct gesture)); | ||
274 | |||
275 | // Ignore gesture under some threshold | ||
276 | // TODO: Make configurable | ||
277 | const double min_rotation = 5; | ||
278 | const double min_scale_delta = 0.1; | ||
279 | |||
280 | // Determine direction | ||
281 | switch(tracker->type) { | ||
282 | // Gestures with scale and rotation | ||
283 | case GESTURE_TYPE_PINCH: | ||
284 | if (tracker->rotation > min_rotation) { | ||
285 | result->directions |= GESTURE_DIRECTION_CLOCKWISE; | ||
286 | } | ||
287 | if (tracker->rotation < -min_rotation) { | ||
288 | result->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; | ||
289 | } | ||
290 | |||
291 | if (tracker->scale > (1.0 + min_scale_delta)) { | ||
292 | result->directions |= GESTURE_DIRECTION_OUTWARD; | ||
293 | } | ||
294 | if (tracker->scale < (1.0 - min_scale_delta)) { | ||
295 | result->directions |= GESTURE_DIRECTION_INWARD; | ||
296 | } | ||
297 | __attribute__ ((fallthrough)); | ||
298 | // Gestures with dx and dy | ||
299 | case GESTURE_TYPE_SWIPE: | ||
300 | if (fabs(tracker->dx) > fabs(tracker->dy)) { | ||
301 | if (tracker->dx > 0) { | ||
302 | result->directions |= GESTURE_DIRECTION_RIGHT; | ||
303 | } else { | ||
304 | result->directions |= GESTURE_DIRECTION_LEFT; | ||
305 | } | ||
306 | } else { | ||
307 | if (tracker->dy > 0) { | ||
308 | result->directions |= GESTURE_DIRECTION_DOWN; | ||
309 | } else { | ||
310 | result->directions |= GESTURE_DIRECTION_UP; | ||
311 | } | ||
312 | } | ||
313 | // Gesture without any direction | ||
314 | case GESTURE_TYPE_HOLD: | ||
315 | break; | ||
316 | // Not tracking any gesture | ||
317 | case GESTURE_TYPE_NONE: | ||
318 | sway_assert(false, "Not tracking any gesture."); | ||
319 | return result; | ||
320 | } | ||
321 | |||
322 | result->type = tracker->type; | ||
323 | result->fingers = tracker->fingers; | ||
324 | |||
325 | char *description = gesture_to_string(result); | ||
326 | sway_log(SWAY_DEBUG, "end tracking gesture: %s", description); | ||
327 | free(description); | ||
328 | |||
329 | tracker->type = GESTURE_TYPE_NONE; | ||
330 | |||
331 | return result; | ||
332 | } | ||
diff --git a/common/ipc-client.c b/common/ipc-client.c index d30212d2..a0be2b2d 100644 --- a/common/ipc-client.c +++ b/common/ipc-client.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #include <stdint.h> | 2 | #include <stdint.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
diff --git a/common/log.c b/common/log.c index 483420e7..3eacdb34 100644 --- a/common/log.c +++ b/common/log.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200112L | ||
2 | #include <signal.h> | 1 | #include <signal.h> |
3 | #include <stdarg.h> | 2 | #include <stdarg.h> |
4 | #include <stdio.h> | 3 | #include <stdio.h> |
diff --git a/common/loop.c b/common/loop.c index 80fe18ea..b99c6d55 100644 --- a/common/loop.c +++ b/common/loop.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200112L | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
diff --git a/common/meson.build b/common/meson.build index c653dd72..c0ce1f68 100644 --- a/common/meson.build +++ b/common/meson.build | |||
@@ -1,8 +1,8 @@ | |||
1 | lib_sway_common = static_library( | 1 | lib_sway_common = static_library( |
2 | 'sway-common', | 2 | 'sway-common', |
3 | files( | 3 | files( |
4 | 'background-image.c', | ||
5 | 'cairo.c', | 4 | 'cairo.c', |
5 | 'gesture.c', | ||
6 | 'ipc-client.c', | 6 | 'ipc-client.c', |
7 | 'log.c', | 7 | 'log.c', |
8 | 'loop.c', | 8 | 'loop.c', |
@@ -13,7 +13,6 @@ lib_sway_common = static_library( | |||
13 | ), | 13 | ), |
14 | dependencies: [ | 14 | dependencies: [ |
15 | cairo, | 15 | cairo, |
16 | gdk_pixbuf, | ||
17 | pango, | 16 | pango, |
18 | pangocairo, | 17 | pangocairo, |
19 | wayland_client.partial_dependency(compile_args: true) | 18 | wayland_client.partial_dependency(compile_args: true) |
diff --git a/common/pango.c b/common/pango.c index dbc369dc..288569b3 100644 --- a/common/pango.c +++ b/common/pango.c | |||
@@ -50,7 +50,7 @@ size_t escape_markup_text(const char *src, char *dest) { | |||
50 | return length; | 50 | return length; |
51 | } | 51 | } |
52 | 52 | ||
53 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, | 53 | PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, |
54 | const char *text, double scale, bool markup) { | 54 | const char *text, double scale, bool markup) { |
55 | PangoLayout *layout = pango_cairo_create_layout(cairo); | 55 | PangoLayout *layout = pango_cairo_create_layout(cairo); |
56 | PangoAttrList *attrs; | 56 | PangoAttrList *attrs; |
@@ -73,60 +73,59 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, | |||
73 | } | 73 | } |
74 | 74 | ||
75 | pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); | 75 | pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); |
76 | PangoFontDescription *desc = pango_font_description_from_string(font); | ||
77 | pango_layout_set_font_description(layout, desc); | 76 | pango_layout_set_font_description(layout, desc); |
78 | pango_layout_set_single_paragraph_mode(layout, 1); | 77 | pango_layout_set_single_paragraph_mode(layout, 1); |
79 | pango_layout_set_attributes(layout, attrs); | 78 | pango_layout_set_attributes(layout, attrs); |
80 | pango_attr_list_unref(attrs); | 79 | pango_attr_list_unref(attrs); |
81 | pango_font_description_free(desc); | ||
82 | return layout; | 80 | return layout; |
83 | } | 81 | } |
84 | 82 | ||
85 | void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, | 83 | void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, |
86 | int *baseline, double scale, bool markup, const char *fmt, ...) { | 84 | int *baseline, double scale, bool markup, const char *fmt, ...) { |
87 | va_list args; | 85 | va_list args; |
88 | va_start(args, fmt); | 86 | va_start(args, fmt); |
89 | // Add one since vsnprintf excludes null terminator. | 87 | char *buf = vformat_str(fmt, args); |
90 | int length = vsnprintf(NULL, 0, fmt, args) + 1; | ||
91 | va_end(args); | 88 | va_end(args); |
92 | |||
93 | char *buf = malloc(length); | ||
94 | if (buf == NULL) { | 89 | if (buf == NULL) { |
95 | sway_log(SWAY_ERROR, "Failed to allocate memory"); | ||
96 | return; | 90 | return; |
97 | } | 91 | } |
98 | va_start(args, fmt); | ||
99 | vsnprintf(buf, length, fmt, args); | ||
100 | va_end(args); | ||
101 | 92 | ||
102 | PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); | 93 | PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); |
103 | pango_cairo_update_layout(cairo, layout); | 94 | pango_cairo_update_layout(cairo, layout); |
104 | pango_layout_get_pixel_size(layout, width, height); | 95 | pango_layout_get_pixel_size(layout, width, height); |
105 | if (baseline) { | 96 | if (baseline) { |
106 | *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; | 97 | *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; |
107 | } | 98 | } |
108 | g_object_unref(layout); | 99 | g_object_unref(layout); |
100 | |||
109 | free(buf); | 101 | free(buf); |
110 | } | 102 | } |
111 | 103 | ||
112 | void pango_printf(cairo_t *cairo, const char *font, | 104 | void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) { |
105 | cairo_t *cairo = cairo_create(NULL); | ||
106 | PangoContext *pango = pango_cairo_create_context(cairo); | ||
107 | // When passing NULL as a language, pango uses the current locale. | ||
108 | PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL); | ||
109 | |||
110 | *baseline = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; | ||
111 | *height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE; | ||
112 | |||
113 | pango_font_metrics_unref(metrics); | ||
114 | g_object_unref(pango); | ||
115 | cairo_destroy(cairo); | ||
116 | } | ||
117 | |||
118 | void render_text(cairo_t *cairo, const PangoFontDescription *desc, | ||
113 | double scale, bool markup, const char *fmt, ...) { | 119 | double scale, bool markup, const char *fmt, ...) { |
114 | va_list args; | 120 | va_list args; |
115 | va_start(args, fmt); | 121 | va_start(args, fmt); |
116 | // Add one since vsnprintf excludes null terminator. | 122 | char *buf = vformat_str(fmt, args); |
117 | int length = vsnprintf(NULL, 0, fmt, args) + 1; | ||
118 | va_end(args); | 123 | va_end(args); |
119 | |||
120 | char *buf = malloc(length); | ||
121 | if (buf == NULL) { | 124 | if (buf == NULL) { |
122 | sway_log(SWAY_ERROR, "Failed to allocate memory"); | ||
123 | return; | 125 | return; |
124 | } | 126 | } |
125 | va_start(args, fmt); | ||
126 | vsnprintf(buf, length, fmt, args); | ||
127 | va_end(args); | ||
128 | 127 | ||
129 | PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); | 128 | PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); |
130 | cairo_font_options_t *fo = cairo_font_options_create(); | 129 | cairo_font_options_t *fo = cairo_font_options_create(); |
131 | cairo_get_font_options(cairo, fo); | 130 | cairo_get_font_options(cairo, fo); |
132 | pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); | 131 | pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); |
@@ -134,5 +133,6 @@ void pango_printf(cairo_t *cairo, const char *font, | |||
134 | pango_cairo_update_layout(cairo, layout); | 133 | pango_cairo_update_layout(cairo, layout); |
135 | pango_cairo_show_layout(cairo, layout); | 134 | pango_cairo_show_layout(cairo, layout); |
136 | g_object_unref(layout); | 135 | g_object_unref(layout); |
136 | |||
137 | free(buf); | 137 | free(buf); |
138 | } | 138 | } |
diff --git a/common/stringop.c b/common/stringop.c index 7fb3fe12..16d04917 100644 --- a/common/stringop.c +++ b/common/stringop.c | |||
@@ -1,5 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
2 | #include <stdarg.h> | ||
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include <stdio.h> | 4 | #include <stdio.h> |
5 | #include <stdlib.h> | 5 | #include <stdlib.h> |
@@ -328,3 +328,35 @@ bool expand_path(char **path) { | |||
328 | wordfree(&p); | 328 | wordfree(&p); |
329 | return true; | 329 | return true; |
330 | } | 330 | } |
331 | |||
332 | char *vformat_str(const char *fmt, va_list args) { | ||
333 | char *str = NULL; | ||
334 | va_list args_copy; | ||
335 | va_copy(args_copy, args); | ||
336 | |||
337 | int len = vsnprintf(NULL, 0, fmt, args); | ||
338 | if (len < 0) { | ||
339 | sway_log_errno(SWAY_ERROR, "vsnprintf(\"%s\") failed", fmt); | ||
340 | goto out; | ||
341 | } | ||
342 | |||
343 | str = malloc(len + 1); | ||
344 | if (str == NULL) { | ||
345 | sway_log_errno(SWAY_ERROR, "malloc() failed"); | ||
346 | goto out; | ||
347 | } | ||
348 | |||
349 | vsnprintf(str, len + 1, fmt, args_copy); | ||
350 | |||
351 | out: | ||
352 | va_end(args_copy); | ||
353 | return str; | ||
354 | } | ||
355 | |||
356 | char *format_str(const char *fmt, ...) { | ||
357 | va_list args; | ||
358 | va_start(args, fmt); | ||
359 | char *str = vformat_str(fmt, args); | ||
360 | va_end(args); | ||
361 | return str; | ||
362 | } | ||
diff --git a/common/util.c b/common/util.c index 199f3ee1..7c492bcb 100644 --- a/common/util.c +++ b/common/util.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <fcntl.h> | 2 | #include <fcntl.h> |
4 | #include <math.h> | 3 | #include <math.h> |
@@ -80,6 +79,12 @@ enum movement_unit parse_movement_unit(const char *unit) { | |||
80 | 79 | ||
81 | int parse_movement_amount(int argc, char **argv, | 80 | int parse_movement_amount(int argc, char **argv, |
82 | struct movement_amount *amount) { | 81 | struct movement_amount *amount) { |
82 | if (!sway_assert(argc > 0, "Expected args in parse_movement_amount")) { | ||
83 | amount->amount = 0; | ||
84 | amount->unit = MOVEMENT_UNIT_INVALID; | ||
85 | return 0; | ||
86 | } | ||
87 | |||
83 | char *err; | 88 | char *err; |
84 | amount->amount = (int)strtol(argv[0], &err, 10); | 89 | amount->amount = (int)strtol(argv[0], &err, 10); |
85 | if (*err) { | 90 | if (*err) { |
diff --git a/completions/bash/sway b/completions/bash/sway index edd752cd..01b20073 100644 --- a/completions/bash/sway +++ b/completions/bash/sway | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | _sway() | 3 | _sway() |
4 | { | 4 | { |
5 | local cur prev | 5 | local cur prev short long |
6 | _get_comp_words_by_ref cur prev | 6 | _get_comp_words_by_ref cur prev |
7 | 7 | ||
8 | short=( | 8 | short=( |
diff --git a/completions/bash/swaybar b/completions/bash/swaybar index 1e085c65..3709d4f9 100644 --- a/completions/bash/swaybar +++ b/completions/bash/swaybar | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | _swaybar() | 3 | _swaybar() |
4 | { | 4 | { |
5 | local cur prev | 5 | local cur prev short long |
6 | _get_comp_words_by_ref cur prev | 6 | _get_comp_words_by_ref cur prev |
7 | 7 | ||
8 | short=( | 8 | short=( |
diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg index f865e4e1..30457751 100644 --- a/completions/bash/swaymsg +++ b/completions/bash/swaymsg | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | _swaymsg() | 3 | _swaymsg() |
4 | { | 4 | { |
5 | local cur prev | 5 | local cur prev types short long |
6 | _get_comp_words_by_ref cur prev | 6 | _get_comp_words_by_ref cur prev |
7 | 7 | ||
8 | types=( | 8 | types=( |
diff --git a/completions/meson.build b/completions/meson.build new file mode 100644 index 00000000..6bca9391 --- /dev/null +++ b/completions/meson.build | |||
@@ -0,0 +1,57 @@ | |||
1 | if get_option('zsh-completions') | ||
2 | zsh_files = files( | ||
3 | 'zsh/_sway', | ||
4 | 'zsh/_swaymsg', | ||
5 | ) | ||
6 | zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions') | ||
7 | |||
8 | install_data(zsh_files, install_dir: zsh_install_dir) | ||
9 | endif | ||
10 | |||
11 | if get_option('bash-completions') | ||
12 | bash_comp = dependency('bash-completion', required: false) | ||
13 | |||
14 | bash_files = files( | ||
15 | 'bash/sway', | ||
16 | 'bash/swaymsg', | ||
17 | ) | ||
18 | |||
19 | if get_option('swaybar') | ||
20 | bash_files += files('bash/swaybar') | ||
21 | endif | ||
22 | |||
23 | if bash_comp.found() | ||
24 | bash_install_dir = bash_comp.get_variable( | ||
25 | pkgconfig: 'completionsdir', | ||
26 | pkgconfig_define: ['datadir', datadir] | ||
27 | ) | ||
28 | else | ||
29 | bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') | ||
30 | endif | ||
31 | |||
32 | install_data(bash_files, install_dir: bash_install_dir) | ||
33 | endif | ||
34 | |||
35 | if get_option('fish-completions') | ||
36 | fish_comp = dependency('fish', required: false) | ||
37 | |||
38 | fish_files = files( | ||
39 | 'fish/sway.fish', | ||
40 | 'fish/swaymsg.fish', | ||
41 | ) | ||
42 | |||
43 | if get_option('swaynag') | ||
44 | fish_files += files('fish/swaynag.fish') | ||
45 | endif | ||
46 | |||
47 | if fish_comp.found() | ||
48 | fish_install_dir = fish_comp.get_variable( | ||
49 | pkgconfig: 'completionsdir', | ||
50 | pkgconfig_define: ['datadir', datadir] | ||
51 | ) | ||
52 | else | ||
53 | fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') | ||
54 | endif | ||
55 | |||
56 | install_data(fish_files, install_dir: fish_install_dir) | ||
57 | endif | ||
@@ -18,7 +18,7 @@ set $term foot | |||
18 | # Your preferred application launcher | 18 | # Your preferred application launcher |
19 | # Note: pass the final command to swaymsg so that the resulting window can be opened | 19 | # Note: pass the final command to swaymsg so that the resulting window can be opened |
20 | # on the original workspace that the command was run on. | 20 | # on the original workspace that the command was run on. |
21 | set $menu dmenu_path | dmenu | xargs swaymsg exec -- | 21 | set $menu dmenu_path | wmenu | xargs swaymsg exec -- |
22 | 22 | ||
23 | ### Output configuration | 23 | ### Output configuration |
24 | # | 24 | # |
@@ -37,7 +37,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill | |||
37 | # | 37 | # |
38 | # exec swayidle -w \ | 38 | # exec swayidle -w \ |
39 | # timeout 300 'swaylock -f -c 000000' \ | 39 | # timeout 300 'swaylock -f -c 000000' \ |
40 | # timeout 600 'swaymsg "output * dpms off"' resume 'swaymsg "output * dpms on"' \ | 40 | # timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \ |
41 | # before-sleep 'swaylock -f -c 000000' | 41 | # before-sleep 'swaylock -f -c 000000' |
42 | # | 42 | # |
43 | # This will lock your screen after 300 seconds of inactivity, then turn off | 43 | # This will lock your screen after 300 seconds of inactivity, then turn off |
@@ -82,7 +82,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill | |||
82 | bindsym $mod+Shift+c reload | 82 | bindsym $mod+Shift+c reload |
83 | 83 | ||
84 | # Exit sway (logs you out of your Wayland session) | 84 | # Exit sway (logs you out of your Wayland session) |
85 | bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit' | 85 | bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit' |
86 | # | 86 | # |
87 | # Moving around: | 87 | # Moving around: |
88 | # | 88 | # |
@@ -205,7 +205,7 @@ bar { | |||
205 | 205 | ||
206 | # When the status_command prints a new line to stdout, swaybar updates. | 206 | # When the status_command prints a new line to stdout, swaybar updates. |
207 | # The default just shows the current date and time. | 207 | # The default just shows the current date and time. |
208 | status_command while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done | 208 | status_command while date +'%Y-%m-%d %X'; do sleep 1; done |
209 | 209 | ||
210 | colors { | 210 | colors { |
211 | statusline #ffffff | 211 | statusline #ffffff |
diff --git a/contrib/autoname-workspaces.py b/contrib/autoname-workspaces.py deleted file mode 100755 index 3ec39928..00000000 --- a/contrib/autoname-workspaces.py +++ /dev/null | |||
@@ -1,124 +0,0 @@ | |||
1 | #!/usr/bin/python | ||
2 | |||
3 | # This script requires i3ipc-python package (install it from a system package manager | ||
4 | # or pip). | ||
5 | # It adds icons to the workspace name for each open window. | ||
6 | # Set your keybindings like this: set $workspace1 workspace number 1 | ||
7 | # Add your icons to WINDOW_ICONS. | ||
8 | # Based on https://github.com/maximbaz/dotfiles/blob/master/bin/i3-autoname-workspaces | ||
9 | |||
10 | import argparse | ||
11 | import i3ipc | ||
12 | import logging | ||
13 | import re | ||
14 | import signal | ||
15 | import sys | ||
16 | |||
17 | WINDOW_ICONS = { | ||
18 | "firefox": "", | ||
19 | } | ||
20 | |||
21 | DEFAULT_ICON = "ó°€" | ||
22 | |||
23 | |||
24 | def icon_for_window(window): | ||
25 | name = None | ||
26 | if window.app_id is not None and len(window.app_id) > 0: | ||
27 | name = window.app_id.lower() | ||
28 | elif window.window_class is not None and len(window.window_class) > 0: | ||
29 | name = window.window_class.lower() | ||
30 | |||
31 | if name in WINDOW_ICONS: | ||
32 | return WINDOW_ICONS[name] | ||
33 | |||
34 | logging.info("No icon available for window with name: %s" % str(name)) | ||
35 | return DEFAULT_ICON | ||
36 | |||
37 | def rename_workspaces(ipc): | ||
38 | for workspace in ipc.get_tree().workspaces(): | ||
39 | name_parts = parse_workspace_name(workspace.name) | ||
40 | icon_tuple = () | ||
41 | for w in workspace: | ||
42 | if w.app_id is not None or w.window_class is not None: | ||
43 | icon = icon_for_window(w) | ||
44 | if not ARGUMENTS.duplicates and icon in icon_tuple: | ||
45 | continue | ||
46 | icon_tuple += (icon,) | ||
47 | name_parts["icons"] = " ".join(icon_tuple) + " " | ||
48 | new_name = construct_workspace_name(name_parts) | ||
49 | ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name)) | ||
50 | |||
51 | |||
52 | def undo_window_renaming(ipc): | ||
53 | for workspace in ipc.get_tree().workspaces(): | ||
54 | name_parts = parse_workspace_name(workspace.name) | ||
55 | name_parts["icons"] = None | ||
56 | new_name = construct_workspace_name(name_parts) | ||
57 | ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name)) | ||
58 | ipc.main_quit() | ||
59 | sys.exit(0) | ||
60 | |||
61 | |||
62 | def parse_workspace_name(name): | ||
63 | return re.match( | ||
64 | "(?P<num>[0-9]+):?(?P<shortname>\w+)? ?(?P<icons>.+)?", name | ||
65 | ).groupdict() | ||
66 | |||
67 | |||
68 | def construct_workspace_name(parts): | ||
69 | new_name = str(parts["num"]) | ||
70 | if parts["shortname"] or parts["icons"]: | ||
71 | new_name += ":" | ||
72 | |||
73 | if parts["shortname"]: | ||
74 | new_name += parts["shortname"] | ||
75 | |||
76 | if parts["icons"]: | ||
77 | new_name += " " + parts["icons"] | ||
78 | |||
79 | return new_name | ||
80 | |||
81 | |||
82 | if __name__ == "__main__": | ||
83 | parser = argparse.ArgumentParser( | ||
84 | description="This script automatically changes the workspace name in sway depending on your open applications." | ||
85 | ) | ||
86 | parser.add_argument( | ||
87 | "--duplicates", | ||
88 | "-d", | ||
89 | action="store_true", | ||
90 | help="Set it when you want an icon for each instance of the same application per workspace.", | ||
91 | ) | ||
92 | parser.add_argument( | ||
93 | "--logfile", | ||
94 | "-l", | ||
95 | type=str, | ||
96 | default="/tmp/sway-autoname-workspaces.log", | ||
97 | help="Path for the logfile.", | ||
98 | ) | ||
99 | args = parser.parse_args() | ||
100 | global ARGUMENTS | ||
101 | ARGUMENTS = args | ||
102 | |||
103 | logging.basicConfig( | ||
104 | level=logging.INFO, | ||
105 | filename=ARGUMENTS.logfile, | ||
106 | filemode="w", | ||
107 | format="%(message)s", | ||
108 | ) | ||
109 | |||
110 | ipc = i3ipc.Connection() | ||
111 | |||
112 | for sig in [signal.SIGINT, signal.SIGTERM]: | ||
113 | signal.signal(sig, lambda signal, frame: undo_window_renaming(ipc)) | ||
114 | |||
115 | def window_event_handler(ipc, e): | ||
116 | if e.change in ["new", "close", "move"]: | ||
117 | rename_workspaces(ipc) | ||
118 | |||
119 | ipc.on("window", window_event_handler) | ||
120 | |||
121 | rename_workspaces(ipc) | ||
122 | |||
123 | ipc.main() | ||
124 | |||
diff --git a/contrib/grimshot b/contrib/grimshot deleted file mode 100755 index 4ce31f29..00000000 --- a/contrib/grimshot +++ /dev/null | |||
@@ -1,154 +0,0 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | ## Grimshot: a helper for screenshots within sway | ||
4 | ## Requirements: | ||
5 | ## - `grim`: screenshot utility for wayland | ||
6 | ## - `slurp`: to select an area | ||
7 | ## - `swaymsg`: to read properties of current window | ||
8 | ## - `wl-copy`: clipboard utility | ||
9 | ## - `jq`: json utility to parse swaymsg output | ||
10 | ## - `notify-send`: to show notifications | ||
11 | ## Those are needed to be installed, if unsure, run `grimshot check` | ||
12 | ## | ||
13 | ## See `man 1 grimshot` or `grimshot usage` for further details. | ||
14 | |||
15 | getTargetDirectory() { | ||
16 | test -f ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs && \ | ||
17 | . ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs | ||
18 | |||
19 | echo ${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}} | ||
20 | } | ||
21 | |||
22 | if [ "$1" = "--notify" ]; then | ||
23 | NOTIFY=yes | ||
24 | shift 1 | ||
25 | else | ||
26 | NOTIFY=no | ||
27 | fi | ||
28 | |||
29 | ACTION=${1:-usage} | ||
30 | SUBJECT=${2:-screen} | ||
31 | FILE=${3:-$(getTargetDirectory)/$(date -Ins).png} | ||
32 | |||
33 | if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then | ||
34 | echo "Usage:" | ||
35 | echo " grimshot [--notify] (copy|save) [active|screen|output|area|window] [FILE|-]" | ||
36 | echo " grimshot check" | ||
37 | echo " grimshot usage" | ||
38 | echo "" | ||
39 | echo "Commands:" | ||
40 | echo " copy: Copy the screenshot data into the clipboard." | ||
41 | echo " save: Save the screenshot to a regular file or '-' to pipe to STDOUT." | ||
42 | echo " check: Verify if required tools are installed and exit." | ||
43 | echo " usage: Show this message and exit." | ||
44 | echo "" | ||
45 | echo "Targets:" | ||
46 | echo " active: Currently active window." | ||
47 | echo " screen: All visible outputs." | ||
48 | echo " output: Currently active output." | ||
49 | echo " area: Manually select a region." | ||
50 | echo " window: Manually select a window." | ||
51 | exit | ||
52 | fi | ||
53 | |||
54 | notify() { | ||
55 | notify-send -t 3000 -a grimshot "$@" | ||
56 | } | ||
57 | notifyOk() { | ||
58 | [ "$NOTIFY" = "no" ] && return | ||
59 | |||
60 | TITLE=${2:-"Screenshot"} | ||
61 | MESSAGE=${1:-"OK"} | ||
62 | notify "$TITLE" "$MESSAGE" | ||
63 | } | ||
64 | notifyError() { | ||
65 | if [ $NOTIFY = "yes" ]; then | ||
66 | TITLE=${2:-"Screenshot"} | ||
67 | MESSAGE=${1:-"Error taking screenshot with grim"} | ||
68 | notify -u critical "$TITLE" "$MESSAGE" | ||
69 | else | ||
70 | echo $1 | ||
71 | fi | ||
72 | } | ||
73 | |||
74 | die() { | ||
75 | MSG=${1:-Bye} | ||
76 | notifyError "Error: $MSG" | ||
77 | exit 2 | ||
78 | } | ||
79 | |||
80 | check() { | ||
81 | COMMAND=$1 | ||
82 | if command -v "$COMMAND" > /dev/null 2>&1; then | ||
83 | RESULT="OK" | ||
84 | else | ||
85 | RESULT="NOT FOUND" | ||
86 | fi | ||
87 | echo " $COMMAND: $RESULT" | ||
88 | } | ||
89 | |||
90 | takeScreenshot() { | ||
91 | FILE=$1 | ||
92 | GEOM=$2 | ||
93 | OUTPUT=$3 | ||
94 | if [ ! -z "$OUTPUT" ]; then | ||
95 | grim -o "$OUTPUT" "$FILE" || die "Unable to invoke grim" | ||
96 | elif [ -z "$GEOM" ]; then | ||
97 | grim "$FILE" || die "Unable to invoke grim" | ||
98 | else | ||
99 | grim -g "$GEOM" "$FILE" || die "Unable to invoke grim" | ||
100 | fi | ||
101 | } | ||
102 | |||
103 | if [ "$ACTION" = "check" ] ; then | ||
104 | echo "Checking if required tools are installed. If something is missing, install it to your system and make it available in PATH..." | ||
105 | check grim | ||
106 | check slurp | ||
107 | check swaymsg | ||
108 | check wl-copy | ||
109 | check jq | ||
110 | check notify-send | ||
111 | exit | ||
112 | elif [ "$SUBJECT" = "area" ] ; then | ||
113 | GEOM=$(slurp -d) | ||
114 | # Check if user exited slurp without selecting the area | ||
115 | if [ -z "$GEOM" ]; then | ||
116 | exit 1 | ||
117 | fi | ||
118 | WHAT="Area" | ||
119 | elif [ "$SUBJECT" = "active" ] ; then | ||
120 | FOCUSED=$(swaymsg -t get_tree | jq -r 'recurse(.nodes[]?, .floating_nodes[]?) | select(.focused)') | ||
121 | GEOM=$(echo "$FOCUSED" | jq -r '.rect | "\(.x),\(.y) \(.width)x\(.height)"') | ||
122 | APP_ID=$(echo "$FOCUSED" | jq -r '.app_id') | ||
123 | WHAT="$APP_ID window" | ||
124 | elif [ "$SUBJECT" = "screen" ] ; then | ||
125 | GEOM="" | ||
126 | WHAT="Screen" | ||
127 | elif [ "$SUBJECT" = "output" ] ; then | ||
128 | GEOM="" | ||
129 | OUTPUT=$(swaymsg -t get_outputs | jq -r '.[] | select(.focused)' | jq -r '.name') | ||
130 | WHAT="$OUTPUT" | ||
131 | elif [ "$SUBJECT" = "window" ] ; then | ||
132 | GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp) | ||
133 | # Check if user exited slurp without selecting the area | ||
134 | if [ -z "$GEOM" ]; then | ||
135 | exit 1 | ||
136 | fi | ||
137 | WHAT="Window" | ||
138 | else | ||
139 | die "Unknown subject to take a screen shot from" "$SUBJECT" | ||
140 | fi | ||
141 | |||
142 | if [ "$ACTION" = "copy" ] ; then | ||
143 | takeScreenshot - "$GEOM" "$OUTPUT" | wl-copy --type image/png || die "Clipboard error" | ||
144 | notifyOk "$WHAT copied to buffer" | ||
145 | else | ||
146 | if takeScreenshot "$FILE" "$GEOM" "$OUTPUT"; then | ||
147 | TITLE="Screenshot of $SUBJECT" | ||
148 | MESSAGE=$(basename "$FILE") | ||
149 | notifyOk "$MESSAGE" "$TITLE" | ||
150 | echo $FILE | ||
151 | else | ||
152 | notifyError "Error taking screenshot with grim" | ||
153 | fi | ||
154 | fi | ||
diff --git a/contrib/grimshot.1 b/contrib/grimshot.1 deleted file mode 100644 index e4baccfd..00000000 --- a/contrib/grimshot.1 +++ /dev/null | |||
@@ -1,104 +0,0 @@ | |||
1 | .\" Generated by scdoc 1.11.1 | ||
2 | .\" Complete documentation for this program is not available as a GNU info page | ||
3 | .ie \n(.g .ds Aq \(aq | ||
4 | .el .ds Aq ' | ||
5 | .nh | ||
6 | .ad l | ||
7 | .\" Begin generated content: | ||
8 | .TH "grimshot" "1" "2021-02-23" | ||
9 | .P | ||
10 | .SH NAME | ||
11 | .P | ||
12 | grimshot - a helper for screenshots within sway | ||
13 | .P | ||
14 | .SH SYNOPSIS | ||
15 | .P | ||
16 | \fBgrimshot\fR [--notify] (copy|save) [TARGET] [FILE] | ||
17 | .br | ||
18 | \fBgrimshot\fR check | ||
19 | .br | ||
20 | \fBgrimshot\fR usage | ||
21 | .P | ||
22 | .SH OPTIONS | ||
23 | .P | ||
24 | \fB--notify\fR | ||
25 | .RS 4 | ||
26 | Show notifications to the user that a screenshot has been taken.\& | ||
27 | .P | ||
28 | .RE | ||
29 | \fBsave\fR | ||
30 | .RS 4 | ||
31 | Save the screenshot into a regular file.\& Grimshot will write images | ||
32 | files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined | ||
33 | in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\& | ||
34 | Set FILE to '-' to pipe the output to STDOUT.\& | ||
35 | .P | ||
36 | .RE | ||
37 | \fBcopy\fR | ||
38 | .RS 4 | ||
39 | Copy the screenshot data (as image/png) into the clipboard.\& | ||
40 | .P | ||
41 | .RE | ||
42 | .SH DESCRIPTION | ||
43 | .P | ||
44 | Grimshot is an easy-to-use screenshot utility for sway.\& It provides a | ||
45 | convenient interface over grim, slurp and jq, and supports storing the | ||
46 | screenshot either directly to the clipboard using wl-copy or to a file.\& | ||
47 | .P | ||
48 | .SH EXAMPLES | ||
49 | .P | ||
50 | An example usage pattern is to add these bindings to your sway config: | ||
51 | .P | ||
52 | .nf | ||
53 | .RS 4 | ||
54 | # Screenshots: | ||
55 | # Super+P: Current window | ||
56 | # Super+Shift+p: Select area | ||
57 | # Super+Alt+p Current output | ||
58 | # Super+Ctrl+p Select a window | ||
59 | |||
60 | bindsym Mod4+p exec grimshot save active | ||
61 | bindsym Mod4+Shift+p exec grimshot save area | ||
62 | bindsym Mod4+Mod1+p exec grimshot save output | ||
63 | bindsym Mod4+Ctrl+p exec grimshot save window | ||
64 | .fi | ||
65 | .RE | ||
66 | .P | ||
67 | .SH TARGETS | ||
68 | .P | ||
69 | grimshot can capture the following named targets: | ||
70 | .P | ||
71 | \fIactive\fR | ||
72 | .RS 4 | ||
73 | Captures the currently active window.\& | ||
74 | .P | ||
75 | .RE | ||
76 | \fIscreen\fR | ||
77 | .RS 4 | ||
78 | Captures the entire screen.\& This includes all visible outputs.\& | ||
79 | .P | ||
80 | .RE | ||
81 | \fIarea\fR | ||
82 | .RS 4 | ||
83 | Allows manually selecting a rectangular region, and captures that.\& | ||
84 | .P | ||
85 | .RE | ||
86 | \fIwindow\fR | ||
87 | .RS 4 | ||
88 | Allows manually selecting a single window (by clicking on it), and | ||
89 | captures it.\& | ||
90 | .P | ||
91 | .RE | ||
92 | \fIoutput\fR | ||
93 | .RS 4 | ||
94 | Captures the currently active output.\& | ||
95 | .P | ||
96 | .RE | ||
97 | .SH OUTPUT | ||
98 | .P | ||
99 | Grimshot will print the filename of the captured screenshot to stdout if called | ||
100 | with the \fIsave\fR subcommand.\& | ||
101 | .P | ||
102 | .SH SEE ALSO | ||
103 | .P | ||
104 | \fBgrim\fR(1) | ||
diff --git a/contrib/grimshot.1.scd b/contrib/grimshot.1.scd deleted file mode 100644 index d2a57759..00000000 --- a/contrib/grimshot.1.scd +++ /dev/null | |||
@@ -1,77 +0,0 @@ | |||
1 | grimshot(1) | ||
2 | |||
3 | # NAME | ||
4 | |||
5 | grimshot - a helper for screenshots within sway | ||
6 | |||
7 | # SYNOPSIS | ||
8 | |||
9 | *grimshot* [--notify] (copy|save) [TARGET] [FILE]++ | ||
10 | *grimshot* check++ | ||
11 | *grimshot* usage | ||
12 | |||
13 | # OPTIONS | ||
14 | |||
15 | *--notify* | ||
16 | Show notifications to the user that a screenshot has been taken. | ||
17 | |||
18 | *save* | ||
19 | Save the screenshot into a regular file. Grimshot will write images | ||
20 | files to *XDG_SCREENSHOTS_DIR* if this is set (or defined | ||
21 | in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*. | ||
22 | Set FILE to '-' to pipe the output to STDOUT. | ||
23 | |||
24 | *copy* | ||
25 | Copy the screenshot data (as image/png) into the clipboard. | ||
26 | |||
27 | # DESCRIPTION | ||
28 | |||
29 | Grimshot is an easy-to-use screenshot utility for sway. It provides a | ||
30 | convenient interface over grim, slurp and jq, and supports storing the | ||
31 | screenshot either directly to the clipboard using wl-copy or to a file. | ||
32 | |||
33 | # EXAMPLES | ||
34 | |||
35 | An example usage pattern is to add these bindings to your sway config: | ||
36 | |||
37 | ``` | ||
38 | # Screenshots: | ||
39 | # Super+P: Current window | ||
40 | # Super+Shift+p: Select area | ||
41 | # Super+Alt+p Current output | ||
42 | # Super+Ctrl+p Select a window | ||
43 | |||
44 | bindsym Mod4+p exec grimshot save active | ||
45 | bindsym Mod4+Shift+p exec grimshot save area | ||
46 | bindsym Mod4+Mod1+p exec grimshot save output | ||
47 | bindsym Mod4+Ctrl+p exec grimshot save window | ||
48 | ``` | ||
49 | |||
50 | # TARGETS | ||
51 | |||
52 | grimshot can capture the following named targets: | ||
53 | |||
54 | _active_ | ||
55 | Captures the currently active window. | ||
56 | |||
57 | _screen_ | ||
58 | Captures the entire screen. This includes all visible outputs. | ||
59 | |||
60 | _area_ | ||
61 | Allows manually selecting a rectangular region, and captures that. | ||
62 | |||
63 | _window_ | ||
64 | Allows manually selecting a single window (by clicking on it), and | ||
65 | captures it. | ||
66 | |||
67 | _output_ | ||
68 | Captures the currently active output. | ||
69 | |||
70 | # OUTPUT | ||
71 | |||
72 | Grimshot will print the filename of the captured screenshot to stdout if called | ||
73 | with the _save_ subcommand. | ||
74 | |||
75 | # SEE ALSO | ||
76 | |||
77 | *grim*(1) | ||
diff --git a/contrib/inactive-windows-transparency.py b/contrib/inactive-windows-transparency.py deleted file mode 100755 index b81134dd..00000000 --- a/contrib/inactive-windows-transparency.py +++ /dev/null | |||
@@ -1,69 +0,0 @@ | |||
1 | #!/usr/bin/python | ||
2 | |||
3 | # This script requires i3ipc-python package (install it from a system package manager | ||
4 | # or pip). | ||
5 | # It makes inactive windows transparent. Use `transparency_val` variable to control | ||
6 | # transparency strength in range of 0…1 or use the command line argument -o. | ||
7 | |||
8 | import argparse | ||
9 | import i3ipc | ||
10 | import signal | ||
11 | import sys | ||
12 | from functools import partial | ||
13 | |||
14 | def on_window_focus(inactive_opacity, ipc, event): | ||
15 | global prev_focused | ||
16 | global prev_workspace | ||
17 | |||
18 | focused_workspace = ipc.get_tree().find_focused() | ||
19 | |||
20 | if focused_workspace == None: | ||
21 | return | ||
22 | |||
23 | focused = event.container | ||
24 | workspace = focused_workspace.workspace().num | ||
25 | |||
26 | if focused.id != prev_focused.id: # https://github.com/swaywm/sway/issues/2859 | ||
27 | focused.command("opacity 1") | ||
28 | if workspace == prev_workspace: | ||
29 | prev_focused.command("opacity " + inactive_opacity) | ||
30 | prev_focused = focused | ||
31 | prev_workspace = workspace | ||
32 | |||
33 | |||
34 | def remove_opacity(ipc): | ||
35 | for workspace in ipc.get_tree().workspaces(): | ||
36 | for w in workspace: | ||
37 | w.command("opacity 1") | ||
38 | ipc.main_quit() | ||
39 | sys.exit(0) | ||
40 | |||
41 | |||
42 | if __name__ == "__main__": | ||
43 | transparency_val = "0.80" | ||
44 | |||
45 | parser = argparse.ArgumentParser( | ||
46 | description="This script allows you to set the transparency of unfocused windows in sway." | ||
47 | ) | ||
48 | parser.add_argument( | ||
49 | "--opacity", | ||
50 | "-o", | ||
51 | type=str, | ||
52 | default=transparency_val, | ||
53 | help="set opacity value in range 0...1", | ||
54 | ) | ||
55 | args = parser.parse_args() | ||
56 | |||
57 | ipc = i3ipc.Connection() | ||
58 | prev_focused = None | ||
59 | prev_workspace = ipc.get_tree().find_focused().workspace().num | ||
60 | |||
61 | for window in ipc.get_tree(): | ||
62 | if window.focused: | ||
63 | prev_focused = window | ||
64 | else: | ||
65 | window.command("opacity " + args.opacity) | ||
66 | for sig in [signal.SIGINT, signal.SIGTERM]: | ||
67 | signal.signal(sig, lambda signal, frame: remove_opacity(ipc)) | ||
68 | ipc.on("window::focus", partial(on_window_focus, args.opacity)) | ||
69 | ipc.main() \ No newline at end of file | ||
diff --git a/include/background-image.h b/include/background-image.h deleted file mode 100644 index a97ef375..00000000 --- a/include/background-image.h +++ /dev/null | |||
@@ -1,20 +0,0 @@ | |||
1 | #ifndef _SWAY_BACKGROUND_IMAGE_H | ||
2 | #define _SWAY_BACKGROUND_IMAGE_H | ||
3 | #include "cairo_util.h" | ||
4 | |||
5 | enum background_mode { | ||
6 | BACKGROUND_MODE_STRETCH, | ||
7 | BACKGROUND_MODE_FILL, | ||
8 | BACKGROUND_MODE_FIT, | ||
9 | BACKGROUND_MODE_CENTER, | ||
10 | BACKGROUND_MODE_TILE, | ||
11 | BACKGROUND_MODE_SOLID_COLOR, | ||
12 | BACKGROUND_MODE_INVALID, | ||
13 | }; | ||
14 | |||
15 | enum background_mode parse_background_mode(const char *mode); | ||
16 | cairo_surface_t *load_background_image(const char *path); | ||
17 | void render_background_image(cairo_t *cairo, cairo_surface_t *image, | ||
18 | enum background_mode mode, int buffer_width, int buffer_height); | ||
19 | |||
20 | #endif | ||
diff --git a/include/gesture.h b/include/gesture.h new file mode 100644 index 00000000..9c6b0f91 --- /dev/null +++ b/include/gesture.h | |||
@@ -0,0 +1,104 @@ | |||
1 | #ifndef _SWAY_GESTURE_H | ||
2 | #define _SWAY_GESTURE_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include <stdint.h> | ||
6 | |||
7 | /** | ||
8 | * A gesture type used in binding. | ||
9 | */ | ||
10 | enum gesture_type { | ||
11 | GESTURE_TYPE_NONE = 0, | ||
12 | GESTURE_TYPE_HOLD, | ||
13 | GESTURE_TYPE_PINCH, | ||
14 | GESTURE_TYPE_SWIPE, | ||
15 | }; | ||
16 | |||
17 | // Turns single type enum value to constant string representation. | ||
18 | const char *gesture_type_string(enum gesture_type direction); | ||
19 | |||
20 | // Value to use to accept any finger count | ||
21 | extern const uint8_t GESTURE_FINGERS_ANY; | ||
22 | |||
23 | /** | ||
24 | * A gesture direction used in binding. | ||
25 | */ | ||
26 | enum gesture_direction { | ||
27 | GESTURE_DIRECTION_NONE = 0, | ||
28 | // Directions based on delta x and y | ||
29 | GESTURE_DIRECTION_UP = 1 << 0, | ||
30 | GESTURE_DIRECTION_DOWN = 1 << 1, | ||
31 | GESTURE_DIRECTION_LEFT = 1 << 2, | ||
32 | GESTURE_DIRECTION_RIGHT = 1 << 3, | ||
33 | // Directions based on scale | ||
34 | GESTURE_DIRECTION_INWARD = 1 << 4, | ||
35 | GESTURE_DIRECTION_OUTWARD = 1 << 5, | ||
36 | // Directions based on rotation | ||
37 | GESTURE_DIRECTION_CLOCKWISE = 1 << 6, | ||
38 | GESTURE_DIRECTION_COUNTERCLOCKWISE = 1 << 7, | ||
39 | }; | ||
40 | |||
41 | // Turns single direction enum value to constant string representation. | ||
42 | const char *gesture_direction_string(enum gesture_direction direction); | ||
43 | |||
44 | /** | ||
45 | * Struct representing a pointer gesture | ||
46 | */ | ||
47 | struct gesture { | ||
48 | enum gesture_type type; | ||
49 | uint8_t fingers; | ||
50 | uint32_t directions; | ||
51 | }; | ||
52 | |||
53 | /** | ||
54 | * Parses gesture from <gesture>[:<fingers>][:<directions>] string. | ||
55 | * | ||
56 | * Return NULL on success, otherwise error message string | ||
57 | */ | ||
58 | char *gesture_parse(const char *input, struct gesture *output); | ||
59 | |||
60 | // Turns gesture into string representation | ||
61 | char *gesture_to_string(struct gesture *gesture); | ||
62 | |||
63 | // Check if gesture is of certain type and finger count. | ||
64 | bool gesture_check(struct gesture *target, | ||
65 | enum gesture_type type, uint8_t fingers); | ||
66 | |||
67 | // Check if a gesture target/binding is match by other gesture/input | ||
68 | bool gesture_match(struct gesture *target, | ||
69 | struct gesture *to_match, bool exact); | ||
70 | |||
71 | // Returns true if gesture are exactly the same | ||
72 | bool gesture_equal(struct gesture *a, struct gesture *b); | ||
73 | |||
74 | // Compare distance between two matched target gestures. | ||
75 | int8_t gesture_compare(struct gesture *a, struct gesture *b); | ||
76 | |||
77 | // Small helper struct to track gestures over time | ||
78 | struct gesture_tracker { | ||
79 | enum gesture_type type; | ||
80 | uint8_t fingers; | ||
81 | double dx, dy; | ||
82 | double scale; | ||
83 | double rotation; | ||
84 | }; | ||
85 | |||
86 | // Begin gesture tracking | ||
87 | void gesture_tracker_begin(struct gesture_tracker *tracker, | ||
88 | enum gesture_type type, uint8_t fingers); | ||
89 | |||
90 | // Check if the provides type is currently being tracked | ||
91 | bool gesture_tracker_check(struct gesture_tracker *tracker, | ||
92 | enum gesture_type type); | ||
93 | |||
94 | // Update gesture track with new data point | ||
95 | void gesture_tracker_update(struct gesture_tracker *tracker, double dx, | ||
96 | double dy, double scale, double rotation); | ||
97 | |||
98 | // Reset tracker | ||
99 | void gesture_tracker_cancel(struct gesture_tracker *tracker); | ||
100 | |||
101 | // Reset tracker and return gesture tracked | ||
102 | struct gesture *gesture_tracker_end(struct gesture_tracker *tracker); | ||
103 | |||
104 | #endif | ||
diff --git a/include/ipc-client.h b/include/ipc-client.h index d3895023..9c5712d7 100644 --- a/include/ipc-client.h +++ b/include/ipc-client.h | |||
@@ -1,6 +1,9 @@ | |||
1 | #ifndef _SWAY_IPC_CLIENT_H | 1 | #ifndef _SWAY_IPC_CLIENT_H |
2 | #define _SWAY_IPC_CLIENT_H | 2 | #define _SWAY_IPC_CLIENT_H |
3 | 3 | ||
4 | // arbitrary number, it's probably sufficient, higher number = more memory usage | ||
5 | #define JSON_MAX_DEPTH 512 | ||
6 | |||
4 | #include <stdbool.h> | 7 | #include <stdbool.h> |
5 | #include <stdint.h> | 8 | #include <stdint.h> |
6 | #include <sys/time.h> | 9 | #include <sys/time.h> |
diff --git a/include/pango.h b/include/pango.h index 75dbba27..228e39cf 100644 --- a/include/pango.h +++ b/include/pango.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <stdint.h> | 5 | #include <stdint.h> |
6 | #include <cairo.h> | 6 | #include <cairo.h> |
7 | #include <pango/pangocairo.h> | 7 | #include <pango/pangocairo.h> |
8 | #include "stringop.h" | ||
8 | 9 | ||
9 | /** | 10 | /** |
10 | * Utility function which escape characters a & < > ' ". | 11 | * Utility function which escape characters a & < > ' ". |
@@ -13,11 +14,12 @@ | |||
13 | * escaped string to dest if provided. | 14 | * escaped string to dest if provided. |
14 | */ | 15 | */ |
15 | size_t escape_markup_text(const char *src, char *dest); | 16 | size_t escape_markup_text(const char *src, char *dest); |
16 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, | 17 | PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, |
17 | const char *text, double scale, bool markup); | 18 | const char *text, double scale, bool markup); |
18 | void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, | 19 | void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, |
19 | int *baseline, double scale, bool markup, const char *fmt, ...); | 20 | int *baseline, double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(8, 9); |
20 | void pango_printf(cairo_t *cairo, const char *font, | 21 | void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline); |
21 | double scale, bool markup, const char *fmt, ...); | 22 | void render_text(cairo_t *cairo, PangoFontDescription *desc, |
23 | double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(5, 6); | ||
22 | 24 | ||
23 | #endif | 25 | #endif |
diff --git a/include/stringop.h b/include/stringop.h index 8d7089e9..19a50f23 100644 --- a/include/stringop.h +++ b/include/stringop.h | |||
@@ -2,8 +2,15 @@ | |||
2 | #define _SWAY_STRINGOP_H | 2 | #define _SWAY_STRINGOP_H |
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <stddef.h> | ||
5 | #include "list.h" | 6 | #include "list.h" |
6 | 7 | ||
8 | #ifdef __GNUC__ | ||
9 | #define _SWAY_ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end))) | ||
10 | #else | ||
11 | #define _SWAY_ATTRIB_PRINTF(start, end) | ||
12 | #endif | ||
13 | |||
7 | void strip_whitespace(char *str); | 14 | void strip_whitespace(char *str); |
8 | void strip_quotes(char *str); | 15 | void strip_quotes(char *str); |
9 | 16 | ||
@@ -30,4 +37,7 @@ char *argsep(char **stringp, const char *delim, char *matched_delim); | |||
30 | // Expand a path using shell replacements such as $HOME and ~ | 37 | // Expand a path using shell replacements such as $HOME and ~ |
31 | bool expand_path(char **path); | 38 | bool expand_path(char **path); |
32 | 39 | ||
40 | char *vformat_str(const char *fmt, va_list args) _SWAY_ATTRIB_PRINTF(1, 0); | ||
41 | char *format_str(const char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); | ||
42 | |||
33 | #endif | 43 | #endif |
diff --git a/include/sway/commands.h b/include/sway/commands.h index 29a6bec3..27058587 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h | |||
@@ -3,13 +3,14 @@ | |||
3 | 3 | ||
4 | #include <wlr/util/edges.h> | 4 | #include <wlr/util/edges.h> |
5 | #include "config.h" | 5 | #include "config.h" |
6 | #include "stringop.h" | ||
6 | 7 | ||
7 | struct sway_container; | 8 | struct sway_container; |
8 | 9 | ||
9 | typedef struct cmd_results *sway_cmd(int argc, char **argv); | 10 | typedef struct cmd_results *sway_cmd(int argc, char **argv); |
10 | 11 | ||
11 | struct cmd_handler { | 12 | struct cmd_handler { |
12 | char *command; | 13 | const char *command; |
13 | sway_cmd *handle; | 14 | sway_cmd *handle; |
14 | }; | 15 | }; |
15 | 16 | ||
@@ -46,7 +47,7 @@ enum expected_args { | |||
46 | struct cmd_results *checkarg(int argc, const char *name, | 47 | struct cmd_results *checkarg(int argc, const char *name, |
47 | enum expected_args type, int val); | 48 | enum expected_args type, int val); |
48 | 49 | ||
49 | const struct cmd_handler *find_handler(char *line, | 50 | const struct cmd_handler *find_handler(const char *line, |
50 | const struct cmd_handler *cmd_handlers, size_t handlers_size); | 51 | const struct cmd_handler *cmd_handlers, size_t handlers_size); |
51 | 52 | ||
52 | /** | 53 | /** |
@@ -76,7 +77,7 @@ struct cmd_results *config_commands_command(char *exec); | |||
76 | /** | 77 | /** |
77 | * Allocates a cmd_results object. | 78 | * Allocates a cmd_results object. |
78 | */ | 79 | */ |
79 | struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...); | 80 | struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...) _SWAY_ATTRIB_PRINTF(2, 3); |
80 | /** | 81 | /** |
81 | * Frees a cmd_results object. | 82 | * Frees a cmd_results object. |
82 | */ | 83 | */ |
@@ -106,12 +107,14 @@ sway_cmd cmd_exec_process; | |||
106 | sway_cmd cmd_assign; | 107 | sway_cmd cmd_assign; |
107 | sway_cmd cmd_bar; | 108 | sway_cmd cmd_bar; |
108 | sway_cmd cmd_bindcode; | 109 | sway_cmd cmd_bindcode; |
110 | sway_cmd cmd_bindgesture; | ||
109 | sway_cmd cmd_bindswitch; | 111 | sway_cmd cmd_bindswitch; |
110 | sway_cmd cmd_bindsym; | 112 | sway_cmd cmd_bindsym; |
111 | sway_cmd cmd_border; | 113 | sway_cmd cmd_border; |
112 | sway_cmd cmd_client_noop; | 114 | sway_cmd cmd_client_noop; |
113 | sway_cmd cmd_client_focused; | 115 | sway_cmd cmd_client_focused; |
114 | sway_cmd cmd_client_focused_inactive; | 116 | sway_cmd cmd_client_focused_inactive; |
117 | sway_cmd cmd_client_focused_tab_title; | ||
115 | sway_cmd cmd_client_unfocused; | 118 | sway_cmd cmd_client_unfocused; |
116 | sway_cmd cmd_client_urgent; | 119 | sway_cmd cmd_client_urgent; |
117 | sway_cmd cmd_client_placeholder; | 120 | sway_cmd cmd_client_placeholder; |
@@ -157,12 +160,11 @@ sway_cmd cmd_new_float; | |||
157 | sway_cmd cmd_new_window; | 160 | sway_cmd cmd_new_window; |
158 | sway_cmd cmd_nop; | 161 | sway_cmd cmd_nop; |
159 | sway_cmd cmd_opacity; | 162 | sway_cmd cmd_opacity; |
160 | sway_cmd cmd_new_float; | ||
161 | sway_cmd cmd_new_window; | ||
162 | sway_cmd cmd_no_focus; | 163 | sway_cmd cmd_no_focus; |
163 | sway_cmd cmd_output; | 164 | sway_cmd cmd_output; |
164 | sway_cmd cmd_permit; | 165 | sway_cmd cmd_permit; |
165 | sway_cmd cmd_popup_during_fullscreen; | 166 | sway_cmd cmd_popup_during_fullscreen; |
167 | sway_cmd cmd_primary_selection; | ||
166 | sway_cmd cmd_reject; | 168 | sway_cmd cmd_reject; |
167 | sway_cmd cmd_reload; | 169 | sway_cmd cmd_reload; |
168 | sway_cmd cmd_rename; | 170 | sway_cmd cmd_rename; |
@@ -190,6 +192,7 @@ sway_cmd cmd_titlebar_border_thickness; | |||
190 | sway_cmd cmd_titlebar_padding; | 192 | sway_cmd cmd_titlebar_padding; |
191 | sway_cmd cmd_unbindcode; | 193 | sway_cmd cmd_unbindcode; |
192 | sway_cmd cmd_unbindswitch; | 194 | sway_cmd cmd_unbindswitch; |
195 | sway_cmd cmd_unbindgesture; | ||
193 | sway_cmd cmd_unbindsym; | 196 | sway_cmd cmd_unbindsym; |
194 | sway_cmd cmd_unmark; | 197 | sway_cmd cmd_unmark; |
195 | sway_cmd cmd_urgent; | 198 | sway_cmd cmd_urgent; |
@@ -249,6 +252,7 @@ sway_cmd input_cmd_click_method; | |||
249 | sway_cmd input_cmd_drag; | 252 | sway_cmd input_cmd_drag; |
250 | sway_cmd input_cmd_drag_lock; | 253 | sway_cmd input_cmd_drag_lock; |
251 | sway_cmd input_cmd_dwt; | 254 | sway_cmd input_cmd_dwt; |
255 | sway_cmd input_cmd_dwtp; | ||
252 | sway_cmd input_cmd_events; | 256 | sway_cmd input_cmd_events; |
253 | sway_cmd input_cmd_left_handed; | 257 | sway_cmd input_cmd_left_handed; |
254 | sway_cmd input_cmd_map_from_region; | 258 | sway_cmd input_cmd_map_from_region; |
@@ -257,10 +261,12 @@ sway_cmd input_cmd_map_to_region; | |||
257 | sway_cmd input_cmd_middle_emulation; | 261 | sway_cmd input_cmd_middle_emulation; |
258 | sway_cmd input_cmd_natural_scroll; | 262 | sway_cmd input_cmd_natural_scroll; |
259 | sway_cmd input_cmd_pointer_accel; | 263 | sway_cmd input_cmd_pointer_accel; |
264 | sway_cmd input_cmd_rotation_angle; | ||
260 | sway_cmd input_cmd_scroll_factor; | 265 | sway_cmd input_cmd_scroll_factor; |
261 | sway_cmd input_cmd_repeat_delay; | 266 | sway_cmd input_cmd_repeat_delay; |
262 | sway_cmd input_cmd_repeat_rate; | 267 | sway_cmd input_cmd_repeat_rate; |
263 | sway_cmd input_cmd_scroll_button; | 268 | sway_cmd input_cmd_scroll_button; |
269 | sway_cmd input_cmd_scroll_button_lock; | ||
264 | sway_cmd input_cmd_scroll_method; | 270 | sway_cmd input_cmd_scroll_method; |
265 | sway_cmd input_cmd_tap; | 271 | sway_cmd input_cmd_tap; |
266 | sway_cmd input_cmd_tap_button_map; | 272 | sway_cmd input_cmd_tap_button_map; |
@@ -282,12 +288,16 @@ sway_cmd output_cmd_dpms; | |||
282 | sway_cmd output_cmd_enable; | 288 | sway_cmd output_cmd_enable; |
283 | sway_cmd output_cmd_max_render_time; | 289 | sway_cmd output_cmd_max_render_time; |
284 | sway_cmd output_cmd_mode; | 290 | sway_cmd output_cmd_mode; |
291 | sway_cmd output_cmd_modeline; | ||
285 | sway_cmd output_cmd_position; | 292 | sway_cmd output_cmd_position; |
293 | sway_cmd output_cmd_power; | ||
294 | sway_cmd output_cmd_render_bit_depth; | ||
286 | sway_cmd output_cmd_scale; | 295 | sway_cmd output_cmd_scale; |
287 | sway_cmd output_cmd_scale_filter; | 296 | sway_cmd output_cmd_scale_filter; |
288 | sway_cmd output_cmd_subpixel; | 297 | sway_cmd output_cmd_subpixel; |
289 | sway_cmd output_cmd_toggle; | 298 | sway_cmd output_cmd_toggle; |
290 | sway_cmd output_cmd_transform; | 299 | sway_cmd output_cmd_transform; |
300 | sway_cmd output_cmd_unplug; | ||
291 | 301 | ||
292 | sway_cmd seat_cmd_attach; | 302 | sway_cmd seat_cmd_attach; |
293 | sway_cmd seat_cmd_cursor; | 303 | sway_cmd seat_cmd_cursor; |
diff --git a/include/sway/config.h b/include/sway/config.h index b8327e9c..0be1cd22 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -8,13 +8,17 @@ | |||
8 | #include <wlr/types/wlr_tablet_tool.h> | 8 | #include <wlr/types/wlr_tablet_tool.h> |
9 | #include <wlr/util/box.h> | 9 | #include <wlr/util/box.h> |
10 | #include <xkbcommon/xkbcommon.h> | 10 | #include <xkbcommon/xkbcommon.h> |
11 | #include <xf86drmMode.h> | ||
11 | #include "../include/config.h" | 12 | #include "../include/config.h" |
13 | #include "gesture.h" | ||
12 | #include "list.h" | 14 | #include "list.h" |
15 | #include "stringop.h" | ||
13 | #include "swaynag.h" | 16 | #include "swaynag.h" |
14 | #include "tree/container.h" | 17 | #include "tree/container.h" |
15 | #include "sway/input/tablet.h" | 18 | #include "sway/input/tablet.h" |
16 | #include "sway/tree/root.h" | 19 | #include "sway/tree/root.h" |
17 | #include "wlr-layer-shell-unstable-v1-protocol.h" | 20 | #include "wlr-layer-shell-unstable-v1-protocol.h" |
21 | #include <pango/pangocairo.h> | ||
18 | 22 | ||
19 | // TODO: Refactor this shit | 23 | // TODO: Refactor this shit |
20 | 24 | ||
@@ -31,7 +35,8 @@ enum binding_input_type { | |||
31 | BINDING_KEYSYM, | 35 | BINDING_KEYSYM, |
32 | BINDING_MOUSECODE, | 36 | BINDING_MOUSECODE, |
33 | BINDING_MOUSESYM, | 37 | BINDING_MOUSESYM, |
34 | BINDING_SWITCH | 38 | BINDING_SWITCH, // dummy, only used to call seat_execute_command |
39 | BINDING_GESTURE // dummy, only used to call seat_execute_command | ||
35 | }; | 40 | }; |
36 | 41 | ||
37 | enum binding_flags { | 42 | enum binding_flags { |
@@ -44,10 +49,11 @@ enum binding_flags { | |||
44 | BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload | 49 | BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload |
45 | BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor | 50 | BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor |
46 | BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key | 51 | BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key |
52 | BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match | ||
47 | }; | 53 | }; |
48 | 54 | ||
49 | /** | 55 | /** |
50 | * A key binding and an associated command. | 56 | * A key (or mouse) binding and an associated command. |
51 | */ | 57 | */ |
52 | struct sway_binding { | 58 | struct sway_binding { |
53 | enum binding_input_type type; | 59 | enum binding_input_type type; |
@@ -61,12 +67,10 @@ struct sway_binding { | |||
61 | char *command; | 67 | char *command; |
62 | }; | 68 | }; |
63 | 69 | ||
64 | /** | 70 | enum sway_switch_trigger { |
65 | * A mouse binding and an associated command. | 71 | SWAY_SWITCH_TRIGGER_OFF, |
66 | */ | 72 | SWAY_SWITCH_TRIGGER_ON, |
67 | struct sway_mouse_binding { | 73 | SWAY_SWITCH_TRIGGER_TOGGLE, |
68 | uint32_t button; | ||
69 | char *command; | ||
70 | }; | 74 | }; |
71 | 75 | ||
72 | /** | 76 | /** |
@@ -74,8 +78,18 @@ struct sway_mouse_binding { | |||
74 | */ | 78 | */ |
75 | struct sway_switch_binding { | 79 | struct sway_switch_binding { |
76 | enum wlr_switch_type type; | 80 | enum wlr_switch_type type; |
77 | enum wlr_switch_state state; | 81 | enum sway_switch_trigger trigger; |
82 | uint32_t flags; | ||
83 | char *command; | ||
84 | }; | ||
85 | |||
86 | /** | ||
87 | * A gesture binding and an associated command. | ||
88 | */ | ||
89 | struct sway_gesture_binding { | ||
90 | char *input; | ||
78 | uint32_t flags; | 91 | uint32_t flags; |
92 | struct gesture gesture; | ||
79 | char *command; | 93 | char *command; |
80 | }; | 94 | }; |
81 | 95 | ||
@@ -98,6 +112,7 @@ struct sway_mode { | |||
98 | list_t *keycode_bindings; | 112 | list_t *keycode_bindings; |
99 | list_t *mouse_bindings; | 113 | list_t *mouse_bindings; |
100 | list_t *switch_bindings; | 114 | list_t *switch_bindings; |
115 | list_t *gesture_bindings; | ||
101 | bool pango; | 116 | bool pango; |
102 | }; | 117 | }; |
103 | 118 | ||
@@ -136,14 +151,17 @@ struct input_config { | |||
136 | int drag; | 151 | int drag; |
137 | int drag_lock; | 152 | int drag_lock; |
138 | int dwt; | 153 | int dwt; |
154 | int dwtp; | ||
139 | int left_handed; | 155 | int left_handed; |
140 | int middle_emulation; | 156 | int middle_emulation; |
141 | int natural_scroll; | 157 | int natural_scroll; |
142 | float pointer_accel; | 158 | float pointer_accel; |
159 | float rotation_angle; | ||
143 | float scroll_factor; | 160 | float scroll_factor; |
144 | int repeat_delay; | 161 | int repeat_delay; |
145 | int repeat_rate; | 162 | int repeat_rate; |
146 | int scroll_button; | 163 | int scroll_button; |
164 | int scroll_button_lock; | ||
147 | int scroll_method; | 165 | int scroll_method; |
148 | int send_events; | 166 | int send_events; |
149 | int tap; | 167 | int tap; |
@@ -233,12 +251,6 @@ struct seat_config { | |||
233 | } xcursor_theme; | 251 | } xcursor_theme; |
234 | }; | 252 | }; |
235 | 253 | ||
236 | enum config_dpms { | ||
237 | DPMS_IGNORE, | ||
238 | DPMS_ON, | ||
239 | DPMS_OFF, | ||
240 | }; | ||
241 | |||
242 | enum scale_filter_mode { | 254 | enum scale_filter_mode { |
243 | SCALE_FILTER_DEFAULT, // the default is currently smart | 255 | SCALE_FILTER_DEFAULT, // the default is currently smart |
244 | SCALE_FILTER_LINEAR, | 256 | SCALE_FILTER_LINEAR, |
@@ -246,6 +258,12 @@ enum scale_filter_mode { | |||
246 | SCALE_FILTER_SMART, | 258 | SCALE_FILTER_SMART, |
247 | }; | 259 | }; |
248 | 260 | ||
261 | enum render_bit_depth { | ||
262 | RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8 | ||
263 | RENDER_BIT_DEPTH_8, | ||
264 | RENDER_BIT_DEPTH_10, | ||
265 | }; | ||
266 | |||
249 | /** | 267 | /** |
250 | * Size and position configuration for a particular output. | 268 | * Size and position configuration for a particular output. |
251 | * | 269 | * |
@@ -254,9 +272,11 @@ enum scale_filter_mode { | |||
254 | struct output_config { | 272 | struct output_config { |
255 | char *name; | 273 | char *name; |
256 | int enabled; | 274 | int enabled; |
275 | int power; | ||
257 | int width, height; | 276 | int width, height; |
258 | float refresh_rate; | 277 | float refresh_rate; |
259 | int custom_mode; | 278 | int custom_mode; |
279 | drmModeModeInfo drm_mode; | ||
260 | int x, y; | 280 | int x, y; |
261 | float scale; | 281 | float scale; |
262 | enum scale_filter_mode scale_filter; | 282 | enum scale_filter_mode scale_filter; |
@@ -264,11 +284,19 @@ struct output_config { | |||
264 | enum wl_output_subpixel subpixel; | 284 | enum wl_output_subpixel subpixel; |
265 | int max_render_time; // In milliseconds | 285 | int max_render_time; // In milliseconds |
266 | int adaptive_sync; | 286 | int adaptive_sync; |
287 | enum render_bit_depth render_bit_depth; | ||
267 | 288 | ||
268 | char *background; | 289 | char *background; |
269 | char *background_option; | 290 | char *background_option; |
270 | char *background_fallback; | 291 | char *background_fallback; |
271 | enum config_dpms dpms_state; | 292 | }; |
293 | |||
294 | /** | ||
295 | * An output config pre-matched to an output | ||
296 | */ | ||
297 | struct matched_output_config { | ||
298 | struct sway_output *output; | ||
299 | struct output_config *config; | ||
272 | }; | 300 | }; |
273 | 301 | ||
274 | /** | 302 | /** |
@@ -281,6 +309,12 @@ struct side_gaps { | |||
281 | int left; | 309 | int left; |
282 | }; | 310 | }; |
283 | 311 | ||
312 | enum smart_gaps_mode { | ||
313 | SMART_GAPS_OFF, | ||
314 | SMART_GAPS_ON, | ||
315 | SMART_GAPS_INVERSE_OUTER, | ||
316 | }; | ||
317 | |||
284 | /** | 318 | /** |
285 | * Stores configuration for a workspace, regardless of whether the workspace | 319 | * Stores configuration for a workspace, regardless of whether the workspace |
286 | * exists. | 320 | * exists. |
@@ -416,14 +450,6 @@ enum sway_popup_during_fullscreen { | |||
416 | POPUP_LEAVE, | 450 | POPUP_LEAVE, |
417 | }; | 451 | }; |
418 | 452 | ||
419 | enum command_context { | ||
420 | CONTEXT_CONFIG = 1 << 0, | ||
421 | CONTEXT_BINDING = 1 << 1, | ||
422 | CONTEXT_IPC = 1 << 2, | ||
423 | CONTEXT_CRITERIA = 1 << 3, | ||
424 | CONTEXT_ALL = 0xFFFFFFFF, | ||
425 | }; | ||
426 | |||
427 | enum focus_follows_mouse_mode { | 453 | enum focus_follows_mouse_mode { |
428 | FOLLOWS_NO, | 454 | FOLLOWS_NO, |
429 | FOLLOWS_YES, | 455 | FOLLOWS_YES, |
@@ -485,9 +511,10 @@ struct sway_config { | |||
485 | char *floating_scroll_right_cmd; | 511 | char *floating_scroll_right_cmd; |
486 | enum sway_container_layout default_orientation; | 512 | enum sway_container_layout default_orientation; |
487 | enum sway_container_layout default_layout; | 513 | enum sway_container_layout default_layout; |
488 | char *font; | 514 | char *font; // Used for IPC. |
489 | size_t font_height; | 515 | PangoFontDescription *font_description; // Used internally for rendering and validating. |
490 | size_t font_baseline; | 516 | int font_height; |
517 | int font_baseline; | ||
491 | bool pango_markup; | 518 | bool pango_markup; |
492 | int titlebar_border_thickness; | 519 | int titlebar_border_thickness; |
493 | int titlebar_h_padding; | 520 | int titlebar_h_padding; |
@@ -514,11 +541,12 @@ struct sway_config { | |||
514 | bool auto_back_and_forth; | 541 | bool auto_back_and_forth; |
515 | bool show_marks; | 542 | bool show_marks; |
516 | enum alignment title_align; | 543 | enum alignment title_align; |
544 | bool primary_selection; | ||
517 | 545 | ||
518 | bool tiling_drag; | 546 | bool tiling_drag; |
519 | int tiling_drag_threshold; | 547 | int tiling_drag_threshold; |
520 | 548 | ||
521 | bool smart_gaps; | 549 | enum smart_gaps_mode smart_gaps; |
522 | int gaps_inner; | 550 | int gaps_inner; |
523 | struct side_gaps gaps_outer; | 551 | struct side_gaps gaps_outer; |
524 | 552 | ||
@@ -541,12 +569,15 @@ struct sway_config { | |||
541 | struct { | 569 | struct { |
542 | struct border_colors focused; | 570 | struct border_colors focused; |
543 | struct border_colors focused_inactive; | 571 | struct border_colors focused_inactive; |
572 | struct border_colors focused_tab_title; | ||
544 | struct border_colors unfocused; | 573 | struct border_colors unfocused; |
545 | struct border_colors urgent; | 574 | struct border_colors urgent; |
546 | struct border_colors placeholder; | 575 | struct border_colors placeholder; |
547 | float background[4]; | 576 | float background[4]; |
548 | } border_colors; | 577 | } border_colors; |
549 | 578 | ||
579 | bool has_focused_tab_title; | ||
580 | |||
550 | // floating view | 581 | // floating view |
551 | int32_t floating_maximum_width; | 582 | int32_t floating_maximum_width; |
552 | int32_t floating_maximum_height; | 583 | int32_t floating_maximum_height; |
@@ -604,7 +635,7 @@ void run_deferred_bindings(void); | |||
604 | /** | 635 | /** |
605 | * Adds a warning entry to the swaynag instance used for errors. | 636 | * Adds a warning entry to the swaynag instance used for errors. |
606 | */ | 637 | */ |
607 | void config_add_swaynag_warning(char *fmt, ...); | 638 | void config_add_swaynag_warning(char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); |
608 | 639 | ||
609 | /** | 640 | /** |
610 | * Free config struct | 641 | * Free config struct |
@@ -657,20 +688,22 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt | |||
657 | 688 | ||
658 | struct output_config *new_output_config(const char *name); | 689 | struct output_config *new_output_config(const char *name); |
659 | 690 | ||
660 | void merge_output_config(struct output_config *dst, struct output_config *src); | 691 | bool apply_output_configs(struct matched_output_config *configs, |
661 | 692 | size_t configs_len, bool test_only); | |
662 | bool apply_output_config(struct output_config *oc, struct sway_output *output); | ||
663 | 693 | ||
664 | bool test_output_config(struct output_config *oc, struct sway_output *output); | 694 | void apply_all_output_configs(void); |
665 | 695 | ||
666 | struct output_config *store_output_config(struct output_config *oc); | 696 | /** |
697 | * store_output_config stores a new output config. An output may be matched by | ||
698 | * three different config types, in order of precedence: Identifier, name and | ||
699 | * wildcard. When storing a config type of lower precedence, assume that the | ||
700 | * user wants the config to take immediate effect by superseding (clearing) the | ||
701 | * same values from higher presedence configuration. | ||
702 | */ | ||
703 | void store_output_config(struct output_config *oc); | ||
667 | 704 | ||
668 | struct output_config *find_output_config(struct sway_output *output); | 705 | struct output_config *find_output_config(struct sway_output *output); |
669 | 706 | ||
670 | void apply_output_config_to_outputs(struct output_config *oc); | ||
671 | |||
672 | void reset_outputs(void); | ||
673 | |||
674 | void free_output_config(struct output_config *oc); | 707 | void free_output_config(struct output_config *oc); |
675 | 708 | ||
676 | bool spawn_swaybg(void); | 709 | bool spawn_swaybg(void); |
@@ -681,6 +714,8 @@ void free_sway_binding(struct sway_binding *sb); | |||
681 | 714 | ||
682 | void free_switch_binding(struct sway_switch_binding *binding); | 715 | void free_switch_binding(struct sway_switch_binding *binding); |
683 | 716 | ||
717 | void free_gesture_binding(struct sway_gesture_binding *binding); | ||
718 | |||
684 | void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); | 719 | void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); |
685 | 720 | ||
686 | void load_swaybar(struct bar_config *bar); | 721 | void load_swaybar(struct bar_config *bar); |
@@ -696,14 +731,13 @@ void free_bar_binding(struct bar_binding *binding); | |||
696 | void free_workspace_config(struct workspace_config *wsc); | 731 | void free_workspace_config(struct workspace_config *wsc); |
697 | 732 | ||
698 | /** | 733 | /** |
699 | * Updates the value of config->font_height based on the max title height | 734 | * Updates the value of config->font_height based on the metrics for title's |
700 | * reported by each container. If recalculate is true, the containers will | 735 | * font as reported by pango. |
701 | * recalculate their heights before reporting. | ||
702 | * | 736 | * |
703 | * If the height has changed, all containers will be rearranged to take on the | 737 | * If the height has changed, all containers will be rearranged to take on the |
704 | * new size. | 738 | * new size. |
705 | */ | 739 | */ |
706 | void config_update_font_height(bool recalculate); | 740 | void config_update_font_height(void); |
707 | 741 | ||
708 | /** | 742 | /** |
709 | * Convert bindsym into bindcode using the first configured layout. | 743 | * Convert bindsym into bindcode using the first configured layout. |
diff --git a/include/sway/criteria.h b/include/sway/criteria.h index ad8610cd..8da345ea 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h | |||
@@ -1,7 +1,8 @@ | |||
1 | #ifndef _SWAY_CRITERIA_H | 1 | #ifndef _SWAY_CRITERIA_H |
2 | #define _SWAY_CRITERIA_H | 2 | #define _SWAY_CRITERIA_H |
3 | 3 | ||
4 | #include <pcre.h> | 4 | #define PCRE2_CODE_UNIT_WIDTH 8 |
5 | #include <pcre2.h> | ||
5 | #include "config.h" | 6 | #include "config.h" |
6 | #include "list.h" | 7 | #include "list.h" |
7 | #include "tree/view.h" | 8 | #include "tree/view.h" |
@@ -15,13 +16,13 @@ enum criteria_type { | |||
15 | }; | 16 | }; |
16 | 17 | ||
17 | enum pattern_type { | 18 | enum pattern_type { |
18 | PATTERN_PCRE, | 19 | PATTERN_PCRE2, |
19 | PATTERN_FOCUSED, | 20 | PATTERN_FOCUSED, |
20 | }; | 21 | }; |
21 | 22 | ||
22 | struct pattern { | 23 | struct pattern { |
23 | enum pattern_type match_type; | 24 | enum pattern_type match_type; |
24 | pcre *regex; | 25 | pcre2_code *regex; |
25 | }; | 26 | }; |
26 | 27 | ||
27 | struct criteria { | 28 | struct criteria { |
@@ -42,6 +43,7 @@ struct criteria { | |||
42 | struct pattern *window_role; | 43 | struct pattern *window_role; |
43 | enum atom_name window_type; | 44 | enum atom_name window_type; |
44 | #endif | 45 | #endif |
46 | bool all; | ||
45 | bool floating; | 47 | bool floating; |
46 | bool tiling; | 48 | bool tiling; |
47 | char urgent; // 'l' for latest or 'o' for oldest | 49 | char urgent; // 'l' for latest or 'o' for oldest |
diff --git a/include/sway/desktop.h b/include/sway/desktop.h deleted file mode 100644 index c969a76b..00000000 --- a/include/sway/desktop.h +++ /dev/null | |||
@@ -1,13 +0,0 @@ | |||
1 | #include <wlr/types/wlr_surface.h> | ||
2 | |||
3 | struct sway_container; | ||
4 | struct sway_view; | ||
5 | |||
6 | void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, | ||
7 | bool whole); | ||
8 | |||
9 | void desktop_damage_whole_container(struct sway_container *con); | ||
10 | |||
11 | void desktop_damage_box(struct wlr_box *box); | ||
12 | |||
13 | void desktop_damage_view(struct sway_view *view); | ||
diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index 58d54c68..84cc666d 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h | |||
@@ -1,8 +1,6 @@ | |||
1 | #ifndef _SWAY_DESKTOP_IDLE_INHIBIT_V1_H | 1 | #ifndef _SWAY_DESKTOP_IDLE_INHIBIT_V1_H |
2 | #define _SWAY_DESKTOP_IDLE_INHIBIT_V1_H | 2 | #define _SWAY_DESKTOP_IDLE_INHIBIT_V1_H |
3 | #include <wlr/types/wlr_idle_inhibit_v1.h> | 3 | #include <wlr/types/wlr_idle_inhibit_v1.h> |
4 | #include <wlr/types/wlr_idle.h> | ||
5 | #include "sway/server.h" | ||
6 | 4 | ||
7 | enum sway_idle_inhibit_mode { | 5 | enum sway_idle_inhibit_mode { |
8 | INHIBIT_IDLE_APPLICATION, // Application set inhibitor (when visible) | 6 | INHIBIT_IDLE_APPLICATION, // Application set inhibitor (when visible) |
@@ -16,12 +14,9 @@ struct sway_idle_inhibit_manager_v1 { | |||
16 | struct wlr_idle_inhibit_manager_v1 *wlr_manager; | 14 | struct wlr_idle_inhibit_manager_v1 *wlr_manager; |
17 | struct wl_listener new_idle_inhibitor_v1; | 15 | struct wl_listener new_idle_inhibitor_v1; |
18 | struct wl_list inhibitors; | 16 | struct wl_list inhibitors; |
19 | |||
20 | struct wlr_idle *idle; | ||
21 | }; | 17 | }; |
22 | 18 | ||
23 | struct sway_idle_inhibitor_v1 { | 19 | struct sway_idle_inhibitor_v1 { |
24 | struct sway_idle_inhibit_manager_v1 *manager; | ||
25 | struct wlr_idle_inhibitor_v1 *wlr_inhibitor; | 20 | struct wlr_idle_inhibitor_v1 *wlr_inhibitor; |
26 | struct sway_view *view; | 21 | struct sway_view *view; |
27 | enum sway_idle_inhibit_mode mode; | 22 | enum sway_idle_inhibit_mode mode; |
@@ -33,8 +28,7 @@ struct sway_idle_inhibitor_v1 { | |||
33 | bool sway_idle_inhibit_v1_is_active( | 28 | bool sway_idle_inhibit_v1_is_active( |
34 | struct sway_idle_inhibitor_v1 *inhibitor); | 29 | struct sway_idle_inhibitor_v1 *inhibitor); |
35 | 30 | ||
36 | void sway_idle_inhibit_v1_check_active( | 31 | void sway_idle_inhibit_v1_check_active(void); |
37 | struct sway_idle_inhibit_manager_v1 *manager); | ||
38 | 32 | ||
39 | void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, | 33 | void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, |
40 | enum sway_idle_inhibit_mode mode); | 34 | enum sway_idle_inhibit_mode mode); |
@@ -48,6 +42,6 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_vi | |||
48 | void sway_idle_inhibit_v1_user_inhibitor_destroy( | 42 | void sway_idle_inhibit_v1_user_inhibitor_destroy( |
49 | struct sway_idle_inhibitor_v1 *inhibitor); | 43 | struct sway_idle_inhibitor_v1 *inhibitor); |
50 | 44 | ||
51 | struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( | 45 | bool sway_idle_inhibit_manager_v1_init(void); |
52 | struct wl_display *wl_display, struct wlr_idle *idle); | 46 | |
53 | #endif | 47 | #endif |
diff --git a/include/sway/desktop/launcher.h b/include/sway/desktop/launcher.h new file mode 100644 index 00000000..412068a9 --- /dev/null +++ b/include/sway/desktop/launcher.h | |||
@@ -0,0 +1,40 @@ | |||
1 | #ifndef _SWAY_LAUNCHER_H | ||
2 | #define _SWAY_LAUNCHER_H | ||
3 | |||
4 | #include <stdlib.h> | ||
5 | #include <wayland-server-core.h> | ||
6 | #include "sway/input/seat.h" | ||
7 | |||
8 | struct launcher_ctx { | ||
9 | pid_t pid; | ||
10 | char *fallback_name; | ||
11 | struct wlr_xdg_activation_token_v1 *token; | ||
12 | struct wl_listener token_destroy; | ||
13 | struct sway_seat *seat; | ||
14 | struct wl_listener seat_destroy; | ||
15 | |||
16 | bool activated; | ||
17 | bool had_focused_surface; | ||
18 | |||
19 | struct sway_node *node; | ||
20 | struct wl_listener node_destroy; | ||
21 | |||
22 | struct wl_list link; // sway_server::pending_launcher_ctxs | ||
23 | }; | ||
24 | |||
25 | struct launcher_ctx *launcher_ctx_find_pid(pid_t pid); | ||
26 | |||
27 | struct sway_workspace *launcher_ctx_get_workspace(struct launcher_ctx *ctx); | ||
28 | |||
29 | void launcher_ctx_consume(struct launcher_ctx *ctx); | ||
30 | |||
31 | void launcher_ctx_destroy(struct launcher_ctx *ctx); | ||
32 | |||
33 | struct launcher_ctx *launcher_ctx_create_internal(void); | ||
34 | |||
35 | struct launcher_ctx *launcher_ctx_create( | ||
36 | struct wlr_xdg_activation_token_v1 *token, struct sway_node *node); | ||
37 | |||
38 | const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx); | ||
39 | |||
40 | #endif | ||
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 7dd58ba8..dd7edb7a 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h | |||
@@ -1,6 +1,8 @@ | |||
1 | #ifndef _SWAY_TRANSACTION_H | 1 | #ifndef _SWAY_TRANSACTION_H |
2 | #define _SWAY_TRANSACTION_H | 2 | #define _SWAY_TRANSACTION_H |
3 | #include <stdint.h> | 3 | #include <stdint.h> |
4 | #include <stdbool.h> | ||
5 | #include <wlr/types/wlr_scene.h> | ||
4 | 6 | ||
5 | /** | 7 | /** |
6 | * Transactions enable us to perform atomic layout updates. | 8 | * Transactions enable us to perform atomic layout updates. |
@@ -38,8 +40,11 @@ void transaction_commit_dirty_client(void); | |||
38 | * Notify the transaction system that a view is ready for the new layout. | 40 | * Notify the transaction system that a view is ready for the new layout. |
39 | * | 41 | * |
40 | * When all views in the transaction are ready, the layout will be applied. | 42 | * When all views in the transaction are ready, the layout will be applied. |
43 | * | ||
44 | * A success boolean is returned denoting that this part of the transaction is | ||
45 | * ready. | ||
41 | */ | 46 | */ |
42 | void transaction_notify_view_ready_by_serial(struct sway_view *view, | 47 | bool transaction_notify_view_ready_by_serial(struct sway_view *view, |
43 | uint32_t serial); | 48 | uint32_t serial); |
44 | 49 | ||
45 | /** | 50 | /** |
@@ -47,8 +52,13 @@ void transaction_notify_view_ready_by_serial(struct sway_view *view, | |||
47 | * identifying the instruction by geometry rather than by serial. | 52 | * identifying the instruction by geometry rather than by serial. |
48 | * | 53 | * |
49 | * This is used by xwayland views, as they don't have serials. | 54 | * This is used by xwayland views, as they don't have serials. |
55 | * | ||
56 | * A success boolean is returned denoting that this part of the transaction is | ||
57 | * ready. | ||
50 | */ | 58 | */ |
51 | void transaction_notify_view_ready_by_geometry(struct sway_view *view, | 59 | bool transaction_notify_view_ready_by_geometry(struct sway_view *view, |
52 | double x, double y, int width, int height); | 60 | double x, double y, int width, int height); |
53 | 61 | ||
62 | void arrange_popups(struct wlr_scene_tree *popups); | ||
63 | |||
54 | #endif | 64 | #endif |
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 7d66e699..527d0350 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h | |||
@@ -4,7 +4,7 @@ | |||
4 | #include <stdint.h> | 4 | #include <stdint.h> |
5 | #include <wlr/types/wlr_pointer_constraints_v1.h> | 5 | #include <wlr/types/wlr_pointer_constraints_v1.h> |
6 | #include <wlr/types/wlr_pointer_gestures_v1.h> | 6 | #include <wlr/types/wlr_pointer_gestures_v1.h> |
7 | #include <wlr/types/wlr_surface.h> | 7 | #include <wlr/types/wlr_compositor.h> |
8 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
9 | #include "config.h" | 9 | #include "config.h" |
10 | 10 | ||
@@ -35,7 +35,8 @@ struct sway_cursor { | |||
35 | pixman_region32_t confine; // invalid if active_constraint == NULL | 35 | pixman_region32_t confine; // invalid if active_constraint == NULL |
36 | bool active_confine_requires_warp; | 36 | bool active_confine_requires_warp; |
37 | 37 | ||
38 | struct wlr_pointer_gestures_v1 *pointer_gestures; | 38 | struct wl_listener hold_begin; |
39 | struct wl_listener hold_end; | ||
39 | struct wl_listener pinch_begin; | 40 | struct wl_listener pinch_begin; |
40 | struct wl_listener pinch_update; | 41 | struct wl_listener pinch_update; |
41 | struct wl_listener pinch_end; | 42 | struct wl_listener pinch_end; |
@@ -51,6 +52,7 @@ struct sway_cursor { | |||
51 | 52 | ||
52 | struct wl_listener touch_down; | 53 | struct wl_listener touch_down; |
53 | struct wl_listener touch_up; | 54 | struct wl_listener touch_up; |
55 | struct wl_listener touch_cancel; | ||
54 | struct wl_listener touch_motion; | 56 | struct wl_listener touch_motion; |
55 | struct wl_listener touch_frame; | 57 | struct wl_listener touch_frame; |
56 | bool simulating_pointer_from_touch; | 58 | bool simulating_pointer_from_touch; |
@@ -62,6 +64,7 @@ struct sway_cursor { | |||
62 | struct wl_listener tool_proximity; | 64 | struct wl_listener tool_proximity; |
63 | struct wl_listener tool_button; | 65 | struct wl_listener tool_button; |
64 | bool simulating_pointer_from_tool_tip; | 66 | bool simulating_pointer_from_tool_tip; |
67 | bool simulating_pointer_from_tool_button; | ||
65 | uint32_t tool_buttons; | 68 | uint32_t tool_buttons; |
66 | 69 | ||
67 | struct wl_listener request_set_cursor; | 70 | struct wl_listener request_set_cursor; |
@@ -105,12 +108,16 @@ void cursor_unhide(struct sway_cursor *cursor); | |||
105 | int cursor_get_timeout(struct sway_cursor *cursor); | 108 | int cursor_get_timeout(struct sway_cursor *cursor); |
106 | void cursor_notify_key_press(struct sway_cursor *cursor); | 109 | void cursor_notify_key_press(struct sway_cursor *cursor); |
107 | 110 | ||
111 | void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | ||
112 | struct wlr_input_device *device, double dx, double dy, | ||
113 | double dx_unaccel, double dy_unaccel); | ||
114 | |||
108 | void dispatch_cursor_button(struct sway_cursor *cursor, | 115 | void dispatch_cursor_button(struct sway_cursor *cursor, |
109 | struct wlr_input_device *device, uint32_t time_msec, uint32_t button, | 116 | struct wlr_input_device *device, uint32_t time_msec, uint32_t button, |
110 | enum wlr_button_state state); | 117 | enum wl_pointer_button_state state); |
111 | 118 | ||
112 | void dispatch_cursor_axis(struct sway_cursor *cursor, | 119 | void dispatch_cursor_axis(struct sway_cursor *cursor, |
113 | struct wlr_event_pointer_axis *event); | 120 | struct wlr_pointer_axis_event *event); |
114 | 121 | ||
115 | void cursor_set_image(struct sway_cursor *cursor, const char *image, | 122 | void cursor_set_image(struct sway_cursor *cursor, const char *image, |
116 | struct wl_client *client); | 123 | struct wl_client *client); |
@@ -138,4 +145,6 @@ uint32_t get_mouse_button(const char *name, char **error); | |||
138 | 145 | ||
139 | const char *get_mouse_button_name(uint32_t button); | 146 | const char *get_mouse_button_name(uint32_t button); |
140 | 147 | ||
148 | void handle_request_set_cursor_shape(struct wl_listener *listener, void *data); | ||
149 | |||
141 | #endif | 150 | #endif |
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index c9bd08f0..45c75199 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h | |||
@@ -1,10 +1,10 @@ | |||
1 | #ifndef _SWAY_INPUT_INPUT_MANAGER_H | 1 | #ifndef _SWAY_INPUT_INPUT_MANAGER_H |
2 | #define _SWAY_INPUT_INPUT_MANAGER_H | 2 | #define _SWAY_INPUT_INPUT_MANAGER_H |
3 | #include <libinput.h> | 3 | #include <libinput.h> |
4 | #include <wlr/types/wlr_input_inhibitor.h> | ||
5 | #include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> | 4 | #include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> |
6 | #include <wlr/types/wlr_virtual_keyboard_v1.h> | 5 | #include <wlr/types/wlr_virtual_keyboard_v1.h> |
7 | #include <wlr/types/wlr_virtual_pointer_v1.h> | 6 | #include <wlr/types/wlr_virtual_pointer_v1.h> |
7 | #include <wlr/types/wlr_transient_seat_v1.h> | ||
8 | #include "sway/server.h" | 8 | #include "sway/server.h" |
9 | #include "sway/config.h" | 9 | #include "sway/config.h" |
10 | #include "list.h" | 10 | #include "list.h" |
@@ -21,10 +21,11 @@ struct sway_input_manager { | |||
21 | struct wl_list devices; | 21 | struct wl_list devices; |
22 | struct wl_list seats; | 22 | struct wl_list seats; |
23 | 23 | ||
24 | struct wlr_input_inhibit_manager *inhibit; | ||
25 | struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; | 24 | struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; |
26 | struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; | 25 | struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; |
27 | struct wlr_virtual_pointer_manager_v1 *virtual_pointer; | 26 | struct wlr_virtual_pointer_manager_v1 *virtual_pointer; |
27 | struct wlr_pointer_gestures_v1 *pointer_gestures; | ||
28 | struct wlr_transient_seat_manager_v1 *transient_seat_manager; | ||
28 | 29 | ||
29 | struct wl_listener new_input; | 30 | struct wl_listener new_input; |
30 | struct wl_listener inhibit_activate; | 31 | struct wl_listener inhibit_activate; |
@@ -32,6 +33,7 @@ struct sway_input_manager { | |||
32 | struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor; | 33 | struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor; |
33 | struct wl_listener virtual_keyboard_new; | 34 | struct wl_listener virtual_keyboard_new; |
34 | struct wl_listener virtual_pointer_new; | 35 | struct wl_listener virtual_pointer_new; |
36 | struct wl_listener transient_seat_create; | ||
35 | }; | 37 | }; |
36 | 38 | ||
37 | struct sway_input_manager *input_manager_create(struct sway_server *server); | 39 | struct sway_input_manager *input_manager_create(struct sway_server *server); |
@@ -44,7 +46,7 @@ void input_manager_configure_xcursor(void); | |||
44 | 46 | ||
45 | void input_manager_apply_input_config(struct input_config *input_config); | 47 | void input_manager_apply_input_config(struct input_config *input_config); |
46 | 48 | ||
47 | void input_manager_configure_all_inputs(void); | 49 | void input_manager_configure_all_input_mappings(void); |
48 | 50 | ||
49 | void input_manager_reset_input(struct sway_input_device *input_device); | 51 | void input_manager_reset_input(struct sway_input_device *input_device); |
50 | 52 | ||
diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index 2c61e5a7..571d9e6f 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h | |||
@@ -50,6 +50,7 @@ struct sway_shortcut_state { | |||
50 | 50 | ||
51 | struct sway_keyboard { | 51 | struct sway_keyboard { |
52 | struct sway_seat_device *seat_device; | 52 | struct sway_seat_device *seat_device; |
53 | struct wlr_keyboard *wlr; | ||
53 | 54 | ||
54 | struct xkb_keymap *keymap; | 55 | struct xkb_keymap *keymap; |
55 | xkb_layout_index_t effective_layout; | 56 | xkb_layout_index_t effective_layout; |
diff --git a/include/sway/input/libinput.h b/include/sway/input/libinput.h index 890d632e..1f84a8e3 100644 --- a/include/sway/input/libinput.h +++ b/include/sway/input/libinput.h | |||
@@ -2,7 +2,10 @@ | |||
2 | #define _SWAY_INPUT_LIBINPUT_H | 2 | #define _SWAY_INPUT_LIBINPUT_H |
3 | #include "sway/input/input-manager.h" | 3 | #include "sway/input/input-manager.h" |
4 | 4 | ||
5 | void sway_input_configure_libinput_device(struct sway_input_device *device); | 5 | bool sway_input_configure_libinput_device(struct sway_input_device *device); |
6 | |||
7 | void sway_input_configure_libinput_device_send_events( | ||
8 | struct sway_input_device *device); | ||
6 | 9 | ||
7 | void sway_input_reset_libinput_device(struct sway_input_device *device); | 10 | void sway_input_reset_libinput_device(struct sway_input_device *device); |
8 | 11 | ||
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 4118df66..428f9679 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h | |||
@@ -3,7 +3,9 @@ | |||
3 | 3 | ||
4 | #include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> | 4 | #include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> |
5 | #include <wlr/types/wlr_layer_shell_v1.h> | 5 | #include <wlr/types/wlr_layer_shell_v1.h> |
6 | #include <wlr/types/wlr_scene.h> | ||
6 | #include <wlr/types/wlr_seat.h> | 7 | #include <wlr/types/wlr_seat.h> |
8 | #include <wlr/types/wlr_touch.h> | ||
7 | #include <wlr/util/edges.h> | 9 | #include <wlr/util/edges.h> |
8 | #include "sway/config.h" | 10 | #include "sway/config.h" |
9 | #include "sway/input/input-manager.h" | 11 | #include "sway/input/input-manager.h" |
@@ -15,19 +17,41 @@ struct sway_seat; | |||
15 | struct sway_seatop_impl { | 17 | struct sway_seatop_impl { |
16 | void (*button)(struct sway_seat *seat, uint32_t time_msec, | 18 | void (*button)(struct sway_seat *seat, uint32_t time_msec, |
17 | struct wlr_input_device *device, uint32_t button, | 19 | struct wlr_input_device *device, uint32_t button, |
18 | enum wlr_button_state state); | 20 | enum wl_pointer_button_state state); |
19 | void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); | 21 | void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); |
20 | void (*pointer_axis)(struct sway_seat *seat, | 22 | void (*pointer_axis)(struct sway_seat *seat, |
21 | struct wlr_event_pointer_axis *event); | 23 | struct wlr_pointer_axis_event *event); |
24 | void (*hold_begin)(struct sway_seat *seat, | ||
25 | struct wlr_pointer_hold_begin_event *event); | ||
26 | void (*hold_end)(struct sway_seat *seat, | ||
27 | struct wlr_pointer_hold_end_event *event); | ||
28 | void (*pinch_begin)(struct sway_seat *seat, | ||
29 | struct wlr_pointer_pinch_begin_event *event); | ||
30 | void (*pinch_update)(struct sway_seat *seat, | ||
31 | struct wlr_pointer_pinch_update_event *event); | ||
32 | void (*pinch_end)(struct sway_seat *seat, | ||
33 | struct wlr_pointer_pinch_end_event *event); | ||
34 | void (*swipe_begin)(struct sway_seat *seat, | ||
35 | struct wlr_pointer_swipe_begin_event *event); | ||
36 | void (*swipe_update)(struct sway_seat *seat, | ||
37 | struct wlr_pointer_swipe_update_event *event); | ||
38 | void (*swipe_end)(struct sway_seat *seat, | ||
39 | struct wlr_pointer_swipe_end_event *event); | ||
22 | void (*rebase)(struct sway_seat *seat, uint32_t time_msec); | 40 | void (*rebase)(struct sway_seat *seat, uint32_t time_msec); |
41 | void (*touch_motion)(struct sway_seat *seat, | ||
42 | struct wlr_touch_motion_event *event, double lx, double ly); | ||
43 | void (*touch_up)(struct sway_seat *seat, | ||
44 | struct wlr_touch_up_event *event); | ||
45 | void (*touch_down)(struct sway_seat *seat, | ||
46 | struct wlr_touch_down_event *event, double lx, double ly); | ||
47 | void (*touch_cancel)(struct sway_seat *seat, | ||
48 | struct wlr_touch_cancel_event *event); | ||
23 | void (*tablet_tool_motion)(struct sway_seat *seat, | 49 | void (*tablet_tool_motion)(struct sway_seat *seat, |
24 | struct sway_tablet_tool *tool, uint32_t time_msec); | 50 | struct sway_tablet_tool *tool, uint32_t time_msec); |
25 | void (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool, | 51 | void (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool, |
26 | uint32_t time_msec, enum wlr_tablet_tool_tip_state state); | 52 | uint32_t time_msec, enum wlr_tablet_tool_tip_state state); |
27 | void (*end)(struct sway_seat *seat); | 53 | void (*end)(struct sway_seat *seat); |
28 | void (*unref)(struct sway_seat *seat, struct sway_container *con); | 54 | void (*unref)(struct sway_seat *seat, struct sway_container *con); |
29 | void (*render)(struct sway_seat *seat, struct sway_output *output, | ||
30 | pixman_region32_t *damage); | ||
31 | bool allow_set_cursor; | 55 | bool allow_set_cursor; |
32 | }; | 56 | }; |
33 | 57 | ||
@@ -50,19 +74,6 @@ struct sway_seat_node { | |||
50 | struct wl_listener destroy; | 74 | struct wl_listener destroy; |
51 | }; | 75 | }; |
52 | 76 | ||
53 | struct sway_drag_icon { | ||
54 | struct sway_seat *seat; | ||
55 | struct wlr_drag_icon *wlr_drag_icon; | ||
56 | struct wl_list link; // sway_root::drag_icons | ||
57 | |||
58 | double x, y; // in layout-local coordinates | ||
59 | |||
60 | struct wl_listener surface_commit; | ||
61 | struct wl_listener map; | ||
62 | struct wl_listener unmap; | ||
63 | struct wl_listener destroy; | ||
64 | }; | ||
65 | |||
66 | struct sway_drag { | 77 | struct sway_drag { |
67 | struct sway_seat *seat; | 78 | struct sway_seat *seat; |
68 | struct wlr_drag *wlr_drag; | 79 | struct wlr_drag *wlr_drag; |
@@ -73,16 +84,23 @@ struct sway_seat { | |||
73 | struct wlr_seat *wlr_seat; | 84 | struct wlr_seat *wlr_seat; |
74 | struct sway_cursor *cursor; | 85 | struct sway_cursor *cursor; |
75 | 86 | ||
87 | // Seat scene tree structure | ||
88 | // - scene_tree | ||
89 | // - drag icons | ||
90 | // - drag icon 1 | ||
91 | // - drag icon 2 | ||
92 | // - seatop specific stuff | ||
93 | struct wlr_scene_tree *scene_tree; | ||
94 | struct wlr_scene_tree *drag_icons; | ||
95 | |||
76 | bool has_focus; | 96 | bool has_focus; |
77 | struct wl_list focus_stack; // list of containers in focus order | 97 | struct wl_list focus_stack; // list of containers in focus order |
78 | struct sway_workspace *workspace; | 98 | struct sway_workspace *workspace; |
79 | char *prev_workspace_name; // for workspace back_and_forth | 99 | char *prev_workspace_name; // for workspace back_and_forth |
80 | 100 | ||
81 | // If the focused layer is set, views cannot receive keyboard focus | ||
82 | struct wlr_layer_surface_v1 *focused_layer; | 101 | struct wlr_layer_surface_v1 *focused_layer; |
83 | 102 | // If the exclusive layer is set, views cannot receive keyboard focus | |
84 | // If exclusive_client is set, no other clients will receive input events | 103 | bool has_exclusive_layer; |
85 | struct wl_client *exclusive_client; | ||
86 | 104 | ||
87 | // Last touch point | 105 | // Last touch point |
88 | int32_t touch_id; | 106 | int32_t touch_id; |
@@ -106,6 +124,7 @@ struct sway_seat { | |||
106 | struct wl_listener start_drag; | 124 | struct wl_listener start_drag; |
107 | struct wl_listener request_set_selection; | 125 | struct wl_listener request_set_selection; |
108 | struct wl_listener request_set_primary_selection; | 126 | struct wl_listener request_set_primary_selection; |
127 | struct wl_listener destroy; | ||
109 | 128 | ||
110 | struct wl_list devices; // sway_seat_device::link | 129 | struct wl_list devices; // sway_seat_device::link |
111 | struct wl_list keyboard_groups; // sway_keyboard_group::link | 130 | struct wl_list keyboard_groups; // sway_keyboard_group::link |
@@ -141,6 +160,9 @@ void seat_add_device(struct sway_seat *seat, | |||
141 | void seat_configure_device(struct sway_seat *seat, | 160 | void seat_configure_device(struct sway_seat *seat, |
142 | struct sway_input_device *device); | 161 | struct sway_input_device *device); |
143 | 162 | ||
163 | void seat_configure_device_mapping(struct sway_seat *seat, | ||
164 | struct sway_input_device *input_device); | ||
165 | |||
144 | void seat_reset_device(struct sway_seat *seat, | 166 | void seat_reset_device(struct sway_seat *seat, |
145 | struct sway_input_device *input_device); | 167 | struct sway_input_device *input_device); |
146 | 168 | ||
@@ -171,8 +193,7 @@ void seat_set_focus_surface(struct sway_seat *seat, | |||
171 | void seat_set_focus_layer(struct sway_seat *seat, | 193 | void seat_set_focus_layer(struct sway_seat *seat, |
172 | struct wlr_layer_surface_v1 *layer); | 194 | struct wlr_layer_surface_v1 *layer); |
173 | 195 | ||
174 | void seat_set_exclusive_client(struct sway_seat *seat, | 196 | void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client); |
175 | struct wl_client *client); | ||
176 | 197 | ||
177 | struct sway_node *seat_get_focus(struct sway_seat *seat); | 198 | struct sway_node *seat_get_focus(struct sway_seat *seat); |
178 | 199 | ||
@@ -231,7 +252,7 @@ void seat_idle_notify_activity(struct sway_seat *seat, | |||
231 | 252 | ||
232 | bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); | 253 | bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); |
233 | 254 | ||
234 | void drag_icon_update_position(struct sway_drag_icon *icon); | 255 | void drag_icons_update_position(struct sway_seat *seat); |
235 | 256 | ||
236 | enum wlr_edges find_resize_edge(struct sway_container *cont, | 257 | enum wlr_edges find_resize_edge(struct sway_container *cont, |
237 | struct wlr_surface *surface, struct sway_cursor *cursor); | 258 | struct wlr_surface *surface, struct sway_cursor *cursor); |
@@ -239,7 +260,13 @@ enum wlr_edges find_resize_edge(struct sway_container *cont, | |||
239 | void seatop_begin_default(struct sway_seat *seat); | 260 | void seatop_begin_default(struct sway_seat *seat); |
240 | 261 | ||
241 | void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, | 262 | void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, |
242 | uint32_t time_msec, int sx, int sy); | 263 | double sx, double sy); |
264 | |||
265 | void seatop_begin_down_on_surface(struct sway_seat *seat, | ||
266 | struct wlr_surface *surface, double sx, double sy); | ||
267 | |||
268 | void seatop_begin_touch_down(struct sway_seat *seat, struct wlr_surface *surface, | ||
269 | struct wlr_touch_down_event *event, double sx, double sy, double lx, double ly); | ||
243 | 270 | ||
244 | void seatop_begin_move_floating(struct sway_seat *seat, | 271 | void seatop_begin_move_floating(struct sway_seat *seat, |
245 | struct sway_container *con); | 272 | struct sway_container *con); |
@@ -260,18 +287,18 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, | |||
260 | struct sway_workspace *workspace); | 287 | struct sway_workspace *workspace); |
261 | 288 | ||
262 | void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, | 289 | void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, |
263 | uint32_t button, enum wlr_button_state state); | 290 | uint32_t button, enum wl_pointer_button_state state); |
264 | 291 | ||
265 | void seat_consider_warp_to_focus(struct sway_seat *seat); | 292 | void seat_consider_warp_to_focus(struct sway_seat *seat); |
266 | 293 | ||
267 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, | 294 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, |
268 | struct wlr_input_device *device, uint32_t button, | 295 | struct wlr_input_device *device, uint32_t button, |
269 | enum wlr_button_state state); | 296 | enum wl_pointer_button_state state); |
270 | 297 | ||
271 | void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); | 298 | void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); |
272 | 299 | ||
273 | void seatop_pointer_axis(struct sway_seat *seat, | 300 | void seatop_pointer_axis(struct sway_seat *seat, |
274 | struct wlr_event_pointer_axis *event); | 301 | struct wlr_pointer_axis_event *event); |
275 | 302 | ||
276 | void seatop_tablet_tool_tip(struct sway_seat *seat, | 303 | void seatop_tablet_tool_tip(struct sway_seat *seat, |
277 | struct sway_tablet_tool *tool, uint32_t time_msec, | 304 | struct sway_tablet_tool *tool, uint32_t time_msec, |
@@ -280,6 +307,37 @@ void seatop_tablet_tool_tip(struct sway_seat *seat, | |||
280 | void seatop_tablet_tool_motion(struct sway_seat *seat, | 307 | void seatop_tablet_tool_motion(struct sway_seat *seat, |
281 | struct sway_tablet_tool *tool, uint32_t time_msec); | 308 | struct sway_tablet_tool *tool, uint32_t time_msec); |
282 | 309 | ||
310 | void seatop_hold_begin(struct sway_seat *seat, | ||
311 | struct wlr_pointer_hold_begin_event *event); | ||
312 | void seatop_hold_end(struct sway_seat *seat, | ||
313 | struct wlr_pointer_hold_end_event *event); | ||
314 | |||
315 | void seatop_pinch_begin(struct sway_seat *seat, | ||
316 | struct wlr_pointer_pinch_begin_event *event); | ||
317 | void seatop_pinch_update(struct sway_seat *seat, | ||
318 | struct wlr_pointer_pinch_update_event *event); | ||
319 | void seatop_pinch_end(struct sway_seat *seat, | ||
320 | struct wlr_pointer_pinch_end_event *event); | ||
321 | |||
322 | void seatop_swipe_begin(struct sway_seat *seat, | ||
323 | struct wlr_pointer_swipe_begin_event *event); | ||
324 | void seatop_swipe_update(struct sway_seat *seat, | ||
325 | struct wlr_pointer_swipe_update_event *event); | ||
326 | void seatop_swipe_end(struct sway_seat *seat, | ||
327 | struct wlr_pointer_swipe_end_event *event); | ||
328 | |||
329 | void seatop_touch_motion(struct sway_seat *seat, | ||
330 | struct wlr_touch_motion_event *event, double lx, double ly); | ||
331 | |||
332 | void seatop_touch_up(struct sway_seat *seat, | ||
333 | struct wlr_touch_up_event *event); | ||
334 | |||
335 | void seatop_touch_down(struct sway_seat *seat, | ||
336 | struct wlr_touch_down_event *event, double lx, double ly); | ||
337 | |||
338 | void seatop_touch_cancel(struct sway_seat *seat, | ||
339 | struct wlr_touch_cancel_event *event); | ||
340 | |||
283 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); | 341 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); |
284 | 342 | ||
285 | /** | 343 | /** |
@@ -294,13 +352,6 @@ void seatop_end(struct sway_seat *seat); | |||
294 | */ | 352 | */ |
295 | void seatop_unref(struct sway_seat *seat, struct sway_container *con); | 353 | void seatop_unref(struct sway_seat *seat, struct sway_container *con); |
296 | 354 | ||
297 | /** | ||
298 | * Instructs a seatop to render anything that it needs to render | ||
299 | * (eg. dropzone for move-tiling) | ||
300 | */ | ||
301 | void seatop_render(struct sway_seat *seat, struct sway_output *output, | ||
302 | pixman_region32_t *damage); | ||
303 | |||
304 | bool seatop_allows_set_cursor(struct sway_seat *seat); | 355 | bool seatop_allows_set_cursor(struct sway_seat *seat); |
305 | 356 | ||
306 | /** | 357 | /** |
diff --git a/include/sway/input/switch.h b/include/sway/input/switch.h index 213b471d..de6787b7 100644 --- a/include/sway/input/switch.h +++ b/include/sway/input/switch.h | |||
@@ -5,6 +5,7 @@ | |||
5 | 5 | ||
6 | struct sway_switch { | 6 | struct sway_switch { |
7 | struct sway_seat_device *seat_device; | 7 | struct sway_seat_device *seat_device; |
8 | struct wlr_switch *wlr; | ||
8 | enum wlr_switch_state state; | 9 | enum wlr_switch_state state; |
9 | enum wlr_switch_type type; | 10 | enum wlr_switch_type type; |
10 | 11 | ||
diff --git a/include/sway/input/tablet.h b/include/sway/input/tablet.h index d7e4c242..2fa5db6d 100644 --- a/include/sway/input/tablet.h +++ b/include/sway/input/tablet.h | |||
@@ -32,6 +32,7 @@ struct sway_tablet_pad { | |||
32 | struct wl_list link; | 32 | struct wl_list link; |
33 | struct sway_seat_device *seat_device; | 33 | struct sway_seat_device *seat_device; |
34 | struct sway_tablet *tablet; | 34 | struct sway_tablet *tablet; |
35 | struct wlr_tablet_pad *wlr; | ||
35 | struct wlr_tablet_v2_tablet_pad *tablet_v2_pad; | 36 | struct wlr_tablet_v2_tablet_pad *tablet_v2_pad; |
36 | 37 | ||
37 | struct wl_listener attach; | 38 | struct wl_listener attach; |
@@ -62,7 +63,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad); | |||
62 | 63 | ||
63 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad); | 64 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad); |
64 | 65 | ||
65 | void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | 66 | void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad, |
66 | struct wlr_surface *surface); | 67 | struct wlr_surface *surface); |
67 | 68 | ||
68 | #endif | 69 | #endif |
diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h index 37744266..1993f928 100644 --- a/include/sway/input/text_input.h +++ b/include/sway/input/text_input.h | |||
@@ -3,13 +3,12 @@ | |||
3 | 3 | ||
4 | #include <wlr/types/wlr_text_input_v3.h> | 4 | #include <wlr/types/wlr_text_input_v3.h> |
5 | #include <wlr/types/wlr_input_method_v2.h> | 5 | #include <wlr/types/wlr_input_method_v2.h> |
6 | #include <wlr/types/wlr_surface.h> | 6 | #include <wlr/types/wlr_compositor.h> |
7 | #include "sway/input/seat.h" | ||
8 | 7 | ||
9 | /** | 8 | /** |
10 | * The relay structure manages the relationship between text-input and | 9 | * The relay structure manages the relationship between text-input and |
11 | * input_method interfaces on a given seat. Multiple text-input interfaces may | 10 | * input_method interfaces on a given seat. Multiple text-input interfaces may |
12 | * be bound to a relay, but at most one will be focused (reveiving events) at | 11 | * be bound to a relay, but at most one will be focused (receiving events) at |
13 | * a time. At most one input-method interface may be bound to the seat. The | 12 | * a time. At most one input-method interface may be bound to the seat. The |
14 | * relay manages life cycle of both sides. When both sides are present and | 13 | * relay manages life cycle of both sides. When both sides are present and |
15 | * focused, the relay passes messages between them. | 14 | * focused, the relay passes messages between them. |
@@ -22,18 +21,21 @@ struct sway_input_method_relay { | |||
22 | struct sway_seat *seat; | 21 | struct sway_seat *seat; |
23 | 22 | ||
24 | struct wl_list text_inputs; // sway_text_input::link | 23 | struct wl_list text_inputs; // sway_text_input::link |
24 | struct wl_list input_popups; // sway_input_popup::link | ||
25 | struct wlr_input_method_v2 *input_method; // doesn't have to be present | 25 | struct wlr_input_method_v2 *input_method; // doesn't have to be present |
26 | 26 | ||
27 | struct wl_listener text_input_new; | 27 | struct wl_listener text_input_new; |
28 | 28 | ||
29 | struct wl_listener input_method_new; | 29 | struct wl_listener input_method_new; |
30 | struct wl_listener input_method_commit; | 30 | struct wl_listener input_method_commit; |
31 | struct wl_listener input_method_new_popup_surface; | ||
31 | struct wl_listener input_method_grab_keyboard; | 32 | struct wl_listener input_method_grab_keyboard; |
32 | struct wl_listener input_method_destroy; | 33 | struct wl_listener input_method_destroy; |
33 | 34 | ||
34 | struct wl_listener input_method_keyboard_grab_destroy; | 35 | struct wl_listener input_method_keyboard_grab_destroy; |
35 | }; | 36 | }; |
36 | 37 | ||
38 | |||
37 | struct sway_text_input { | 39 | struct sway_text_input { |
38 | struct sway_input_method_relay *relay; | 40 | struct sway_input_method_relay *relay; |
39 | 41 | ||
diff --git a/include/sway/input/text_input_popup.h b/include/sway/input/text_input_popup.h new file mode 100644 index 00000000..e5f6ab8b --- /dev/null +++ b/include/sway/input/text_input_popup.h | |||
@@ -0,0 +1,20 @@ | |||
1 | #ifndef _SWAY_INPUT_TEXT_INPUT_POPUP_H | ||
2 | #define _SWAY_INPUT_TEXT_INPUT_POPUP_H | ||
3 | |||
4 | #include "sway/tree/view.h" | ||
5 | |||
6 | struct sway_input_popup { | ||
7 | struct sway_input_method_relay *relay; | ||
8 | |||
9 | struct wlr_scene_tree *scene_tree; | ||
10 | struct sway_popup_desc desc; | ||
11 | struct wlr_input_popup_surface_v2 *popup_surface; | ||
12 | |||
13 | struct wl_list link; | ||
14 | |||
15 | struct wl_listener popup_destroy; | ||
16 | struct wl_listener popup_surface_commit; | ||
17 | |||
18 | struct wl_listener focused_surface_unmap; | ||
19 | }; | ||
20 | #endif | ||
diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h index 6f4ade1a..bc9f4985 100644 --- a/include/sway/ipc-json.h +++ b/include/sway/ipc-json.h | |||
@@ -1,6 +1,7 @@ | |||
1 | #ifndef _SWAY_IPC_JSON_H | 1 | #ifndef _SWAY_IPC_JSON_H |
2 | #define _SWAY_IPC_JSON_H | 2 | #define _SWAY_IPC_JSON_H |
3 | #include <json.h> | 3 | #include <json.h> |
4 | #include "sway/output.h" | ||
4 | #include "sway/tree/container.h" | 5 | #include "sway/tree/container.h" |
5 | #include "sway/input/input-manager.h" | 6 | #include "sway/input/input-manager.h" |
6 | 7 | ||
@@ -9,6 +10,7 @@ json_object *ipc_json_get_version(void); | |||
9 | json_object *ipc_json_get_binding_mode(void); | 10 | json_object *ipc_json_get_binding_mode(void); |
10 | 11 | ||
11 | json_object *ipc_json_describe_disabled_output(struct sway_output *o); | 12 | json_object *ipc_json_describe_disabled_output(struct sway_output *o); |
13 | json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *o); | ||
12 | json_object *ipc_json_describe_node(struct sway_node *node); | 14 | json_object *ipc_json_describe_node(struct sway_node *node); |
13 | json_object *ipc_json_describe_node_recursive(struct sway_node *node); | 15 | json_object *ipc_json_describe_node_recursive(struct sway_node *node); |
14 | json_object *ipc_json_describe_input(struct sway_input_device *device); | 16 | json_object *ipc_json_describe_input(struct sway_input_device *device); |
diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index bc4f781a..d4c00942 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h | |||
@@ -21,5 +21,6 @@ void ipc_event_mode(const char *mode, bool pango); | |||
21 | void ipc_event_shutdown(const char *reason); | 21 | void ipc_event_shutdown(const char *reason); |
22 | void ipc_event_binding(struct sway_binding *binding); | 22 | void ipc_event_binding(struct sway_binding *binding); |
23 | void ipc_event_input(const char *change, struct sway_input_device *device); | 23 | void ipc_event_input(const char *change, struct sway_input_device *device); |
24 | void ipc_event_output(void); | ||
24 | 25 | ||
25 | #endif | 26 | #endif |
diff --git a/include/sway/layers.h b/include/sway/layers.h index 82ac5368..fd6384e0 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h | |||
@@ -1,58 +1,44 @@ | |||
1 | #ifndef _SWAY_LAYERS_H | 1 | #ifndef _SWAY_LAYERS_H |
2 | #define _SWAY_LAYERS_H | 2 | #define _SWAY_LAYERS_H |
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include <wlr/types/wlr_surface.h> | 4 | #include <wlr/types/wlr_compositor.h> |
5 | #include <wlr/types/wlr_layer_shell_v1.h> | 5 | #include <wlr/types/wlr_layer_shell_v1.h> |
6 | 6 | #include "sway/tree/view.h" | |
7 | enum layer_parent { | ||
8 | LAYER_PARENT_LAYER, | ||
9 | LAYER_PARENT_POPUP, | ||
10 | }; | ||
11 | 7 | ||
12 | struct sway_layer_surface { | 8 | struct sway_layer_surface { |
13 | struct wlr_layer_surface_v1 *layer_surface; | ||
14 | struct wl_list link; | ||
15 | |||
16 | struct wl_listener destroy; | ||
17 | struct wl_listener map; | 9 | struct wl_listener map; |
18 | struct wl_listener unmap; | 10 | struct wl_listener unmap; |
19 | struct wl_listener surface_commit; | 11 | struct wl_listener surface_commit; |
20 | struct wl_listener output_destroy; | 12 | struct wl_listener output_destroy; |
13 | struct wl_listener node_destroy; | ||
21 | struct wl_listener new_popup; | 14 | struct wl_listener new_popup; |
22 | struct wl_listener new_subsurface; | ||
23 | 15 | ||
24 | struct wlr_box geo; | 16 | bool mapped; |
25 | enum zwlr_layer_shell_v1_layer layer; | 17 | |
18 | struct wlr_scene_tree *popups; | ||
19 | struct sway_popup_desc desc; | ||
20 | |||
21 | struct sway_output *output; | ||
22 | struct wlr_scene_layer_surface_v1 *scene; | ||
23 | struct wlr_scene_tree *tree; | ||
24 | struct wlr_layer_surface_v1 *layer_surface; | ||
26 | }; | 25 | }; |
27 | 26 | ||
28 | struct sway_layer_popup { | 27 | struct sway_layer_popup { |
29 | struct wlr_xdg_popup *wlr_popup; | 28 | struct wlr_xdg_popup *wlr_popup; |
30 | enum layer_parent parent_type; | 29 | struct wlr_scene_tree *scene; |
31 | union { | 30 | struct sway_layer_surface *toplevel; |
32 | struct sway_layer_surface *parent_layer; | ||
33 | struct sway_layer_popup *parent_popup; | ||
34 | }; | ||
35 | struct wl_listener map; | ||
36 | struct wl_listener unmap; | ||
37 | struct wl_listener destroy; | ||
38 | struct wl_listener commit; | ||
39 | struct wl_listener new_popup; | ||
40 | }; | ||
41 | 31 | ||
42 | struct sway_layer_subsurface { | ||
43 | struct wlr_subsurface *wlr_subsurface; | ||
44 | struct sway_layer_surface *layer_surface; | ||
45 | |||
46 | struct wl_listener map; | ||
47 | struct wl_listener unmap; | ||
48 | struct wl_listener destroy; | 32 | struct wl_listener destroy; |
33 | struct wl_listener new_popup; | ||
49 | struct wl_listener commit; | 34 | struct wl_listener commit; |
50 | }; | 35 | }; |
51 | 36 | ||
52 | struct sway_output; | 37 | struct sway_output; |
53 | void arrange_layers(struct sway_output *output); | ||
54 | 38 | ||
55 | struct sway_layer_surface *layer_from_wlr_layer_surface_v1( | 39 | struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( |
56 | struct wlr_layer_surface_v1 *layer_surface); | 40 | struct wlr_surface *surface); |
41 | |||
42 | void arrange_layers(struct sway_output *output); | ||
57 | 43 | ||
58 | #endif | 44 | #endif |
diff --git a/include/sway/output.h b/include/sway/output.h index 5dfe0fff..d546d488 100644 --- a/include/sway/output.h +++ b/include/sway/output.h | |||
@@ -3,7 +3,9 @@ | |||
3 | #include <time.h> | 3 | #include <time.h> |
4 | #include <unistd.h> | 4 | #include <unistd.h> |
5 | #include <wayland-server-core.h> | 5 | #include <wayland-server-core.h> |
6 | #include <wlr/types/wlr_damage_ring.h> | ||
6 | #include <wlr/types/wlr_output.h> | 7 | #include <wlr/types/wlr_output.h> |
8 | #include <wlr/types/wlr_scene.h> | ||
7 | #include "config.h" | 9 | #include "config.h" |
8 | #include "sway/tree/node.h" | 10 | #include "sway/tree/node.h" |
9 | #include "sway/tree/view.h" | 11 | #include "sway/tree/view.h" |
@@ -18,43 +20,63 @@ struct sway_output_state { | |||
18 | 20 | ||
19 | struct sway_output { | 21 | struct sway_output { |
20 | struct sway_node node; | 22 | struct sway_node node; |
23 | |||
24 | struct { | ||
25 | struct wlr_scene_tree *shell_background; | ||
26 | struct wlr_scene_tree *shell_bottom; | ||
27 | struct wlr_scene_tree *tiling; | ||
28 | struct wlr_scene_tree *fullscreen; | ||
29 | struct wlr_scene_tree *shell_top; | ||
30 | struct wlr_scene_tree *shell_overlay; | ||
31 | struct wlr_scene_tree *session_lock; | ||
32 | } layers; | ||
33 | |||
34 | // when a container is fullscreen, in case the fullscreen surface is | ||
35 | // translucent (can see behind) we must make sure that the background is a | ||
36 | // solid color in order to conform to the wayland protocol. This rect | ||
37 | // ensures that when looking through a surface, all that will be seen | ||
38 | // is black. | ||
39 | struct wlr_scene_rect *fullscreen_background; | ||
40 | |||
21 | struct wlr_output *wlr_output; | 41 | struct wlr_output *wlr_output; |
42 | struct wlr_scene_output *scene_output; | ||
22 | struct sway_server *server; | 43 | struct sway_server *server; |
23 | struct wl_list link; | 44 | struct wl_list link; |
24 | 45 | ||
25 | struct wl_list layers[4]; // sway_layer_surface::link | ||
26 | struct wlr_box usable_area; | 46 | struct wlr_box usable_area; |
27 | 47 | ||
28 | struct timespec last_frame; | ||
29 | struct wlr_output_damage *damage; | ||
30 | |||
31 | int lx, ly; // layout coords | 48 | int lx, ly; // layout coords |
32 | int width, height; // transformed buffer size | 49 | int width, height; // transformed buffer size |
33 | enum wl_output_subpixel detected_subpixel; | 50 | enum wl_output_subpixel detected_subpixel; |
34 | enum scale_filter_mode scale_filter; | 51 | enum scale_filter_mode scale_filter; |
35 | // last applied mode when the output is DPMS'ed | ||
36 | struct wlr_output_mode *current_mode; | ||
37 | 52 | ||
38 | bool enabling, enabled; | 53 | bool enabled; |
39 | list_t *workspaces; | 54 | list_t *workspaces; |
40 | 55 | ||
41 | struct sway_output_state current; | 56 | struct sway_output_state current; |
42 | 57 | ||
58 | struct wl_listener layout_destroy; | ||
43 | struct wl_listener destroy; | 59 | struct wl_listener destroy; |
44 | struct wl_listener commit; | 60 | struct wl_listener commit; |
45 | struct wl_listener mode; | ||
46 | struct wl_listener present; | 61 | struct wl_listener present; |
47 | struct wl_listener damage_destroy; | 62 | struct wl_listener frame; |
48 | struct wl_listener damage_frame; | 63 | struct wl_listener request_state; |
49 | 64 | ||
50 | struct { | 65 | struct { |
51 | struct wl_signal destroy; | 66 | struct wl_signal disable; |
52 | } events; | 67 | } events; |
53 | 68 | ||
54 | struct timespec last_presentation; | 69 | struct timespec last_presentation; |
55 | uint32_t refresh_nsec; | 70 | uint32_t refresh_nsec; |
56 | int max_render_time; // In milliseconds | 71 | int max_render_time; // In milliseconds |
57 | struct wl_event_source *repaint_timer; | 72 | struct wl_event_source *repaint_timer; |
73 | bool gamma_lut_changed; | ||
74 | }; | ||
75 | |||
76 | struct sway_output_non_desktop { | ||
77 | struct wlr_output *wlr_output; | ||
78 | |||
79 | struct wl_listener destroy; | ||
58 | }; | 80 | }; |
59 | 81 | ||
60 | struct sway_output *output_create(struct wlr_output *wlr_output); | 82 | struct sway_output *output_create(struct wlr_output *wlr_output); |
@@ -75,18 +97,8 @@ typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, | |||
75 | struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box, | 97 | struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box, |
76 | void *user_data); | 98 | void *user_data); |
77 | 99 | ||
78 | void output_damage_whole(struct sway_output *output); | 100 | bool output_match_name_or_id(struct sway_output *output, |
79 | 101 | const char *name_or_id); | |
80 | void output_damage_surface(struct sway_output *output, double ox, double oy, | ||
81 | struct wlr_surface *surface, bool whole); | ||
82 | |||
83 | void output_damage_from_view(struct sway_output *output, | ||
84 | struct sway_view *view); | ||
85 | |||
86 | void output_damage_box(struct sway_output *output, struct wlr_box *box); | ||
87 | |||
88 | void output_damage_whole_container(struct sway_output *output, | ||
89 | struct sway_container *con); | ||
90 | 102 | ||
91 | // this ONLY includes the enabled outputs | 103 | // this ONLY includes the enabled outputs |
92 | struct sway_output *output_by_name_or_id(const char *name_or_id); | 104 | struct sway_output *output_by_name_or_id(const char *name_or_id); |
@@ -100,47 +112,8 @@ void output_enable(struct sway_output *output); | |||
100 | 112 | ||
101 | void output_disable(struct sway_output *output); | 113 | void output_disable(struct sway_output *output); |
102 | 114 | ||
103 | bool output_has_opaque_overlay_layer_surface(struct sway_output *output); | ||
104 | |||
105 | struct sway_workspace *output_get_active_workspace(struct sway_output *output); | 115 | struct sway_workspace *output_get_active_workspace(struct sway_output *output); |
106 | 116 | ||
107 | void output_render(struct sway_output *output, struct timespec *when, | ||
108 | pixman_region32_t *damage); | ||
109 | |||
110 | void output_surface_for_each_surface(struct sway_output *output, | ||
111 | struct wlr_surface *surface, double ox, double oy, | ||
112 | sway_surface_iterator_func_t iterator, void *user_data); | ||
113 | |||
114 | void output_view_for_each_surface(struct sway_output *output, | ||
115 | struct sway_view *view, sway_surface_iterator_func_t iterator, | ||
116 | void *user_data); | ||
117 | |||
118 | void output_view_for_each_popup_surface(struct sway_output *output, | ||
119 | struct sway_view *view, sway_surface_iterator_func_t iterator, | ||
120 | void *user_data); | ||
121 | |||
122 | void output_layer_for_each_surface(struct sway_output *output, | ||
123 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | ||
124 | void *user_data); | ||
125 | |||
126 | void output_layer_for_each_toplevel_surface(struct sway_output *output, | ||
127 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | ||
128 | void *user_data); | ||
129 | |||
130 | void output_layer_for_each_popup_surface(struct sway_output *output, | ||
131 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | ||
132 | void *user_data); | ||
133 | |||
134 | #if HAVE_XWAYLAND | ||
135 | void output_unmanaged_for_each_surface(struct sway_output *output, | ||
136 | struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, | ||
137 | void *user_data); | ||
138 | #endif | ||
139 | |||
140 | void output_drag_icons_for_each_surface(struct sway_output *output, | ||
141 | struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, | ||
142 | void *user_data); | ||
143 | |||
144 | void output_for_each_workspace(struct sway_output *output, | 117 | void output_for_each_workspace(struct sway_output *output, |
145 | void (*f)(struct sway_workspace *ws, void *data), void *data); | 118 | void (*f)(struct sway_workspace *ws, void *data), void *data); |
146 | 119 | ||
@@ -158,18 +131,12 @@ void output_get_box(struct sway_output *output, struct wlr_box *box); | |||
158 | enum sway_container_layout output_get_default_layout( | 131 | enum sway_container_layout output_get_default_layout( |
159 | struct sway_output *output); | 132 | struct sway_output *output); |
160 | 133 | ||
161 | void render_rect(struct sway_output *output, | ||
162 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
163 | float color[static 4]); | ||
164 | |||
165 | void premultiply_alpha(float color[4], float opacity); | ||
166 | |||
167 | void scale_box(struct wlr_box *box, float scale); | ||
168 | |||
169 | enum wlr_direction opposite_direction(enum wlr_direction d); | 134 | enum wlr_direction opposite_direction(enum wlr_direction d); |
170 | 135 | ||
171 | void handle_output_layout_change(struct wl_listener *listener, void *data); | 136 | void handle_output_layout_change(struct wl_listener *listener, void *data); |
172 | 137 | ||
138 | void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data); | ||
139 | |||
173 | void handle_output_manager_apply(struct wl_listener *listener, void *data); | 140 | void handle_output_manager_apply(struct wl_listener *listener, void *data); |
174 | 141 | ||
175 | void handle_output_manager_test(struct wl_listener *listener, void *data); | 142 | void handle_output_manager_test(struct wl_listener *listener, void *data); |
@@ -177,4 +144,6 @@ void handle_output_manager_test(struct wl_listener *listener, void *data); | |||
177 | void handle_output_power_manager_set_mode(struct wl_listener *listener, | 144 | void handle_output_power_manager_set_mode(struct wl_listener *listener, |
178 | void *data); | 145 | void *data); |
179 | 146 | ||
147 | struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output); | ||
148 | |||
180 | #endif | 149 | #endif |
diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h new file mode 100644 index 00000000..2649d7c2 --- /dev/null +++ b/include/sway/scene_descriptor.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /** | ||
2 | * Across a wayland compositor, there are multiple shells: It can be | ||
3 | * a toplevel, or a layer_shell, or even something more meta like a drag | ||
4 | * icon or highlight indicators when dragging windows around. | ||
5 | * | ||
6 | * This object lets us store values that represent these modes of operation | ||
7 | * and keep track of what object is being represented. | ||
8 | */ | ||
9 | #ifndef _SWAY_SCENE_DESCRIPTOR_H | ||
10 | #define _SWAY_SCENE_DESCRIPTOR_H | ||
11 | #include <wlr/types/wlr_scene.h> | ||
12 | |||
13 | enum sway_scene_descriptor_type { | ||
14 | SWAY_SCENE_DESC_BUFFER_TIMER, | ||
15 | SWAY_SCENE_DESC_NON_INTERACTIVE, | ||
16 | SWAY_SCENE_DESC_CONTAINER, | ||
17 | SWAY_SCENE_DESC_VIEW, | ||
18 | SWAY_SCENE_DESC_LAYER_SHELL, | ||
19 | SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, | ||
20 | SWAY_SCENE_DESC_POPUP, | ||
21 | SWAY_SCENE_DESC_DRAG_ICON, | ||
22 | }; | ||
23 | |||
24 | bool scene_descriptor_assign(struct wlr_scene_node *node, | ||
25 | enum sway_scene_descriptor_type type, void *data); | ||
26 | |||
27 | void *scene_descriptor_try_get(struct wlr_scene_node *node, | ||
28 | enum sway_scene_descriptor_type type); | ||
29 | |||
30 | void scene_descriptor_destroy(struct wlr_scene_node *node, | ||
31 | enum sway_scene_descriptor_type type); | ||
32 | |||
33 | #endif | ||
diff --git a/include/sway/server.h b/include/sway/server.h index 3a5670d9..c71851f6 100644 --- a/include/sway/server.h +++ b/include/sway/server.h | |||
@@ -2,41 +2,43 @@ | |||
2 | #define _SWAY_SERVER_H | 2 | #define _SWAY_SERVER_H |
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include <wayland-server-core.h> | 4 | #include <wayland-server-core.h> |
5 | #include <wlr/backend.h> | ||
6 | #include <wlr/backend/session.h> | ||
7 | #include <wlr/render/wlr_renderer.h> | ||
8 | #include <wlr/types/wlr_compositor.h> | ||
9 | #include <wlr/types/wlr_data_device.h> | ||
10 | #include <wlr/types/wlr_input_method_v2.h> | ||
11 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
12 | #include <wlr/types/wlr_layer_shell_v1.h> | ||
13 | #include <wlr/types/wlr_output_management_v1.h> | ||
14 | #include <wlr/types/wlr_output_power_management_v1.h> | ||
15 | #include <wlr/types/wlr_presentation_time.h> | ||
16 | #include <wlr/types/wlr_relative_pointer_v1.h> | ||
17 | #include <wlr/types/wlr_server_decoration.h> | ||
18 | #include <wlr/types/wlr_text_input_v3.h> | ||
19 | #include <wlr/types/wlr_xdg_shell.h> | ||
20 | #include "config.h" | 5 | #include "config.h" |
21 | #include "list.h" | 6 | #include "list.h" |
7 | #include "sway/desktop/idle_inhibit_v1.h" | ||
22 | #if HAVE_XWAYLAND | 8 | #if HAVE_XWAYLAND |
23 | #include "sway/xwayland.h" | 9 | #include "sway/xwayland.h" |
24 | #endif | 10 | #endif |
25 | 11 | ||
26 | struct sway_transaction; | 12 | struct sway_transaction; |
27 | 13 | ||
14 | struct sway_session_lock { | ||
15 | struct wlr_session_lock_v1 *lock; | ||
16 | struct wlr_surface *focused; | ||
17 | bool abandoned; | ||
18 | |||
19 | struct wl_list outputs; // struct sway_session_lock_output | ||
20 | |||
21 | // invalid if the session is abandoned | ||
22 | struct wl_listener new_surface; | ||
23 | struct wl_listener unlock; | ||
24 | struct wl_listener destroy; | ||
25 | }; | ||
26 | |||
28 | struct sway_server { | 27 | struct sway_server { |
29 | struct wl_display *wl_display; | 28 | struct wl_display *wl_display; |
30 | struct wl_event_loop *wl_event_loop; | 29 | struct wl_event_loop *wl_event_loop; |
31 | const char *socket; | 30 | const char *socket; |
32 | 31 | ||
33 | struct wlr_backend *backend; | 32 | struct wlr_backend *backend; |
34 | struct wlr_backend *noop_backend; | 33 | struct wlr_session *session; |
35 | // secondary headless backend used for creating virtual outputs on-the-fly | 34 | // secondary headless backend used for creating virtual outputs on-the-fly |
36 | struct wlr_backend *headless_backend; | 35 | struct wlr_backend *headless_backend; |
36 | struct wlr_renderer *renderer; | ||
37 | struct wlr_allocator *allocator; | ||
37 | 38 | ||
38 | struct wlr_compositor *compositor; | 39 | struct wlr_compositor *compositor; |
39 | struct wl_listener compositor_new_surface; | 40 | |
41 | struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; | ||
40 | 42 | ||
41 | struct wlr_data_device_manager *data_device_manager; | 43 | struct wlr_data_device_manager *data_device_manager; |
42 | 44 | ||
@@ -44,15 +46,16 @@ struct sway_server { | |||
44 | 46 | ||
45 | struct wl_listener new_output; | 47 | struct wl_listener new_output; |
46 | struct wl_listener output_layout_change; | 48 | struct wl_listener output_layout_change; |
49 | struct wl_listener renderer_lost; | ||
47 | 50 | ||
48 | struct wlr_idle *idle; | 51 | struct wlr_idle_notifier_v1 *idle_notifier_v1; |
49 | struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1; | 52 | struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; |
50 | 53 | ||
51 | struct wlr_layer_shell_v1 *layer_shell; | 54 | struct wlr_layer_shell_v1 *layer_shell; |
52 | struct wl_listener layer_shell_surface; | 55 | struct wl_listener layer_shell_surface; |
53 | 56 | ||
54 | struct wlr_xdg_shell *xdg_shell; | 57 | struct wlr_xdg_shell *xdg_shell; |
55 | struct wl_listener xdg_shell_surface; | 58 | struct wl_listener xdg_shell_toplevel; |
56 | 59 | ||
57 | struct wlr_tablet_manager_v2 *tablet_v2; | 60 | struct wlr_tablet_manager_v2 *tablet_v2; |
58 | 61 | ||
@@ -72,7 +75,8 @@ struct sway_server { | |||
72 | struct wl_listener xdg_decoration; | 75 | struct wl_listener xdg_decoration; |
73 | struct wl_list xdg_decorations; // sway_xdg_decoration::link | 76 | struct wl_list xdg_decorations; // sway_xdg_decoration::link |
74 | 77 | ||
75 | struct wlr_presentation *presentation; | 78 | struct wlr_drm_lease_v1_manager *drm_lease_manager; |
79 | struct wl_listener drm_lease_request; | ||
76 | 80 | ||
77 | struct wlr_pointer_constraints_v1 *pointer_constraints; | 81 | struct wlr_pointer_constraints_v1 *pointer_constraints; |
78 | struct wl_listener pointer_constraint; | 82 | struct wl_listener pointer_constraint; |
@@ -81,14 +85,36 @@ struct sway_server { | |||
81 | struct wl_listener output_manager_apply; | 85 | struct wl_listener output_manager_apply; |
82 | struct wl_listener output_manager_test; | 86 | struct wl_listener output_manager_test; |
83 | 87 | ||
88 | struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; | ||
89 | struct wl_listener gamma_control_set_gamma; | ||
90 | |||
91 | struct { | ||
92 | struct sway_session_lock *lock; | ||
93 | struct wlr_session_lock_manager_v1 *manager; | ||
94 | |||
95 | struct wl_listener new_lock; | ||
96 | struct wl_listener manager_destroy; | ||
97 | } session_lock; | ||
98 | |||
84 | struct wlr_output_power_manager_v1 *output_power_manager_v1; | 99 | struct wlr_output_power_manager_v1 *output_power_manager_v1; |
85 | struct wl_listener output_power_manager_set_mode; | 100 | struct wl_listener output_power_manager_set_mode; |
86 | struct wlr_input_method_manager_v2 *input_method; | 101 | struct wlr_input_method_manager_v2 *input_method; |
87 | struct wlr_text_input_manager_v3 *text_input; | 102 | struct wlr_text_input_manager_v3 *text_input; |
103 | struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list; | ||
88 | struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; | 104 | struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; |
105 | struct wlr_content_type_manager_v1 *content_type_manager_v1; | ||
106 | struct wlr_data_control_manager_v1 *data_control_manager_v1; | ||
107 | struct wlr_screencopy_manager_v1 *screencopy_manager_v1; | ||
108 | struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1; | ||
109 | struct wlr_security_context_manager_v1 *security_context_manager_v1; | ||
89 | 110 | ||
90 | struct wlr_xdg_activation_v1 *xdg_activation_v1; | 111 | struct wlr_xdg_activation_v1 *xdg_activation_v1; |
91 | struct wl_listener xdg_activation_v1_request_activate; | 112 | struct wl_listener xdg_activation_v1_request_activate; |
113 | struct wl_listener xdg_activation_v1_new_token; | ||
114 | |||
115 | struct wl_listener request_set_cursor_shape; | ||
116 | |||
117 | struct wl_list pending_launcher_ctxs; // launcher_ctx::link | ||
92 | 118 | ||
93 | // The timeout for transactions, after which a transaction is applied | 119 | // The timeout for transactions, after which a transaction is applied |
94 | // regardless of readiness. | 120 | // regardless of readiness. |
@@ -115,29 +141,30 @@ struct sway_debug { | |||
115 | bool noatomic; // Ignore atomic layout updates | 141 | bool noatomic; // Ignore atomic layout updates |
116 | bool txn_timings; // Log verbose messages about transactions | 142 | bool txn_timings; // Log verbose messages about transactions |
117 | bool txn_wait; // Always wait for the timeout before applying | 143 | bool txn_wait; // Always wait for the timeout before applying |
118 | 144 | bool legacy_wl_drm; // Enable the legacy wl_drm interface | |
119 | enum { | ||
120 | DAMAGE_DEFAULT, // Default behaviour | ||
121 | DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged | ||
122 | DAMAGE_RERENDER, // Render the full output when any damage occurs | ||
123 | } damage; | ||
124 | }; | 145 | }; |
125 | 146 | ||
126 | extern struct sway_debug debug; | 147 | extern struct sway_debug debug; |
127 | 148 | ||
128 | /* Prepares an unprivileged server_init by performing all privileged operations in advance */ | 149 | extern bool allow_unsupported_gpu; |
129 | bool server_privileged_prepare(struct sway_server *server); | 150 | |
130 | bool server_init(struct sway_server *server); | 151 | bool server_init(struct sway_server *server); |
131 | void server_fini(struct sway_server *server); | 152 | void server_fini(struct sway_server *server); |
132 | bool server_start(struct sway_server *server); | 153 | bool server_start(struct sway_server *server); |
133 | void server_run(struct sway_server *server); | 154 | void server_run(struct sway_server *server); |
134 | 155 | ||
135 | void handle_compositor_new_surface(struct wl_listener *listener, void *data); | 156 | void restore_nofile_limit(void); |
157 | |||
136 | void handle_new_output(struct wl_listener *listener, void *data); | 158 | void handle_new_output(struct wl_listener *listener, void *data); |
137 | 159 | ||
138 | void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); | 160 | void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); |
139 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); | 161 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); |
140 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data); | 162 | void sway_session_lock_init(void); |
163 | void sway_session_lock_add_output(struct sway_session_lock *lock, | ||
164 | struct sway_output *output); | ||
165 | bool sway_session_lock_has_surface(struct sway_session_lock *lock, | ||
166 | struct wlr_surface *surface); | ||
167 | void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data); | ||
141 | #if HAVE_XWAYLAND | 168 | #if HAVE_XWAYLAND |
142 | void handle_xwayland_surface(struct wl_listener *listener, void *data); | 169 | void handle_xwayland_surface(struct wl_listener *listener, void *data); |
143 | #endif | 170 | #endif |
@@ -146,5 +173,9 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data); | |||
146 | void handle_pointer_constraint(struct wl_listener *listener, void *data); | 173 | void handle_pointer_constraint(struct wl_listener *listener, void *data); |
147 | void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, | 174 | void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, |
148 | void *data); | 175 | void *data); |
176 | void xdg_activation_v1_handle_new_token(struct wl_listener *listener, | ||
177 | void *data); | ||
178 | |||
179 | void set_rr_scheduling(void); | ||
149 | 180 | ||
150 | #endif | 181 | #endif |
diff --git a/include/sway/surface.h b/include/sway/surface.h deleted file mode 100644 index 4da96c02..00000000 --- a/include/sway/surface.h +++ /dev/null | |||
@@ -1,18 +0,0 @@ | |||
1 | #ifndef _SWAY_SURFACE_H | ||
2 | #define _SWAY_SURFACE_H | ||
3 | #include <wlr/types/wlr_surface.h> | ||
4 | |||
5 | struct sway_surface { | ||
6 | struct wlr_surface *wlr_surface; | ||
7 | |||
8 | struct wl_listener destroy; | ||
9 | |||
10 | /** | ||
11 | * This timer can be used for issuing delayed frame done callbacks (for | ||
12 | * example, to improve presentation latency). Its handler is set to a | ||
13 | * function that issues a frame done callback to this surface. | ||
14 | */ | ||
15 | struct wl_event_source *frame_done_timer; | ||
16 | }; | ||
17 | |||
18 | #endif | ||
diff --git a/include/sway/sway_text_node.h b/include/sway/sway_text_node.h new file mode 100644 index 00000000..0d4209bb --- /dev/null +++ b/include/sway/sway_text_node.h | |||
@@ -0,0 +1,28 @@ | |||
1 | #ifndef _SWAY_BUFFER_H | ||
2 | #define _SWAY_BUFFER_H | ||
3 | #include <wlr/types/wlr_scene.h> | ||
4 | |||
5 | struct sway_text_node { | ||
6 | int width; | ||
7 | int max_width; | ||
8 | int height; | ||
9 | int baseline; | ||
10 | bool pango_markup; | ||
11 | float color[4]; | ||
12 | float background[4]; | ||
13 | |||
14 | struct wlr_scene_node *node; | ||
15 | }; | ||
16 | |||
17 | struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, | ||
18 | char *text, float color[4], bool pango_markup); | ||
19 | |||
20 | void sway_text_node_set_color(struct sway_text_node *node, float color[4]); | ||
21 | |||
22 | void sway_text_node_set_text(struct sway_text_node *node, char *text); | ||
23 | |||
24 | void sway_text_node_set_max_width(struct sway_text_node *node, int max_width); | ||
25 | |||
26 | void sway_text_node_set_background(struct sway_text_node *node, float background[4]); | ||
27 | |||
28 | #endif | ||
diff --git a/include/sway/swaynag.h b/include/sway/swaynag.h index 74d9ea18..03bd52c3 100644 --- a/include/sway/swaynag.h +++ b/include/sway/swaynag.h | |||
@@ -1,6 +1,7 @@ | |||
1 | #ifndef _SWAY_SWAYNAG_H | 1 | #ifndef _SWAY_SWAYNAG_H |
2 | #define _SWAY_SWAYNAG_H | 2 | #define _SWAY_SWAYNAG_H |
3 | #include <wayland-server-core.h> | 3 | #include <wayland-server-core.h> |
4 | #include "stringop.h" | ||
4 | 5 | ||
5 | struct swaynag_instance { | 6 | struct swaynag_instance { |
6 | struct wl_client *client; | 7 | struct wl_client *client; |
@@ -21,7 +22,7 @@ bool swaynag_spawn(const char *swaynag_command, | |||
21 | // Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed | 22 | // Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed |
22 | // is false. | 23 | // is false. |
23 | void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, | 24 | void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, |
24 | const char *fmt, ...); | 25 | const char *fmt, ...) _SWAY_ATTRIB_PRINTF(3, 4); |
25 | 26 | ||
26 | // If swaynag->detailed, close swaynag->fd[1] so swaynag displays | 27 | // If swaynag->detailed, close swaynag->fd[1] so swaynag displays |
27 | void swaynag_show(struct swaynag_instance *swaynag); | 28 | void swaynag_show(struct swaynag_instance *swaynag); |
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 38ee68bd..93f6bfbb 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h | |||
@@ -2,7 +2,8 @@ | |||
2 | #define _SWAY_CONTAINER_H | 2 | #define _SWAY_CONTAINER_H |
3 | #include <stdint.h> | 3 | #include <stdint.h> |
4 | #include <sys/types.h> | 4 | #include <sys/types.h> |
5 | #include <wlr/types/wlr_surface.h> | 5 | #include <wlr/types/wlr_compositor.h> |
6 | #include <wlr/types/wlr_scene.h> | ||
6 | #include "list.h" | 7 | #include "list.h" |
7 | #include "sway/tree/node.h" | 8 | #include "sway/tree/node.h" |
8 | 9 | ||
@@ -68,11 +69,39 @@ struct sway_container { | |||
68 | struct sway_node node; | 69 | struct sway_node node; |
69 | struct sway_view *view; | 70 | struct sway_view *view; |
70 | 71 | ||
72 | struct wlr_scene_tree *scene_tree; | ||
73 | |||
74 | struct { | ||
75 | struct wlr_scene_tree *tree; | ||
76 | |||
77 | struct wlr_scene_tree *border; | ||
78 | struct wlr_scene_tree *background; | ||
79 | |||
80 | struct sway_text_node *title_text; | ||
81 | struct sway_text_node *marks_text; | ||
82 | } title_bar; | ||
83 | |||
84 | struct { | ||
85 | struct wlr_scene_tree *tree; | ||
86 | |||
87 | struct wlr_scene_rect *top; | ||
88 | struct wlr_scene_rect *bottom; | ||
89 | struct wlr_scene_rect *left; | ||
90 | struct wlr_scene_rect *right; | ||
91 | } border; | ||
92 | |||
93 | struct wlr_scene_tree *content_tree; | ||
94 | struct wlr_scene_buffer *output_handler; | ||
95 | |||
96 | struct wl_listener output_enter; | ||
97 | struct wl_listener output_leave; | ||
98 | |||
71 | struct sway_container_state current; | 99 | struct sway_container_state current; |
72 | struct sway_container_state pending; | 100 | struct sway_container_state pending; |
73 | 101 | ||
74 | char *title; // The view's title (unformatted) | 102 | char *title; // The view's title (unformatted) |
75 | char *formatted_title; // The title displayed in the title bar | 103 | char *formatted_title; // The title displayed in the title bar |
104 | int title_width; | ||
76 | 105 | ||
77 | enum sway_container_layout prev_split_layout; | 106 | enum sway_container_layout prev_split_layout; |
78 | 107 | ||
@@ -100,33 +129,19 @@ struct sway_container { | |||
100 | double child_total_width; | 129 | double child_total_width; |
101 | double child_total_height; | 130 | double child_total_height; |
102 | 131 | ||
103 | // In most cases this is the same as the content x and y, but if the view | ||
104 | // refuses to resize to the content dimensions then it can be smaller. | ||
105 | // These are in layout coordinates. | ||
106 | double surface_x, surface_y; | ||
107 | |||
108 | // Outputs currently being intersected | ||
109 | list_t *outputs; // struct sway_output | ||
110 | |||
111 | // Indicates that the container is a scratchpad container. | 132 | // Indicates that the container is a scratchpad container. |
112 | // Both hidden and visible scratchpad containers have scratchpad=true. | 133 | // Both hidden and visible scratchpad containers have scratchpad=true. |
113 | // Hidden scratchpad containers have a NULL parent. | 134 | // Hidden scratchpad containers have a NULL parent. |
114 | bool scratchpad; | 135 | bool scratchpad; |
115 | 136 | ||
116 | float alpha; | 137 | // Stores last output size and position for adjusting coordinates of |
138 | // scratchpad windows. | ||
139 | // Unused for non-scratchpad windows. | ||
140 | struct wlr_box transform; | ||
117 | 141 | ||
118 | struct wlr_texture *title_focused; | 142 | float alpha; |
119 | struct wlr_texture *title_focused_inactive; | ||
120 | struct wlr_texture *title_unfocused; | ||
121 | struct wlr_texture *title_urgent; | ||
122 | size_t title_height; | ||
123 | size_t title_baseline; | ||
124 | 143 | ||
125 | list_t *marks; // char * | 144 | list_t *marks; // char * |
126 | struct wlr_texture *marks_focused; | ||
127 | struct wlr_texture *marks_focused_inactive; | ||
128 | struct wlr_texture *marks_unfocused; | ||
129 | struct wlr_texture *marks_urgent; | ||
130 | 145 | ||
131 | struct { | 146 | struct { |
132 | struct wl_signal destroy; | 147 | struct wl_signal destroy; |
@@ -146,19 +161,6 @@ void container_begin_destroy(struct sway_container *con); | |||
146 | struct sway_container *container_find_child(struct sway_container *container, | 161 | struct sway_container *container_find_child(struct sway_container *container, |
147 | bool (*test)(struct sway_container *view, void *data), void *data); | 162 | bool (*test)(struct sway_container *view, void *data), void *data); |
148 | 163 | ||
149 | /** | ||
150 | * Find a container at the given coordinates. Returns the surface and | ||
151 | * surface-local coordinates of the given layout coordinates if the container | ||
152 | * is a view and the view contains a surface at those coordinates. | ||
153 | */ | ||
154 | struct sway_container *container_at(struct sway_workspace *workspace, | ||
155 | double lx, double ly, struct wlr_surface **surface, | ||
156 | double *sx, double *sy); | ||
157 | |||
158 | struct sway_container *tiling_container_at( | ||
159 | struct sway_node *parent, double lx, double ly, | ||
160 | struct wlr_surface **surface, double *sx, double *sy); | ||
161 | |||
162 | void container_for_each_child(struct sway_container *container, | 164 | void container_for_each_child(struct sway_container *container, |
163 | void (*f)(struct sway_container *container, void *data), void *data); | 165 | void (*f)(struct sway_container *container, void *data), void *data); |
164 | 166 | ||
@@ -175,18 +177,13 @@ bool container_has_ancestor(struct sway_container *container, | |||
175 | 177 | ||
176 | void container_update_textures_recursive(struct sway_container *con); | 178 | void container_update_textures_recursive(struct sway_container *con); |
177 | 179 | ||
178 | void container_damage_whole(struct sway_container *container); | ||
179 | |||
180 | void container_reap_empty(struct sway_container *con); | 180 | void container_reap_empty(struct sway_container *con); |
181 | 181 | ||
182 | struct sway_container *container_flatten(struct sway_container *container); | 182 | struct sway_container *container_flatten(struct sway_container *container); |
183 | 183 | ||
184 | void container_update_title_textures(struct sway_container *container); | 184 | void container_update_title_bar(struct sway_container *container); |
185 | 185 | ||
186 | /** | 186 | void container_update_marks(struct sway_container *container); |
187 | * Calculate the container's title_height property. | ||
188 | */ | ||
189 | void container_calculate_title_height(struct sway_container *container); | ||
190 | 187 | ||
191 | size_t container_build_representation(enum sway_container_layout layout, | 188 | size_t container_build_representation(enum sway_container_layout layout, |
192 | list_t *children, char *buffer); | 189 | list_t *children, char *buffer); |
@@ -201,6 +198,9 @@ size_t container_titlebar_height(void); | |||
201 | void floating_calculate_constraints(int *min_width, int *max_width, | 198 | void floating_calculate_constraints(int *min_width, int *max_width, |
202 | int *min_height, int *max_height); | 199 | int *min_height, int *max_height); |
203 | 200 | ||
201 | void floating_fix_coordinates(struct sway_container *con, | ||
202 | struct wlr_box *old, struct wlr_box *new); | ||
203 | |||
204 | void container_floating_resize_and_center(struct sway_container *con); | 204 | void container_floating_resize_and_center(struct sway_container *con); |
205 | 205 | ||
206 | void container_floating_set_default_size(struct sway_container *con); | 206 | void container_floating_set_default_size(struct sway_container *con); |
@@ -220,11 +220,6 @@ void container_set_geometry_from_content(struct sway_container *con); | |||
220 | bool container_is_floating(struct sway_container *container); | 220 | bool container_is_floating(struct sway_container *container); |
221 | 221 | ||
222 | /** | 222 | /** |
223 | * Same as above, but for current container state. | ||
224 | */ | ||
225 | bool container_is_current_floating(struct sway_container *container); | ||
226 | |||
227 | /** | ||
228 | * Get a container's box in layout coordinates. | 223 | * Get a container's box in layout coordinates. |
229 | */ | 224 | */ |
230 | void container_get_box(struct sway_container *container, struct wlr_box *box); | 225 | void container_get_box(struct sway_container *container, struct wlr_box *box); |
@@ -286,26 +281,12 @@ bool container_is_floating_or_child(struct sway_container *container); | |||
286 | */ | 281 | */ |
287 | bool container_is_fullscreen_or_child(struct sway_container *container); | 282 | bool container_is_fullscreen_or_child(struct sway_container *container); |
288 | 283 | ||
289 | /** | ||
290 | * Return the output which will be used for scale purposes. | ||
291 | * This is the most recently entered output. | ||
292 | * If the container is not on any output, return NULL. | ||
293 | */ | ||
294 | struct sway_output *container_get_effective_output(struct sway_container *con); | ||
295 | |||
296 | void container_discover_outputs(struct sway_container *con); | ||
297 | |||
298 | enum sway_container_layout container_parent_layout(struct sway_container *con); | 284 | enum sway_container_layout container_parent_layout(struct sway_container *con); |
299 | 285 | ||
300 | enum sway_container_layout container_current_parent_layout( | ||
301 | struct sway_container *con); | ||
302 | |||
303 | list_t *container_get_siblings(struct sway_container *container); | 286 | list_t *container_get_siblings(struct sway_container *container); |
304 | 287 | ||
305 | int container_sibling_index(struct sway_container *child); | 288 | int container_sibling_index(struct sway_container *child); |
306 | 289 | ||
307 | list_t *container_get_current_siblings(struct sway_container *container); | ||
308 | |||
309 | void container_handle_fullscreen_reparent(struct sway_container *con); | 290 | void container_handle_fullscreen_reparent(struct sway_container *con); |
310 | 291 | ||
311 | void container_add_child(struct sway_container *parent, | 292 | void container_add_child(struct sway_container *parent, |
@@ -353,8 +334,6 @@ bool container_has_mark(struct sway_container *container, char *mark); | |||
353 | 334 | ||
354 | void container_add_mark(struct sway_container *container, char *mark); | 335 | void container_add_mark(struct sway_container *container, char *mark); |
355 | 336 | ||
356 | void container_update_marks_textures(struct sway_container *container); | ||
357 | |||
358 | void container_raise_floating(struct sway_container *con); | 337 | void container_raise_floating(struct sway_container *con); |
359 | 338 | ||
360 | bool container_is_scratchpad_hidden(struct sway_container *con); | 339 | bool container_is_scratchpad_hidden(struct sway_container *con); |
@@ -369,7 +348,7 @@ bool container_is_sticky_or_child(struct sway_container *con); | |||
369 | * This will destroy pairs of redundant H/V splits | 348 | * This will destroy pairs of redundant H/V splits |
370 | * e.g. H[V[H[app app]] app] -> H[app app app] | 349 | * e.g. H[V[H[app app]] app] -> H[app app app] |
371 | * The middle "V[H[" are eliminated by a call to container_squash | 350 | * The middle "V[H[" are eliminated by a call to container_squash |
372 | * on the V[ con. It's grandchildren are added to it's parent. | 351 | * on the V[ con. It's grandchildren are added to its parent. |
373 | * | 352 | * |
374 | * This function is roughly equivalent to i3's tree_flatten here: | 353 | * This function is roughly equivalent to i3's tree_flatten here: |
375 | * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651 | 354 | * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651 |
@@ -378,4 +357,10 @@ bool container_is_sticky_or_child(struct sway_container *con); | |||
378 | */ | 357 | */ |
379 | int container_squash(struct sway_container *con); | 358 | int container_squash(struct sway_container *con); |
380 | 359 | ||
360 | void container_arrange_title_bar(struct sway_container *con); | ||
361 | |||
362 | void container_update(struct sway_container *con); | ||
363 | |||
364 | void container_update_itself_and_parents(struct sway_container *con); | ||
365 | |||
381 | #endif | 366 | #endif |
diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index 470ee3b5..e2dbcdf0 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h | |||
@@ -1,6 +1,8 @@ | |||
1 | #ifndef _SWAY_NODE_H | 1 | #ifndef _SWAY_NODE_H |
2 | #define _SWAY_NODE_H | 2 | #define _SWAY_NODE_H |
3 | #include <wayland-server-core.h> | ||
3 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <wlr/types/wlr_scene.h> | ||
4 | #include "list.h" | 6 | #include "list.h" |
5 | 7 | ||
6 | #define MIN_SANE_W 100 | 8 | #define MIN_SANE_W 100 |
@@ -74,4 +76,15 @@ list_t *node_get_children(struct sway_node *node); | |||
74 | 76 | ||
75 | bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor); | 77 | bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor); |
76 | 78 | ||
79 | // when destroying a sway tree, it's not known which order the tree will be | ||
80 | // destroyed. To prevent freeing of scene_nodes recursing up the tree, | ||
81 | // let's use this helper function to disown them to the staging node. | ||
82 | void scene_node_disown_children(struct wlr_scene_tree *tree); | ||
83 | |||
84 | // a helper function used to allocate tree nodes. If an allocation failure | ||
85 | // occurs a flag is flipped that can be checked later to destroy a parent | ||
86 | // of this scene node preventing memory leaks. | ||
87 | struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, | ||
88 | bool *failed); | ||
89 | |||
77 | #endif | 90 | #endif |
diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index e8f4d573..15df0f55 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <wayland-server-core.h> | 3 | #include <wayland-server-core.h> |
4 | #include <wayland-util.h> | 4 | #include <wayland-util.h> |
5 | #include <wlr/types/wlr_output_layout.h> | 5 | #include <wlr/types/wlr_output_layout.h> |
6 | #include <wlr/types/wlr_scene.h> | ||
6 | #include <wlr/render/wlr_texture.h> | 7 | #include <wlr/render/wlr_texture.h> |
7 | #include "sway/tree/container.h" | 8 | #include "sway/tree/container.h" |
8 | #include "sway/tree/node.h" | 9 | #include "sway/tree/node.h" |
@@ -16,10 +17,44 @@ struct sway_root { | |||
16 | struct wlr_output_layout *output_layout; | 17 | struct wlr_output_layout *output_layout; |
17 | 18 | ||
18 | struct wl_listener output_layout_change; | 19 | struct wl_listener output_layout_change; |
20 | |||
21 | // scene node layout: | ||
22 | // - root | ||
23 | // - staging | ||
24 | // - layer shell stuff | ||
25 | // - tiling | ||
26 | // - floating | ||
27 | // - fullscreen stuff | ||
28 | // - seat stuff | ||
29 | // - ext_session_lock | ||
30 | struct wlr_scene *root_scene; | ||
31 | |||
32 | // since wlr_scene nodes can't be orphaned and must always | ||
33 | // have a parent, use this staging scene_tree so that a | ||
34 | // node always have a valid parent. Nothing in this | ||
35 | // staging node will be visible. | ||
36 | struct wlr_scene_tree *staging; | ||
37 | |||
38 | // tree containing all layers the compositor will render. Cursor handling | ||
39 | // will end up iterating this tree. | ||
40 | struct wlr_scene_tree *layer_tree; | ||
41 | |||
42 | struct { | ||
43 | struct wlr_scene_tree *shell_background; | ||
44 | struct wlr_scene_tree *shell_bottom; | ||
45 | struct wlr_scene_tree *tiling; | ||
46 | struct wlr_scene_tree *floating; | ||
47 | struct wlr_scene_tree *shell_top; | ||
48 | struct wlr_scene_tree *fullscreen; | ||
49 | struct wlr_scene_tree *fullscreen_global; | ||
19 | #if HAVE_XWAYLAND | 50 | #if HAVE_XWAYLAND |
20 | struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link | 51 | struct wlr_scene_tree *unmanaged; |
21 | #endif | 52 | #endif |
22 | struct wl_list drag_icons; // sway_drag_icon::link | 53 | struct wlr_scene_tree *shell_overlay; |
54 | struct wlr_scene_tree *popup; | ||
55 | struct wlr_scene_tree *seat; | ||
56 | struct wlr_scene_tree *session_lock; | ||
57 | } layers; | ||
23 | 58 | ||
24 | // Includes disabled outputs | 59 | // Includes disabled outputs |
25 | struct wl_list all_outputs; // sway_output::link | 60 | struct wl_list all_outputs; // sway_output::link |
@@ -28,10 +63,11 @@ struct sway_root { | |||
28 | double width, height; | 63 | double width, height; |
29 | 64 | ||
30 | list_t *outputs; // struct sway_output | 65 | list_t *outputs; // struct sway_output |
66 | list_t *non_desktop_outputs; // struct sway_output_non_desktop | ||
31 | list_t *scratchpad; // struct sway_container | 67 | list_t *scratchpad; // struct sway_container |
32 | 68 | ||
33 | // For when there's no connected outputs | 69 | // For when there's no connected outputs |
34 | struct sway_output *noop_output; | 70 | struct sway_output *fallback_output; |
35 | 71 | ||
36 | struct sway_container *fullscreen_global; | 72 | struct sway_container *fullscreen_global; |
37 | 73 | ||
@@ -40,7 +76,7 @@ struct sway_root { | |||
40 | } events; | 76 | } events; |
41 | }; | 77 | }; |
42 | 78 | ||
43 | struct sway_root *root_create(void); | 79 | struct sway_root *root_create(struct wl_display *display); |
44 | 80 | ||
45 | void root_destroy(struct sway_root *root); | 81 | void root_destroy(struct sway_root *root); |
46 | 82 | ||
@@ -68,12 +104,6 @@ void root_scratchpad_show(struct sway_container *con); | |||
68 | */ | 104 | */ |
69 | void root_scratchpad_hide(struct sway_container *con); | 105 | void root_scratchpad_hide(struct sway_container *con); |
70 | 106 | ||
71 | struct sway_workspace *root_workspace_for_pid(pid_t pid); | ||
72 | |||
73 | void root_record_workspace_pid(pid_t pid); | ||
74 | |||
75 | void root_remove_workspace_pid(pid_t pid); | ||
76 | |||
77 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), | 107 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), |
78 | void *data); | 108 | void *data); |
79 | 109 | ||
@@ -91,6 +121,4 @@ struct sway_container *root_find_container( | |||
91 | 121 | ||
92 | void root_get_box(struct sway_root *root, struct wlr_box *box); | 122 | void root_get_box(struct sway_root *root, struct wlr_box *box); |
93 | 123 | ||
94 | void root_rename_pid_workspaces(const char *old_name, const char *new_name); | ||
95 | |||
96 | #endif | 124 | #endif |
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 923498ec..7faacdcc 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -1,8 +1,9 @@ | |||
1 | #ifndef _SWAY_VIEW_H | 1 | #ifndef _SWAY_VIEW_H |
2 | #define _SWAY_VIEW_H | 2 | #define _SWAY_VIEW_H |
3 | #include <wayland-server-core.h> | 3 | #include <wayland-server-core.h> |
4 | #include <wlr/types/wlr_surface.h> | 4 | #include <wlr/types/wlr_compositor.h> |
5 | #include "config.h" | 5 | #include <wlr/types/wlr_scene.h> |
6 | #include "sway/config.h" | ||
6 | #if HAVE_XWAYLAND | 7 | #if HAVE_XWAYLAND |
7 | #include <wlr/xwayland.h> | 8 | #include <wlr/xwayland.h> |
8 | #endif | 9 | #endif |
@@ -45,10 +46,6 @@ struct sway_view_impl { | |||
45 | void (*set_fullscreen)(struct sway_view *view, bool fullscreen); | 46 | void (*set_fullscreen)(struct sway_view *view, bool fullscreen); |
46 | void (*set_resizing)(struct sway_view *view, bool resizing); | 47 | void (*set_resizing)(struct sway_view *view, bool resizing); |
47 | bool (*wants_floating)(struct sway_view *view); | 48 | bool (*wants_floating)(struct sway_view *view); |
48 | void (*for_each_surface)(struct sway_view *view, | ||
49 | wlr_surface_iterator_func_t iterator, void *user_data); | ||
50 | void (*for_each_popup_surface)(struct sway_view *view, | ||
51 | wlr_surface_iterator_func_t iterator, void *user_data); | ||
52 | bool (*is_transient_for)(struct sway_view *child, | 49 | bool (*is_transient_for)(struct sway_view *child, |
53 | struct sway_view *ancestor); | 50 | struct sway_view *ancestor); |
54 | void (*close)(struct sway_view *view); | 51 | void (*close)(struct sway_view *view); |
@@ -56,24 +53,20 @@ struct sway_view_impl { | |||
56 | void (*destroy)(struct sway_view *view); | 53 | void (*destroy)(struct sway_view *view); |
57 | }; | 54 | }; |
58 | 55 | ||
59 | struct sway_saved_buffer { | ||
60 | struct wlr_client_buffer *buffer; | ||
61 | int x, y; | ||
62 | int width, height; | ||
63 | enum wl_output_transform transform; | ||
64 | struct wlr_fbox source_box; | ||
65 | struct wl_list link; // sway_view::saved_buffers | ||
66 | }; | ||
67 | |||
68 | struct sway_view { | 56 | struct sway_view { |
69 | enum sway_view_type type; | 57 | enum sway_view_type type; |
70 | const struct sway_view_impl *impl; | 58 | const struct sway_view_impl *impl; |
71 | 59 | ||
60 | struct wlr_scene_tree *scene_tree; | ||
61 | struct wlr_scene_tree *content_tree; | ||
62 | struct wlr_scene_tree *saved_surface_tree; | ||
63 | |||
72 | struct sway_container *container; // NULL if unmapped and transactions finished | 64 | struct sway_container *container; // NULL if unmapped and transactions finished |
73 | struct wlr_surface *surface; // NULL for unmapped views | 65 | struct wlr_surface *surface; // NULL for unmapped views |
74 | struct sway_xdg_decoration *xdg_decoration; | 66 | struct sway_xdg_decoration *xdg_decoration; |
75 | 67 | ||
76 | pid_t pid; | 68 | pid_t pid; |
69 | struct launcher_ctx *ctx; | ||
77 | 70 | ||
78 | // The size the view would want to be if it weren't tiled. | 71 | // The size the view would want to be if it weren't tiled. |
79 | // Used when changing a view from tiled to floating. | 72 | // Used when changing a view from tiled to floating. |
@@ -87,15 +80,11 @@ struct sway_view { | |||
87 | bool allow_request_urgent; | 80 | bool allow_request_urgent; |
88 | struct wl_event_source *urgent_timer; | 81 | struct wl_event_source *urgent_timer; |
89 | 82 | ||
90 | struct wl_list saved_buffers; // sway_saved_buffer::link | ||
91 | |||
92 | // The geometry for whatever the client is committing, regardless of | 83 | // The geometry for whatever the client is committing, regardless of |
93 | // transaction state. Updated on every commit. | 84 | // transaction state. Updated on every commit. |
94 | struct wlr_box geometry; | 85 | struct wlr_box geometry; |
95 | 86 | ||
96 | // The "old" geometry during a transaction. Used to damage the old location | 87 | struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel; |
97 | // when a transaction is applied. | ||
98 | struct wlr_box saved_geometry; | ||
99 | 88 | ||
100 | struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; | 89 | struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; |
101 | struct wl_listener foreign_activate_request; | 90 | struct wl_listener foreign_activate_request; |
@@ -108,7 +97,7 @@ struct sway_view { | |||
108 | list_t *executed_criteria; // struct criteria * | 97 | list_t *executed_criteria; // struct criteria * |
109 | 98 | ||
110 | union { | 99 | union { |
111 | struct wlr_xdg_surface *wlr_xdg_surface; | 100 | struct wlr_xdg_toplevel *wlr_xdg_toplevel; |
112 | #if HAVE_XWAYLAND | 101 | #if HAVE_XWAYLAND |
113 | struct wlr_xwayland_surface *wlr_xwayland_surface; | 102 | struct wlr_xwayland_surface *wlr_xwayland_surface; |
114 | #endif | 103 | #endif |
@@ -118,8 +107,6 @@ struct sway_view { | |||
118 | struct wl_signal unmap; | 107 | struct wl_signal unmap; |
119 | } events; | 108 | } events; |
120 | 109 | ||
121 | struct wl_listener surface_new_subsurface; | ||
122 | |||
123 | int max_render_time; // In milliseconds | 110 | int max_render_time; // In milliseconds |
124 | 111 | ||
125 | enum seat_config_shortcuts_inhibit shortcuts_inhibit; | 112 | enum seat_config_shortcuts_inhibit shortcuts_inhibit; |
@@ -144,6 +131,8 @@ struct sway_xdg_shell_view { | |||
144 | struct sway_xwayland_view { | 131 | struct sway_xwayland_view { |
145 | struct sway_view view; | 132 | struct sway_view view; |
146 | 133 | ||
134 | struct wlr_scene_tree *surface_tree; | ||
135 | |||
147 | struct wl_listener commit; | 136 | struct wl_listener commit; |
148 | struct wl_listener request_move; | 137 | struct wl_listener request_move; |
149 | struct wl_listener request_resize; | 138 | struct wl_listener request_resize; |
@@ -155,71 +144,55 @@ struct sway_xwayland_view { | |||
155 | struct wl_listener set_title; | 144 | struct wl_listener set_title; |
156 | struct wl_listener set_class; | 145 | struct wl_listener set_class; |
157 | struct wl_listener set_role; | 146 | struct wl_listener set_role; |
147 | struct wl_listener set_startup_id; | ||
158 | struct wl_listener set_window_type; | 148 | struct wl_listener set_window_type; |
159 | struct wl_listener set_hints; | 149 | struct wl_listener set_hints; |
160 | struct wl_listener set_decorations; | 150 | struct wl_listener set_decorations; |
151 | struct wl_listener associate; | ||
152 | struct wl_listener dissociate; | ||
161 | struct wl_listener map; | 153 | struct wl_listener map; |
162 | struct wl_listener unmap; | 154 | struct wl_listener unmap; |
163 | struct wl_listener destroy; | 155 | struct wl_listener destroy; |
164 | struct wl_listener override_redirect; | 156 | struct wl_listener override_redirect; |
157 | |||
158 | struct wl_listener surface_tree_destroy; | ||
165 | }; | 159 | }; |
166 | 160 | ||
167 | struct sway_xwayland_unmanaged { | 161 | struct sway_xwayland_unmanaged { |
168 | struct wlr_xwayland_surface *wlr_xwayland_surface; | 162 | struct wlr_xwayland_surface *wlr_xwayland_surface; |
169 | struct wl_list link; | ||
170 | 163 | ||
171 | int lx, ly; | 164 | struct wlr_scene_surface *surface_scene; |
172 | 165 | ||
166 | struct wl_listener request_activate; | ||
173 | struct wl_listener request_configure; | 167 | struct wl_listener request_configure; |
174 | struct wl_listener request_fullscreen; | 168 | struct wl_listener request_fullscreen; |
175 | struct wl_listener commit; | ||
176 | struct wl_listener set_geometry; | 169 | struct wl_listener set_geometry; |
170 | struct wl_listener associate; | ||
171 | struct wl_listener dissociate; | ||
177 | struct wl_listener map; | 172 | struct wl_listener map; |
178 | struct wl_listener unmap; | 173 | struct wl_listener unmap; |
179 | struct wl_listener destroy; | 174 | struct wl_listener destroy; |
180 | struct wl_listener override_redirect; | 175 | struct wl_listener override_redirect; |
181 | }; | 176 | }; |
182 | #endif | 177 | #endif |
183 | struct sway_view_child; | ||
184 | |||
185 | struct sway_view_child_impl { | ||
186 | void (*get_root_coords)(struct sway_view_child *child, int *sx, int *sy); | ||
187 | void (*destroy)(struct sway_view_child *child); | ||
188 | }; | ||
189 | |||
190 | /** | ||
191 | * A view child is a surface in the view tree, such as a subsurface or a popup. | ||
192 | */ | ||
193 | struct sway_view_child { | ||
194 | const struct sway_view_child_impl *impl; | ||
195 | struct wl_list link; | ||
196 | 178 | ||
179 | struct sway_popup_desc { | ||
180 | struct wlr_scene_node *relative; | ||
197 | struct sway_view *view; | 181 | struct sway_view *view; |
198 | struct sway_view_child *parent; | ||
199 | struct wl_list children; // sway_view_child::link | ||
200 | struct wlr_surface *surface; | ||
201 | bool mapped; | ||
202 | |||
203 | struct wl_listener surface_commit; | ||
204 | struct wl_listener surface_new_subsurface; | ||
205 | struct wl_listener surface_map; | ||
206 | struct wl_listener surface_unmap; | ||
207 | struct wl_listener surface_destroy; | ||
208 | struct wl_listener view_unmap; | ||
209 | }; | ||
210 | |||
211 | struct sway_subsurface { | ||
212 | struct sway_view_child child; | ||
213 | |||
214 | struct wl_listener destroy; | ||
215 | }; | 182 | }; |
216 | 183 | ||
217 | struct sway_xdg_popup { | 184 | struct sway_xdg_popup { |
218 | struct sway_view_child child; | 185 | struct sway_view *view; |
186 | |||
187 | struct wlr_scene_tree *scene_tree; | ||
188 | struct wlr_scene_tree *xdg_surface_tree; | ||
189 | struct wlr_xdg_popup *wlr_xdg_popup; | ||
219 | 190 | ||
220 | struct wlr_xdg_surface *wlr_xdg_surface; | 191 | struct sway_popup_desc desc; |
221 | 192 | ||
193 | struct wl_listener surface_commit; | ||
222 | struct wl_listener new_popup; | 194 | struct wl_listener new_popup; |
195 | struct wl_listener reposition; | ||
223 | struct wl_listener destroy; | 196 | struct wl_listener destroy; |
224 | }; | 197 | }; |
225 | 198 | ||
@@ -268,7 +241,12 @@ void view_set_activated(struct sway_view *view, bool activated); | |||
268 | /** | 241 | /** |
269 | * Called when the view requests to be focused. | 242 | * Called when the view requests to be focused. |
270 | */ | 243 | */ |
271 | void view_request_activate(struct sway_view *view); | 244 | void view_request_activate(struct sway_view *view, struct sway_seat *seat); |
245 | |||
246 | /* | ||
247 | * Called when the view requests urgent state | ||
248 | */ | ||
249 | void view_request_urgent(struct sway_view *view); | ||
272 | 250 | ||
273 | /** | 251 | /** |
274 | * If possible, instructs the client to change their decoration mode. | 252 | * If possible, instructs the client to change their decoration mode. |
@@ -287,43 +265,31 @@ void view_close(struct sway_view *view); | |||
287 | 265 | ||
288 | void view_close_popups(struct sway_view *view); | 266 | void view_close_popups(struct sway_view *view); |
289 | 267 | ||
290 | void view_damage_from(struct sway_view *view); | ||
291 | |||
292 | /** | ||
293 | * Iterate all surfaces of a view (toplevels + popups). | ||
294 | */ | ||
295 | void view_for_each_surface(struct sway_view *view, | ||
296 | wlr_surface_iterator_func_t iterator, void *user_data); | ||
297 | |||
298 | /** | ||
299 | * Iterate all popup surfaces of a view. | ||
300 | */ | ||
301 | void view_for_each_popup_surface(struct sway_view *view, | ||
302 | wlr_surface_iterator_func_t iterator, void *user_data); | ||
303 | |||
304 | // view implementation | 268 | // view implementation |
305 | 269 | ||
306 | void view_init(struct sway_view *view, enum sway_view_type type, | 270 | bool view_init(struct sway_view *view, enum sway_view_type type, |
307 | const struct sway_view_impl *impl); | 271 | const struct sway_view_impl *impl); |
308 | 272 | ||
309 | void view_destroy(struct sway_view *view); | 273 | void view_destroy(struct sway_view *view); |
310 | 274 | ||
311 | void view_begin_destroy(struct sway_view *view); | 275 | void view_begin_destroy(struct sway_view *view); |
312 | 276 | ||
277 | /** | ||
278 | * Map a view, ie. make it visible in the tree. | ||
279 | * | ||
280 | * `fullscreen` should be set to true (and optionally `fullscreen_output` | ||
281 | * should be populated) if the view should be made fullscreen immediately. | ||
282 | * | ||
283 | * `decoration` should be set to true if the client prefers CSD. The client's | ||
284 | * preference may be ignored. | ||
285 | */ | ||
313 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | 286 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, |
314 | bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); | 287 | bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); |
315 | 288 | ||
316 | void view_unmap(struct sway_view *view); | 289 | void view_unmap(struct sway_view *view); |
317 | 290 | ||
318 | void view_update_size(struct sway_view *view); | 291 | void view_update_size(struct sway_view *view); |
319 | void view_center_surface(struct sway_view *view); | 292 | void view_center_and_clip_surface(struct sway_view *view); |
320 | |||
321 | void view_child_init(struct sway_view_child *child, | ||
322 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
323 | struct wlr_surface *surface); | ||
324 | |||
325 | void view_child_destroy(struct sway_view_child *child); | ||
326 | |||
327 | 293 | ||
328 | struct sway_view *view_from_wlr_xdg_surface( | 294 | struct sway_view *view_from_wlr_xdg_surface( |
329 | struct wlr_xdg_surface *xdg_surface); | 295 | struct wlr_xdg_surface *xdg_surface); |
@@ -333,6 +299,8 @@ struct sway_view *view_from_wlr_xwayland_surface( | |||
333 | #endif | 299 | #endif |
334 | struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); | 300 | struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); |
335 | 301 | ||
302 | void view_update_app_id(struct sway_view *view); | ||
303 | |||
336 | /** | 304 | /** |
337 | * Re-read the view's title property and update any relevant title bars. | 305 | * Re-read the view's title property and update any relevant title bars. |
338 | * The force argument makes it recreate the title bars even if the title hasn't | 306 | * The force argument makes it recreate the title bars even if the title hasn't |
@@ -362,4 +330,8 @@ void view_save_buffer(struct sway_view *view); | |||
362 | 330 | ||
363 | bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); | 331 | bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); |
364 | 332 | ||
333 | void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx); | ||
334 | |||
335 | void view_send_frame_done(struct sway_view *view); | ||
336 | |||
365 | #endif | 337 | #endif |
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index b3d93a81..58bde20c 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h | |||
@@ -2,6 +2,8 @@ | |||
2 | #define _SWAY_WORKSPACE_H | 2 | #define _SWAY_WORKSPACE_H |
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <wlr/types/wlr_scene.h> | ||
6 | #include "sway/config.h" | ||
5 | #include "sway/tree/container.h" | 7 | #include "sway/tree/container.h" |
6 | #include "sway/tree/node.h" | 8 | #include "sway/tree/node.h" |
7 | 9 | ||
@@ -22,6 +24,12 @@ struct sway_workspace_state { | |||
22 | 24 | ||
23 | struct sway_workspace { | 25 | struct sway_workspace { |
24 | struct sway_node node; | 26 | struct sway_node node; |
27 | |||
28 | struct { | ||
29 | struct wlr_scene_tree *tiling; | ||
30 | struct wlr_scene_tree *fullscreen; | ||
31 | } layers; | ||
32 | |||
25 | struct sway_container *fullscreen; | 33 | struct sway_container *fullscreen; |
26 | 34 | ||
27 | char *name; | 35 | char *name; |
diff --git a/include/sway/xdg_decoration.h b/include/sway/xdg_decoration.h index 8bef4c6d..2388ebcb 100644 --- a/include/sway/xdg_decoration.h +++ b/include/sway/xdg_decoration.h | |||
@@ -16,4 +16,6 @@ struct sway_xdg_decoration { | |||
16 | struct sway_xdg_decoration *xdg_decoration_from_surface( | 16 | struct sway_xdg_decoration *xdg_decoration_from_surface( |
17 | struct wlr_surface *surface); | 17 | struct wlr_surface *surface); |
18 | 18 | ||
19 | void set_xdg_decoration_mode(struct sway_xdg_decoration *deco); | ||
20 | |||
19 | #endif | 21 | #endif |
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 545a66a8..197d2190 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "config.h" | 4 | #include "config.h" |
5 | #include "input.h" | 5 | #include "input.h" |
6 | #include "pool-buffer.h" | 6 | #include "pool-buffer.h" |
7 | #include "cursor-shape-v1-client-protocol.h" | ||
7 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 8 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
8 | #include "xdg-output-unstable-v1-client-protocol.h" | 9 | #include "xdg-output-unstable-v1-client-protocol.h" |
9 | 10 | ||
@@ -30,6 +31,7 @@ struct swaybar { | |||
30 | struct wl_compositor *compositor; | 31 | struct wl_compositor *compositor; |
31 | struct zwlr_layer_shell_v1 *layer_shell; | 32 | struct zwlr_layer_shell_v1 *layer_shell; |
32 | struct zxdg_output_manager_v1 *xdg_output_manager; | 33 | struct zxdg_output_manager_v1 *xdg_output_manager; |
34 | struct wp_cursor_shape_manager_v1 *cursor_shape_manager; | ||
33 | struct wl_shm *shm; | 35 | struct wl_shm *shm; |
34 | 36 | ||
35 | struct swaybar_config *config; | 37 | struct swaybar_config *config; |
@@ -58,7 +60,6 @@ struct swaybar_output { | |||
58 | struct zxdg_output_v1 *xdg_output; | 60 | struct zxdg_output_v1 *xdg_output; |
59 | struct wl_surface *surface; | 61 | struct wl_surface *surface; |
60 | struct zwlr_layer_surface_v1 *layer_surface; | 62 | struct zwlr_layer_surface_v1 *layer_surface; |
61 | struct wl_region *input_region; | ||
62 | uint32_t wl_name; | 63 | uint32_t wl_name; |
63 | 64 | ||
64 | struct wl_list workspaces; // swaybar_workspace::link | 65 | struct wl_list workspaces; // swaybar_workspace::link |
diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 4cacd21a..361acd99 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h | |||
@@ -6,6 +6,7 @@ | |||
6 | #include "../include/config.h" | 6 | #include "../include/config.h" |
7 | #include "list.h" | 7 | #include "list.h" |
8 | #include "util.h" | 8 | #include "util.h" |
9 | #include <pango/pangocairo.h> | ||
9 | 10 | ||
10 | struct box_colors { | 11 | struct box_colors { |
11 | uint32_t border; | 12 | uint32_t border; |
@@ -28,7 +29,7 @@ struct swaybar_config { | |||
28 | char *status_command; | 29 | char *status_command; |
29 | bool pango_markup; | 30 | bool pango_markup; |
30 | uint32_t position; // zwlr_layer_surface_v1_anchor | 31 | uint32_t position; // zwlr_layer_surface_v1_anchor |
31 | char *font; | 32 | PangoFontDescription *font_description; |
32 | char *sep_symbol; | 33 | char *sep_symbol; |
33 | char *mode; | 34 | char *mode; |
34 | char *hidden_state; | 35 | char *hidden_state; |
diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h index df8cdd09..dced2a6c 100644 --- a/include/swaybar/i3bar.h +++ b/include/swaybar/i3bar.h | |||
@@ -19,6 +19,7 @@ struct i3bar_block { | |||
19 | // Airblader features | 19 | // Airblader features |
20 | uint32_t background; | 20 | uint32_t background; |
21 | uint32_t border; | 21 | uint32_t border; |
22 | bool border_set; | ||
22 | int border_top; | 23 | int border_top; |
23 | int border_bottom; | 24 | int border_bottom; |
24 | int border_left; | 25 | int border_left; |
@@ -29,6 +30,6 @@ void i3bar_block_unref(struct i3bar_block *block); | |||
29 | bool i3bar_handle_readable(struct status_line *status); | 30 | bool i3bar_handle_readable(struct status_line *status); |
30 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, | 31 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, |
31 | struct i3bar_block *block, double x, double y, double rx, double ry, | 32 | struct i3bar_block *block, double x, double y, double rx, double ry, |
32 | double w, double h, int scale, uint32_t button); | 33 | double w, double h, int scale, uint32_t button, bool released); |
33 | 34 | ||
34 | #endif | 35 | #endif |
diff --git a/include/swaybar/image.h b/include/swaybar/image.h new file mode 100644 index 00000000..53a210dd --- /dev/null +++ b/include/swaybar/image.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef _SWAYBAR_IMAGE_H | ||
2 | #define _SWAYBAR_IMAGE_H | ||
3 | #include <cairo.h> | ||
4 | |||
5 | cairo_surface_t *load_image(const char *path); | ||
6 | |||
7 | #endif | ||
diff --git a/include/swaybar/input.h b/include/swaybar/input.h index e8735d88..8ea88a69 100644 --- a/include/swaybar/input.h +++ b/include/swaybar/input.h | |||
@@ -49,7 +49,7 @@ struct swaybar_hotspot { | |||
49 | int x, y, width, height; | 49 | int x, y, width, height; |
50 | enum hotspot_event_handling (*callback)(struct swaybar_output *output, | 50 | enum hotspot_event_handling (*callback)(struct swaybar_output *output, |
51 | struct swaybar_hotspot *hotspot, double x, double y, uint32_t button, | 51 | struct swaybar_hotspot *hotspot, double x, double y, uint32_t button, |
52 | void *data); | 52 | bool released, void *data); |
53 | void (*destroy)(void *data); | 53 | void (*destroy)(void *data); |
54 | void *data; | 54 | void *data; |
55 | }; | 55 | }; |
diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h index c02a5582..73937a0c 100644 --- a/include/swaybar/tray/item.h +++ b/include/swaybar/tray/item.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <cairo.h> | 4 | #include <cairo.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <stdint.h> | 6 | #include <stdint.h> |
7 | #include <wayland-util.h> | ||
7 | #include "swaybar/tray/tray.h" | 8 | #include "swaybar/tray/tray.h" |
8 | #include "list.h" | 9 | #include "list.h" |
9 | 10 | ||
diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h index 9e39e716..fb9e9c21 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h | |||
@@ -4,8 +4,9 @@ | |||
4 | #include <strings.h> | 4 | #include <strings.h> |
5 | #include "list.h" | 5 | #include "list.h" |
6 | #include "pool-buffer.h" | 6 | #include "pool-buffer.h" |
7 | #include "cursor-shape-v1-client-protocol.h" | ||
8 | |||
7 | #include "swaynag/types.h" | 9 | #include "swaynag/types.h" |
8 | #include "xdg-output-unstable-v1-client-protocol.h" | ||
9 | 10 | ||
10 | #define SWAYNAG_MAX_HEIGHT 500 | 11 | #define SWAYNAG_MAX_HEIGHT 500 |
11 | 12 | ||
@@ -59,6 +60,7 @@ struct swaynag_button { | |||
59 | struct swaynag_details { | 60 | struct swaynag_details { |
60 | bool visible; | 61 | bool visible; |
61 | char *message; | 62 | char *message; |
63 | char *details_text; | ||
62 | 64 | ||
63 | int x; | 65 | int x; |
64 | int y; | 66 | int y; |
@@ -75,18 +77,17 @@ struct swaynag_details { | |||
75 | 77 | ||
76 | struct swaynag { | 78 | struct swaynag { |
77 | bool run_display; | 79 | bool run_display; |
78 | int querying_outputs; | ||
79 | 80 | ||
80 | struct wl_display *display; | 81 | struct wl_display *display; |
81 | struct wl_compositor *compositor; | 82 | struct wl_compositor *compositor; |
82 | struct wl_seat *seat; | 83 | struct wl_seat *seat; |
83 | struct wl_shm *shm; | 84 | struct wl_shm *shm; |
84 | struct zxdg_output_manager_v1 *xdg_output_manager; | ||
85 | struct wl_list outputs; // swaynag_output::link | 85 | struct wl_list outputs; // swaynag_output::link |
86 | struct wl_list seats; // swaynag_seat::link | 86 | struct wl_list seats; // swaynag_seat::link |
87 | struct swaynag_output *output; | 87 | struct swaynag_output *output; |
88 | struct zwlr_layer_shell_v1 *layer_shell; | 88 | struct zwlr_layer_shell_v1 *layer_shell; |
89 | struct zwlr_layer_surface_v1 *layer_surface; | 89 | struct zwlr_layer_surface_v1 *layer_surface; |
90 | struct wp_cursor_shape_manager_v1 *cursor_shape_manager; | ||
90 | struct wl_surface *surface; | 91 | struct wl_surface *surface; |
91 | 92 | ||
92 | uint32_t width; | 93 | uint32_t width; |
diff --git a/include/swaynag/types.h b/include/swaynag/types.h index 3c3b2754..9c3c50db 100644 --- a/include/swaynag/types.h +++ b/include/swaynag/types.h | |||
@@ -1,10 +1,14 @@ | |||
1 | #ifndef _SWAYNAG_TYPES_H | 1 | #ifndef _SWAYNAG_TYPES_H |
2 | #define _SWAYNAG_TYPES_H | 2 | #define _SWAYNAG_TYPES_H |
3 | 3 | ||
4 | #include <stdint.h> | ||
5 | #include <pango/pangocairo.h> | ||
6 | #include "list.h" | ||
7 | |||
4 | struct swaynag_type { | 8 | struct swaynag_type { |
5 | char *name; | 9 | char *name; |
6 | 10 | ||
7 | char *font; | 11 | PangoFontDescription *font_description; |
8 | char *output; | 12 | char *output; |
9 | uint32_t anchors; | 13 | uint32_t anchors; |
10 | int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset | 14 | int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset |
diff --git a/meson.build b/meson.build index 756857b7..1043e4ba 100644 --- a/meson.build +++ b/meson.build | |||
@@ -1,9 +1,9 @@ | |||
1 | project( | 1 | project( |
2 | 'sway', | 2 | 'sway', |
3 | 'c', | 3 | 'c', |
4 | version: '1.6', | 4 | version: '1.10-dev', |
5 | license: 'MIT', | 5 | license: 'MIT', |
6 | meson_version: '>=0.58.1', | 6 | meson_version: '>=0.60.0', |
7 | default_options: [ | 7 | default_options: [ |
8 | 'c_std=c11', | 8 | 'c_std=c11', |
9 | 'warning_level=2', | 9 | 'warning_level=2', |
@@ -14,10 +14,12 @@ project( | |||
14 | add_project_arguments( | 14 | add_project_arguments( |
15 | [ | 15 | [ |
16 | '-DWLR_USE_UNSTABLE', | 16 | '-DWLR_USE_UNSTABLE', |
17 | '-D_POSIX_C_SOURCE=200809L', | ||
17 | 18 | ||
18 | '-Wno-unused-parameter', | 19 | '-Wno-unused-parameter', |
19 | '-Wno-unused-result', | 20 | '-Wno-unused-result', |
20 | '-Wno-missing-braces', | 21 | '-Wno-missing-braces', |
22 | '-Wno-format-zero-length', | ||
21 | '-Wundef', | 23 | '-Wundef', |
22 | '-Wvla', | 24 | '-Wvla', |
23 | ], | 25 | ], |
@@ -35,47 +37,19 @@ if is_freebsd | |||
35 | add_project_arguments('-D_C11_SOURCE', language: 'c') | 37 | add_project_arguments('-D_C11_SOURCE', language: 'c') |
36 | endif | 38 | endif |
37 | 39 | ||
38 | jsonc = dependency('json-c', version: '>=0.13') | 40 | # Execute the wlroots subproject, if any |
39 | pcre = dependency('libpcre') | 41 | wlroots_version = ['>=0.18.0', '<0.19.0'] |
40 | wayland_server = dependency('wayland-server') | 42 | subproject( |
41 | wayland_client = dependency('wayland-client') | ||
42 | wayland_cursor = dependency('wayland-cursor') | ||
43 | wayland_egl = dependency('wayland-egl') | ||
44 | wayland_protos = dependency('wayland-protocols', version: '>=1.14') | ||
45 | xkbcommon = dependency('xkbcommon') | ||
46 | cairo = dependency('cairo') | ||
47 | pango = dependency('pango') | ||
48 | pangocairo = dependency('pangocairo') | ||
49 | gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) | ||
50 | pixman = dependency('pixman-1') | ||
51 | glesv2 = dependency('glesv2') | ||
52 | libevdev = dependency('libevdev') | ||
53 | libinput = dependency('libinput', version: '>=1.6.0') | ||
54 | xcb = dependency('xcb', required: get_option('xwayland')) | ||
55 | drm_full = dependency('libdrm') # only needed for drm_fourcc.h | ||
56 | drm = drm_full.partial_dependency(compile_args: true, includes: true) | ||
57 | libudev = dependency('libudev') | ||
58 | bash_comp = dependency('bash-completion', required: false) | ||
59 | fish_comp = dependency('fish', required: false) | ||
60 | math = cc.find_library('m') | ||
61 | rt = cc.find_library('rt') | ||
62 | |||
63 | # Try first to find wlroots as a subproject, then as a system dependency | ||
64 | wlroots_version = ['>=0.15.0', '<0.16.0'] | ||
65 | wlroots_proj = subproject( | ||
66 | 'wlroots', | 43 | 'wlroots', |
67 | default_options: ['examples=false'], | 44 | default_options: ['examples=false'], |
68 | required: false, | 45 | required: false, |
69 | version: wlroots_version, | 46 | version: wlroots_version, |
70 | ) | 47 | ) |
71 | if wlroots_proj.found() | 48 | wlroots = dependency('wlroots', version: wlroots_version) |
72 | wlroots = wlroots_proj.get_variable('wlroots') | ||
73 | else | ||
74 | wlroots = dependency('wlroots', version: wlroots_version) | ||
75 | endif | ||
76 | |||
77 | wlroots_features = { | 49 | wlroots_features = { |
78 | 'xwayland': false, | 50 | 'xwayland': false, |
51 | 'libinput_backend': false, | ||
52 | 'session': false, | ||
79 | } | 53 | } |
80 | foreach name, _ : wlroots_features | 54 | foreach name, _ : wlroots_features |
81 | var_name = 'have_' + name.underscorify() | 55 | var_name = 'have_' + name.underscorify() |
@@ -86,25 +60,42 @@ endforeach | |||
86 | if get_option('xwayland').enabled() and not wlroots_features['xwayland'] | 60 | if get_option('xwayland').enabled() and not wlroots_features['xwayland'] |
87 | error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') | 61 | error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') |
88 | endif | 62 | endif |
89 | have_xwayland = xcb.found() and wlroots_features['xwayland'] | 63 | |
64 | null_dep = dependency('', required: false) | ||
65 | |||
66 | jsonc = dependency('json-c', version: '>=0.13') | ||
67 | pcre2 = dependency('libpcre2-8') | ||
68 | wayland_server = dependency('wayland-server', version: '>=1.21.0') | ||
69 | wayland_client = dependency('wayland-client') | ||
70 | wayland_cursor = dependency('wayland-cursor') | ||
71 | wayland_protos = dependency('wayland-protocols', version: '>=1.24') | ||
72 | xkbcommon = dependency('xkbcommon', version: '>=1.5.0') | ||
73 | cairo = dependency('cairo') | ||
74 | pango = dependency('pango') | ||
75 | pangocairo = dependency('pangocairo') | ||
76 | gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) | ||
77 | pixman = dependency('pixman-1') | ||
78 | libevdev = dependency('libevdev') | ||
79 | libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.21.0') : null_dep | ||
80 | xcb = dependency('xcb', required: get_option('xwayland')) | ||
81 | drm = dependency('libdrm') | ||
82 | libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep | ||
83 | math = cc.find_library('m') | ||
84 | rt = cc.find_library('rt') | ||
85 | xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) | ||
86 | threads = dependency('threads') # for pthread_setschedparam | ||
87 | |||
88 | have_xwayland = xcb.found() and xcb_icccm.found() and wlroots_features['xwayland'] | ||
90 | 89 | ||
91 | if get_option('sd-bus-provider') == 'auto' | 90 | if get_option('sd-bus-provider') == 'auto' |
92 | if not get_option('tray').disabled() | 91 | if not get_option('tray').disabled() |
93 | assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto') | 92 | assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto') |
94 | endif | 93 | endif |
95 | sdbus = dependency('libsystemd', | 94 | sdbus = dependency(['libsystemd', 'libelogind'], |
96 | required: false, | 95 | required: false, |
97 | version: '>=239', | 96 | version: '>=239', |
98 | not_found_message: 'libsystemd not found, trying libelogind', | ||
99 | ) | 97 | ) |
100 | if not sdbus.found() | 98 | if not sdbus.found() |
101 | sdbus = dependency('libelogind', | ||
102 | required: false, | ||
103 | version: '>=239', | ||
104 | not_found_message: 'libelogind not found, trying basu', | ||
105 | ) | ||
106 | endif | ||
107 | if not sdbus.found() | ||
108 | sdbus = dependency('basu', required: false) | 99 | sdbus = dependency('basu', required: false) |
109 | endif | 100 | endif |
110 | else | 101 | else |
@@ -125,11 +116,15 @@ conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd | |||
125 | conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') | 116 | conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') |
126 | conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') | 117 | conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') |
127 | conf_data.set10('HAVE_TRAY', have_tray) | 118 | conf_data.set10('HAVE_TRAY', have_tray) |
119 | conf_data.set10('HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', cc.has_header_symbol( | ||
120 | 'libinput.h', | ||
121 | 'LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', | ||
122 | dependencies: libinput, | ||
123 | )) | ||
128 | 124 | ||
129 | scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) | 125 | scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) |
130 | if scdoc.found() | 126 | if scdoc.found() |
131 | scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true) | 127 | scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true) |
132 | sh = find_program('sh', native: true) | ||
133 | mandir = get_option('mandir') | 128 | mandir = get_option('mandir') |
134 | man_files = [ | 129 | man_files = [ |
135 | 'sway/sway.1.scd', | 130 | 'sway/sway.1.scd', |
@@ -158,10 +153,10 @@ if scdoc.found() | |||
158 | output, | 153 | output, |
159 | input: filename, | 154 | input: filename, |
160 | output: output, | 155 | output: output, |
161 | command: [ | 156 | command: scdoc_prog, |
162 | sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output) | ||
163 | ], | ||
164 | install: true, | 157 | install: true, |
158 | feed: true, | ||
159 | capture: true, | ||
165 | install_dir: '@0@/man@1@'.format(mandir, section) | 160 | install_dir: '@0@/man@1@'.format(mandir, section) |
166 | ) | 161 | ) |
167 | endforeach | 162 | endforeach |
@@ -172,8 +167,8 @@ add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir | |||
172 | version = '"@0@"'.format(meson.project_version()) | 167 | version = '"@0@"'.format(meson.project_version()) |
173 | git = find_program('git', native: true, required: false) | 168 | git = find_program('git', native: true, required: false) |
174 | if git.found() | 169 | if git.found() |
175 | git_commit = run_command([git, 'rev-parse', '--short', 'HEAD']) | 170 | git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false) |
176 | git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD']) | 171 | git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) |
177 | if git_commit.returncode() == 0 and git_branch.returncode() == 0 | 172 | if git_commit.returncode() == 0 and git_branch.returncode() == 0 |
178 | version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( | 173 | version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( |
179 | meson.project_version(), | 174 | meson.project_version(), |
@@ -273,59 +268,7 @@ if get_option('default-wallpaper') | |||
273 | install_data(wallpaper_files, install_dir: wallpaper_install_dir) | 268 | install_data(wallpaper_files, install_dir: wallpaper_install_dir) |
274 | endif | 269 | endif |
275 | 270 | ||
276 | if get_option('zsh-completions') | 271 | subdir('completions') |
277 | zsh_files = files( | ||
278 | 'completions/zsh/_sway', | ||
279 | 'completions/zsh/_swaymsg', | ||
280 | ) | ||
281 | zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions') | ||
282 | |||
283 | install_data(zsh_files, install_dir: zsh_install_dir) | ||
284 | endif | ||
285 | |||
286 | if get_option('bash-completions') | ||
287 | bash_files = files( | ||
288 | 'completions/bash/sway', | ||
289 | 'completions/bash/swaymsg', | ||
290 | ) | ||
291 | |||
292 | if get_option('swaybar') | ||
293 | bash_files += files('completions/bash/swaybar') | ||
294 | endif | ||
295 | |||
296 | if bash_comp.found() | ||
297 | bash_install_dir = bash_comp.get_variable( | ||
298 | pkgconfig: 'completionsdir', | ||
299 | pkgconfig_define: ['datadir', datadir] | ||
300 | ) | ||
301 | else | ||
302 | bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') | ||
303 | endif | ||
304 | |||
305 | install_data(bash_files, install_dir: bash_install_dir) | ||
306 | endif | ||
307 | |||
308 | if get_option('fish-completions') | ||
309 | fish_files = files( | ||
310 | 'completions/fish/sway.fish', | ||
311 | 'completions/fish/swaymsg.fish', | ||
312 | ) | ||
313 | |||
314 | if get_option('swaynag') | ||
315 | fish_files += files('completions/fish/swaynag.fish') | ||
316 | endif | ||
317 | |||
318 | if fish_comp.found() | ||
319 | fish_install_dir = fish_comp.get_variable( | ||
320 | pkgconfig: 'completionsdir', | ||
321 | pkgconfig_define: ['datadir', datadir] | ||
322 | ) | ||
323 | else | ||
324 | fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') | ||
325 | endif | ||
326 | |||
327 | install_data(fish_files, install_dir: fish_install_dir) | ||
328 | endif | ||
329 | 272 | ||
330 | summary({ | 273 | summary({ |
331 | 'xwayland': have_xwayland, | 274 | 'xwayland': have_xwayland, |
@@ -333,4 +276,3 @@ summary({ | |||
333 | 'tray': have_tray, | 276 | 'tray': have_tray, |
334 | 'man-pages': scdoc.found(), | 277 | 'man-pages': scdoc.found(), |
335 | }, bool_yn: true) | 278 | }, bool_yn: true) |
336 | |||
diff --git a/meson_options.txt b/meson_options.txt index 6ba67554..8d0d6509 100644 --- a/meson_options.txt +++ b/meson_options.txt | |||
@@ -6,6 +6,6 @@ option('swaybar', type: 'boolean', value: true, description: 'Enable support for | |||
6 | option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag') | 6 | option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag') |
7 | option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') | 7 | option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') |
8 | option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray') | 8 | option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray') |
9 | option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybg') | 9 | option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray') |
10 | option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') | 10 | option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') |
11 | option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library') | 11 | option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library') |
diff --git a/protocols/meson.build b/protocols/meson.build index 8c4ae0af..81edb584 100644 --- a/protocols/meson.build +++ b/protocols/meson.build | |||
@@ -1,80 +1,43 @@ | |||
1 | wl_protocol_dir = wayland_protos.get_variable(pkgconfig: 'pkgdatadir') | 1 | wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') |
2 | 2 | ||
3 | wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true) | 3 | wayland_scanner_dep = dependency('wayland-scanner', native: true) |
4 | if wayland_scanner_dep.found() | 4 | wayland_scanner = find_program( |
5 | wayland_scanner = find_program( | 5 | wayland_scanner_dep.get_variable('wayland_scanner'), |
6 | wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'), | 6 | native: true, |
7 | native: true, | 7 | ) |
8 | ) | ||
9 | else | ||
10 | wayland_scanner = find_program('wayland-scanner', native: true) | ||
11 | endif | ||
12 | 8 | ||
13 | protocols = [ | 9 | protocols = [ |
14 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | 10 | wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', |
15 | [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], | 11 | wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', |
16 | [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], | 12 | wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', |
17 | [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'], | 13 | wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml', |
18 | ['wlr-layer-shell-unstable-v1.xml'], | 14 | wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', |
19 | ['idle.xml'], | 15 | wl_protocol_dir / 'staging/content-type/content-type-v1.xml', |
20 | ['wlr-input-inhibitor-unstable-v1.xml'], | 16 | wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', |
21 | ['wlr-output-power-management-unstable-v1.xml'], | 17 | 'wlr-layer-shell-unstable-v1.xml', |
22 | ] | 18 | 'idle.xml', |
23 | 19 | 'wlr-output-power-management-unstable-v1.xml', | |
24 | client_protocols = [ | ||
25 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | ||
26 | [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], | ||
27 | ['wlr-layer-shell-unstable-v1.xml'], | ||
28 | ['wlr-input-inhibitor-unstable-v1.xml'], | ||
29 | ] | 20 | ] |
30 | 21 | ||
31 | wl_protos_src = [] | 22 | wl_protos_src = [] |
32 | wl_protos_headers = [] | ||
33 | 23 | ||
34 | foreach p : protocols | 24 | foreach xml : protocols |
35 | xml = join_paths(p) | ||
36 | wl_protos_src += custom_target( | 25 | wl_protos_src += custom_target( |
37 | xml.underscorify() + '_server_c', | 26 | xml.underscorify() + '_c', |
38 | input: xml, | 27 | input: xml, |
39 | output: '@BASENAME@-protocol.c', | 28 | output: '@BASENAME@-protocol.c', |
40 | command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], | 29 | command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], |
41 | ) | 30 | ) |
42 | wl_protos_headers += custom_target( | 31 | wl_protos_src += custom_target( |
43 | xml.underscorify() + '_server_h', | 32 | xml.underscorify() + '_server_h', |
44 | input: xml, | 33 | input: xml, |
45 | output: '@BASENAME@-protocol.h', | 34 | output: '@BASENAME@-protocol.h', |
46 | command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], | 35 | command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], |
47 | ) | 36 | ) |
48 | endforeach | 37 | wl_protos_src += custom_target( |
49 | |||
50 | foreach p : client_protocols | ||
51 | xml = join_paths(p) | ||
52 | wl_protos_headers += custom_target( | ||
53 | xml.underscorify() + '_client_h', | 38 | xml.underscorify() + '_client_h', |
54 | input: xml, | 39 | input: xml, |
55 | output: '@BASENAME@-client-protocol.h', | 40 | output: '@BASENAME@-client-protocol.h', |
56 | command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], | 41 | command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], |
57 | ) | 42 | ) |
58 | endforeach | 43 | endforeach |
59 | |||
60 | lib_client_protos = static_library( | ||
61 | 'client_protos', | ||
62 | wl_protos_src + wl_protos_headers, | ||
63 | dependencies: wayland_client.partial_dependency(compile_args: true), | ||
64 | ) | ||
65 | |||
66 | client_protos = declare_dependency( | ||
67 | link_with: lib_client_protos, | ||
68 | sources: wl_protos_headers, | ||
69 | ) | ||
70 | |||
71 | lib_server_protos = static_library( | ||
72 | 'server_protos', | ||
73 | wl_protos_src + wl_protos_headers, | ||
74 | dependencies: wayland_server.partial_dependency(compile_args: true), | ||
75 | ) | ||
76 | |||
77 | server_protos = declare_dependency( | ||
78 | link_with: lib_server_protos, | ||
79 | sources: wl_protos_headers, | ||
80 | ) | ||
diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml deleted file mode 100644 index b62d1bb4..00000000 --- a/protocols/wlr-input-inhibitor-unstable-v1.xml +++ /dev/null | |||
@@ -1,67 +0,0 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8"?> | ||
2 | <protocol name="wlr_input_inhibit_unstable_v1"> | ||
3 | <copyright> | ||
4 | Copyright © 2018 Drew DeVault | ||
5 | |||
6 | Permission to use, copy, modify, distribute, and sell this | ||
7 | software and its documentation for any purpose is hereby granted | ||
8 | without fee, provided that the above copyright notice appear in | ||
9 | all copies and that both that copyright notice and this permission | ||
10 | notice appear in supporting documentation, and that the name of | ||
11 | the copyright holders not be used in advertising or publicity | ||
12 | pertaining to distribution of the software without specific, | ||
13 | written prior permission. The copyright holders make no | ||
14 | representations about the suitability of this software for any | ||
15 | purpose. It is provided "as is" without express or implied | ||
16 | warranty. | ||
17 | |||
18 | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | ||
19 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||
20 | FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
21 | SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
22 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN | ||
23 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | ||
24 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF | ||
25 | THIS SOFTWARE. | ||
26 | </copyright> | ||
27 | |||
28 | <interface name="zwlr_input_inhibit_manager_v1" version="1"> | ||
29 | <description summary="inhibits input events to other clients"> | ||
30 | Clients can use this interface to prevent input events from being sent to | ||
31 | any surfaces but its own, which is useful for example in lock screen | ||
32 | software. It is assumed that access to this interface will be locked down | ||
33 | to whitelisted clients by the compositor. | ||
34 | </description> | ||
35 | |||
36 | <request name="get_inhibitor"> | ||
37 | <description summary="inhibit input to other clients"> | ||
38 | Activates the input inhibitor. As long as the inhibitor is active, the | ||
39 | compositor will not send input events to other clients. | ||
40 | </description> | ||
41 | <arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/> | ||
42 | </request> | ||
43 | |||
44 | <enum name="error"> | ||
45 | <entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/> | ||
46 | </enum> | ||
47 | </interface> | ||
48 | |||
49 | <interface name="zwlr_input_inhibitor_v1" version="1"> | ||
50 | <description summary="inhibits input to other clients"> | ||
51 | While this resource exists, input to clients other than the owner of the | ||
52 | inhibitor resource will not receive input events. The client that owns | ||
53 | this resource will receive all input events normally. The compositor will | ||
54 | also disable all of its own input processing (such as keyboard shortcuts) | ||
55 | while the inhibitor is active. | ||
56 | |||
57 | The compositor may continue to send input events to selected clients, | ||
58 | such as an on-screen keyboard (via the input-method protocol). | ||
59 | </description> | ||
60 | |||
61 | <request name="destroy" type="destructor"> | ||
62 | <description summary="destroy the input inhibitor object"> | ||
63 | Destroy the inhibitor and allow other clients to receive input. | ||
64 | </description> | ||
65 | </request> | ||
66 | </interface> | ||
67 | </protocol> | ||
diff --git a/release.sh b/release.sh new file mode 100755 index 00000000..62baf415 --- /dev/null +++ b/release.sh | |||
@@ -0,0 +1,31 @@ | |||
1 | #!/bin/sh -eu | ||
2 | |||
3 | prev=$(git describe --tags --abbrev=0) | ||
4 | next=$(meson rewrite kwargs info project / 2>&1 >/dev/null | jq -r '.kwargs["project#/"].version') | ||
5 | |||
6 | case "$next" in | ||
7 | *-dev) | ||
8 | echo "This is a development version" | ||
9 | exit 1 | ||
10 | ;; | ||
11 | esac | ||
12 | |||
13 | if [ "$prev" = "$next" ]; then | ||
14 | echo "Version not bumped in meson.build" | ||
15 | exit 1 | ||
16 | fi | ||
17 | |||
18 | if ! git diff-index --quiet HEAD -- meson.build; then | ||
19 | echo "meson.build not committed" | ||
20 | exit 1 | ||
21 | fi | ||
22 | |||
23 | shortlog="$(git shortlog --no-merges "$prev..")" | ||
24 | (echo "sway $next"; echo ""; echo "$shortlog") | git tag "$next" -ase -F - | ||
25 | |||
26 | prefix=sway-$next | ||
27 | archive=$prefix.tar.gz | ||
28 | git archive --prefix="$prefix/" -o "$archive" "$next" | ||
29 | gpg --output "$archive".sig --detach-sig "$archive" | ||
30 | |||
31 | gh release create "sway $next" -t "$next" -n "" -d "$archive" "$archive.sig" | ||
diff --git a/sway/commands.c b/sway/commands.c index b09a04c7..8d003dfa 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809 | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <stdarg.h> | 2 | #include <stdarg.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -46,11 +45,13 @@ static const struct cmd_handler handlers[] = { | |||
46 | { "assign", cmd_assign }, | 45 | { "assign", cmd_assign }, |
47 | { "bar", cmd_bar }, | 46 | { "bar", cmd_bar }, |
48 | { "bindcode", cmd_bindcode }, | 47 | { "bindcode", cmd_bindcode }, |
48 | { "bindgesture", cmd_bindgesture }, | ||
49 | { "bindswitch", cmd_bindswitch }, | 49 | { "bindswitch", cmd_bindswitch }, |
50 | { "bindsym", cmd_bindsym }, | 50 | { "bindsym", cmd_bindsym }, |
51 | { "client.background", cmd_client_noop }, | 51 | { "client.background", cmd_client_noop }, |
52 | { "client.focused", cmd_client_focused }, | 52 | { "client.focused", cmd_client_focused }, |
53 | { "client.focused_inactive", cmd_client_focused_inactive }, | 53 | { "client.focused_inactive", cmd_client_focused_inactive }, |
54 | { "client.focused_tab_title", cmd_client_focused_tab_title }, | ||
54 | { "client.placeholder", cmd_client_noop }, | 55 | { "client.placeholder", cmd_client_noop }, |
55 | { "client.unfocused", cmd_client_unfocused }, | 56 | { "client.unfocused", cmd_client_unfocused }, |
56 | { "client.urgent", cmd_client_urgent }, | 57 | { "client.urgent", cmd_client_urgent }, |
@@ -91,6 +92,7 @@ static const struct cmd_handler handlers[] = { | |||
91 | { "titlebar_border_thickness", cmd_titlebar_border_thickness }, | 92 | { "titlebar_border_thickness", cmd_titlebar_border_thickness }, |
92 | { "titlebar_padding", cmd_titlebar_padding }, | 93 | { "titlebar_padding", cmd_titlebar_padding }, |
93 | { "unbindcode", cmd_unbindcode }, | 94 | { "unbindcode", cmd_unbindcode }, |
95 | { "unbindgesture", cmd_unbindgesture }, | ||
94 | { "unbindswitch", cmd_unbindswitch }, | 96 | { "unbindswitch", cmd_unbindswitch }, |
95 | { "unbindsym", cmd_unbindsym }, | 97 | { "unbindsym", cmd_unbindsym }, |
96 | { "workspace", cmd_workspace }, | 98 | { "workspace", cmd_workspace }, |
@@ -101,6 +103,7 @@ static const struct cmd_handler handlers[] = { | |||
101 | static const struct cmd_handler config_handlers[] = { | 103 | static const struct cmd_handler config_handlers[] = { |
102 | { "default_orientation", cmd_default_orientation }, | 104 | { "default_orientation", cmd_default_orientation }, |
103 | { "include", cmd_include }, | 105 | { "include", cmd_include }, |
106 | { "primary_selection", cmd_primary_selection }, | ||
104 | { "swaybg_command", cmd_swaybg_command }, | 107 | { "swaybg_command", cmd_swaybg_command }, |
105 | { "swaynag_command", cmd_swaynag_command }, | 108 | { "swaynag_command", cmd_swaynag_command }, |
106 | { "workspace_layout", cmd_workspace_layout }, | 109 | { "workspace_layout", cmd_workspace_layout }, |
@@ -144,7 +147,7 @@ static int handler_compare(const void *_a, const void *_b) { | |||
144 | return strcasecmp(a->command, b->command); | 147 | return strcasecmp(a->command, b->command); |
145 | } | 148 | } |
146 | 149 | ||
147 | const struct cmd_handler *find_handler(char *line, | 150 | const struct cmd_handler *find_handler(const char *line, |
148 | const struct cmd_handler *handlers, size_t handlers_size) { | 151 | const struct cmd_handler *handlers, size_t handlers_size) { |
149 | if (!handlers || !handlers_size) { | 152 | if (!handlers || !handlers_size) { |
150 | return NULL; | 153 | return NULL; |
@@ -377,10 +380,13 @@ struct cmd_results *config_command(char *exec, char **new_block) { | |||
377 | sway_log(SWAY_INFO, "Config command: %s", exec); | 380 | sway_log(SWAY_INFO, "Config command: %s", exec); |
378 | const struct cmd_handler *handler = find_core_handler(argv[0]); | 381 | const struct cmd_handler *handler = find_core_handler(argv[0]); |
379 | if (!handler || !handler->handle) { | 382 | if (!handler || !handler->handle) { |
380 | const char *error = handler | 383 | if (handler) { |
381 | ? "Command '%s' is shimmed, but unimplemented" | 384 | results = cmd_results_new(CMD_INVALID, |
382 | : "Unknown/invalid command '%s'"; | 385 | "Command '%s' is shimmed, but unimplemented", argv[0]); |
383 | results = cmd_results_new(CMD_INVALID, error, argv[0]); | 386 | } else { |
387 | results = cmd_results_new(CMD_INVALID, | ||
388 | "Unknown/invalid command '%s'", argv[0]); | ||
389 | } | ||
384 | goto cleanup; | 390 | goto cleanup; |
385 | } | 391 | } |
386 | 392 | ||
@@ -406,6 +412,7 @@ struct cmd_results *config_command(char *exec, char **new_block) { | |||
406 | && handler->handle != cmd_bindsym | 412 | && handler->handle != cmd_bindsym |
407 | && handler->handle != cmd_bindcode | 413 | && handler->handle != cmd_bindcode |
408 | && handler->handle != cmd_bindswitch | 414 | && handler->handle != cmd_bindswitch |
415 | && handler->handle != cmd_bindgesture | ||
409 | && handler->handle != cmd_set | 416 | && handler->handle != cmd_set |
410 | && handler->handle != cmd_for_window | 417 | && handler->handle != cmd_for_window |
411 | && (*argv[i] == '\"' || *argv[i] == '\'')) { | 418 | && (*argv[i] == '\"' || *argv[i] == '\'')) { |
@@ -465,34 +472,6 @@ struct cmd_results *config_commands_command(char *exec) { | |||
465 | goto cleanup; | 472 | goto cleanup; |
466 | } | 473 | } |
467 | 474 | ||
468 | enum command_context context = 0; | ||
469 | |||
470 | struct { | ||
471 | char *name; | ||
472 | enum command_context context; | ||
473 | } context_names[] = { | ||
474 | { "config", CONTEXT_CONFIG }, | ||
475 | { "binding", CONTEXT_BINDING }, | ||
476 | { "ipc", CONTEXT_IPC }, | ||
477 | { "criteria", CONTEXT_CRITERIA }, | ||
478 | { "all", CONTEXT_ALL }, | ||
479 | }; | ||
480 | |||
481 | for (int i = 1; i < argc; ++i) { | ||
482 | size_t j; | ||
483 | for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) { | ||
484 | if (strcmp(context_names[j].name, argv[i]) == 0) { | ||
485 | break; | ||
486 | } | ||
487 | } | ||
488 | if (j == sizeof(context_names) / sizeof(context_names[0])) { | ||
489 | results = cmd_results_new(CMD_INVALID, | ||
490 | "Invalid command context %s", argv[i]); | ||
491 | goto cleanup; | ||
492 | } | ||
493 | context |= context_names[j].context; | ||
494 | } | ||
495 | |||
496 | results = cmd_results_new(CMD_SUCCESS, NULL); | 475 | results = cmd_results_new(CMD_SUCCESS, NULL); |
497 | 476 | ||
498 | cleanup: | 477 | cleanup: |
@@ -509,14 +488,10 @@ struct cmd_results *cmd_results_new(enum cmd_status status, | |||
509 | } | 488 | } |
510 | results->status = status; | 489 | results->status = status; |
511 | if (format) { | 490 | if (format) { |
512 | char *error = malloc(256); | ||
513 | va_list args; | 491 | va_list args; |
514 | va_start(args, format); | 492 | va_start(args, format); |
515 | if (error) { | 493 | results->error = vformat_str(format, args); |
516 | vsnprintf(error, 256, format, args); | ||
517 | } | ||
518 | va_end(args); | 494 | va_end(args); |
519 | results->error = error; | ||
520 | } else { | 495 | } else { |
521 | results->error = NULL; | 496 | results->error = NULL; |
522 | } | 497 | } |
diff --git a/sway/commands/assign.c b/sway/commands/assign.c index 976bc3cc..bf95cf00 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -17,7 +16,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) { | |||
17 | char *err_str = NULL; | 16 | char *err_str = NULL; |
18 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | 17 | struct criteria *criteria = criteria_parse(argv[0], &err_str); |
19 | if (!criteria) { | 18 | if (!criteria) { |
20 | error = cmd_results_new(CMD_INVALID, err_str); | 19 | error = cmd_results_new(CMD_INVALID, "%s", err_str); |
21 | free(err_str); | 20 | free(err_str); |
22 | return error; | 21 | return error; |
23 | } | 22 | } |
diff --git a/sway/commands/bar.c b/sway/commands/bar.c index 8571d282..635e895b 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809 | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
@@ -73,12 +72,10 @@ struct cmd_results *cmd_bar(int argc, char **argv) { | |||
73 | } | 72 | } |
74 | ++argv; --argc; | 73 | ++argv; --argc; |
75 | } else if (config->reading && !config->current_bar) { | 74 | } else if (config->reading && !config->current_bar) { |
76 | int len = snprintf(NULL, 0, "bar-%d", config->bars->length) + 1; | 75 | id = format_str("bar-%d", config->bars->length); |
77 | id = malloc(len * sizeof(char)); | ||
78 | if (!id) { | 76 | if (!id) { |
79 | return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id"); | 77 | return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id"); |
80 | } | 78 | } |
81 | snprintf(id, len, "bar-%d", config->bars->length); | ||
82 | } else if (!config->reading && strcmp(argv[0], "mode") != 0 && | 79 | } else if (!config->reading && strcmp(argv[0], "mode") != 0 && |
83 | strcmp(argv[0], "hidden_state") != 0) { | 80 | strcmp(argv[0], "hidden_state") != 0) { |
84 | if (is_subcommand(argv[0])) { | 81 | if (is_subcommand(argv[0])) { |
diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c index b4b5bc45..8a837e3f 100644 --- a/sway/commands/bar/bind.c +++ b/sway/commands/bar/bind.c | |||
@@ -96,7 +96,7 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code, | |||
96 | } | 96 | } |
97 | if (message) { | 97 | if (message) { |
98 | free_bar_binding(binding); | 98 | free_bar_binding(binding); |
99 | error = cmd_results_new(CMD_INVALID, message); | 99 | error = cmd_results_new(CMD_INVALID, "%s", message); |
100 | free(message); | 100 | free(message); |
101 | return error; | 101 | return error; |
102 | } else if (!binding->button) { | 102 | } else if (!binding->button) { |
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index 891c87af..0c074679 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c index 1f08a5d2..7b38831e 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -54,7 +53,7 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { | |||
54 | } | 53 | } |
55 | 54 | ||
56 | const char *state = argv[0]; | 55 | const char *state = argv[0]; |
57 | if (config->reading) { | 56 | if (config->current_bar) { |
58 | error = bar_set_hidden_state(config->current_bar, state); | 57 | error = bar_set_hidden_state(config->current_bar, state); |
59 | } else { | 58 | } else { |
60 | const char *id = argc == 2 ? argv[1] : NULL; | 59 | const char *id = argc == 2 ? argv[1] : NULL; |
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c index 6ac07843..fee21709 100644 --- a/sway/commands/bar/icon_theme.c +++ b/sway/commands/bar/icon_theme.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "config.h" | 2 | #include "config.h" |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c index a9a61743..46cf4ca9 100644 --- a/sway/commands/bar/id.c +++ b/sway/commands/bar/id.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 8b3fb275..d69e910b 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -58,7 +57,7 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) { | |||
58 | } | 57 | } |
59 | 58 | ||
60 | const char *mode = argv[0]; | 59 | const char *mode = argv[0]; |
61 | if (config->reading) { | 60 | if (config->current_bar) { |
62 | error = bar_set_mode(config->current_bar, mode); | 61 | error = bar_set_mode(config->current_bar, mode); |
63 | } else { | 62 | } else { |
64 | const char *id = argc == 2 ? argv[1] : NULL; | 63 | const char *id = argc == 2 ? argv[1] : NULL; |
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c index cac1d056..51730176 100644 --- a/sway/commands/bar/output.c +++ b/sway/commands/bar/output.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c index b207de0b..94f530ec 100644 --- a/sway/commands/bar/position.c +++ b/sway/commands/bar/position.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c index 6737d4d2..50e9a873 100644 --- a/sway/commands/bar/separator_symbol.c +++ b/sway/commands/bar/separator_symbol.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/bar/tray_bind.c b/sway/commands/bar/tray_bind.c index 243834ba..3dc9bc4c 100644 --- a/sway/commands/bar/tray_bind.c +++ b/sway/commands/bar/tray_bind.c | |||
@@ -26,7 +26,7 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) { | |||
26 | } | 26 | } |
27 | if (message) { | 27 | if (message) { |
28 | free(binding); | 28 | free(binding); |
29 | error = cmd_results_new(CMD_INVALID, message); | 29 | error = cmd_results_new(CMD_INVALID, "%s", message); |
30 | free(message); | 30 | free(message); |
31 | return error; | 31 | return error; |
32 | } else if (!binding->button) { | 32 | } else if (!binding->button) { |
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c index eb3b486e..679facf7 100644 --- a/sway/commands/bar/tray_output.c +++ b/sway/commands/bar/tray_output.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "config.h" | 2 | #include "config.h" |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 4c67b3ce..268f2855 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <libevdev/libevdev.h> | 1 | #include <libevdev/libevdev.h> |
3 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -47,7 +46,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a, | |||
47 | if (binding_a->type != binding_b->type) { | 46 | if (binding_a->type != binding_b->type) { |
48 | return false; | 47 | return false; |
49 | } | 48 | } |
50 | if (binding_a->state != binding_b->state) { | 49 | if (binding_a->trigger != binding_b->trigger) { |
51 | return false; | 50 | return false; |
52 | } | 51 | } |
53 | if ((binding_a->flags & BINDING_LOCKED) != | 52 | if ((binding_a->flags & BINDING_LOCKED) != |
@@ -127,7 +126,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, | |||
127 | if (!button) { | 126 | if (!button) { |
128 | if (message) { | 127 | if (message) { |
129 | struct cmd_results *error = | 128 | struct cmd_results *error = |
130 | cmd_results_new(CMD_INVALID, message); | 129 | cmd_results_new(CMD_INVALID, "%s", message); |
131 | free(message); | 130 | free(message); |
132 | return error; | 131 | return error; |
133 | } else { | 132 | } else { |
@@ -143,7 +142,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, | |||
143 | if (!button) { | 142 | if (!button) { |
144 | if (message) { | 143 | if (message) { |
145 | struct cmd_results *error = | 144 | struct cmd_results *error = |
146 | cmd_results_new(CMD_INVALID, message); | 145 | cmd_results_new(CMD_INVALID, "%s", message); |
147 | free(message); | 146 | free(message); |
148 | return error; | 147 | return error; |
149 | } else { | 148 | } else { |
@@ -182,7 +181,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, | |||
182 | uint32_t button = get_mouse_bindsym(name, &message); | 181 | uint32_t button = get_mouse_bindsym(name, &message); |
183 | if (message) { | 182 | if (message) { |
184 | struct cmd_results *error = | 183 | struct cmd_results *error = |
185 | cmd_results_new(CMD_INVALID, message); | 184 | cmd_results_new(CMD_INVALID, "%s", message); |
186 | free(message); | 185 | free(message); |
187 | return error; | 186 | return error; |
188 | } else if (button) { | 187 | } else if (button) { |
@@ -372,6 +371,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | |||
372 | strlen("--input-device=")) == 0) { | 371 | strlen("--input-device=")) == 0) { |
373 | free(binding->input); | 372 | free(binding->input); |
374 | binding->input = strdup(argv[0] + strlen("--input-device=")); | 373 | binding->input = strdup(argv[0] + strlen("--input-device=")); |
374 | strip_quotes(binding->input); | ||
375 | } else if (strcmp("--no-warn", argv[0]) == 0) { | 375 | } else if (strcmp("--no-warn", argv[0]) == 0) { |
376 | warn = false; | 376 | warn = false; |
377 | } else if (strcmp("--no-repeat", argv[0]) == 0) { | 377 | } else if (strcmp("--no-repeat", argv[0]) == 0) { |
@@ -538,7 +538,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, | |||
538 | free_switch_binding(binding); | 538 | free_switch_binding(binding); |
539 | return cmd_results_new(CMD_FAILURE, | 539 | return cmd_results_new(CMD_FAILURE, |
540 | "Invalid %s command (expected binding with the form " | 540 | "Invalid %s command (expected binding with the form " |
541 | "<switch>:<state>)", bindtype, argc); | 541 | "<switch>:<state>)", bindtype); |
542 | } | 542 | } |
543 | if (strcmp(split->items[0], "tablet") == 0) { | 543 | if (strcmp(split->items[0], "tablet") == 0) { |
544 | binding->type = WLR_SWITCH_TYPE_TABLET_MODE; | 544 | binding->type = WLR_SWITCH_TYPE_TABLET_MODE; |
@@ -548,20 +548,21 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, | |||
548 | free_switch_binding(binding); | 548 | free_switch_binding(binding); |
549 | return cmd_results_new(CMD_FAILURE, | 549 | return cmd_results_new(CMD_FAILURE, |
550 | "Invalid %s command (expected switch binding: " | 550 | "Invalid %s command (expected switch binding: " |
551 | "unknown switch %s)", bindtype, split->items[0]); | 551 | "unknown switch %s)", bindtype, |
552 | (const char *)split->items[0]); | ||
552 | } | 553 | } |
553 | if (strcmp(split->items[1], "on") == 0) { | 554 | if (strcmp(split->items[1], "on") == 0) { |
554 | binding->state = WLR_SWITCH_STATE_ON; | 555 | binding->trigger = SWAY_SWITCH_TRIGGER_ON; |
555 | } else if (strcmp(split->items[1], "off") == 0) { | 556 | } else if (strcmp(split->items[1], "off") == 0) { |
556 | binding->state = WLR_SWITCH_STATE_OFF; | 557 | binding->trigger = SWAY_SWITCH_TRIGGER_OFF; |
557 | } else if (strcmp(split->items[1], "toggle") == 0) { | 558 | } else if (strcmp(split->items[1], "toggle") == 0) { |
558 | binding->state = WLR_SWITCH_STATE_TOGGLE; | 559 | binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE; |
559 | } else { | 560 | } else { |
560 | free_switch_binding(binding); | 561 | free_switch_binding(binding); |
561 | return cmd_results_new(CMD_FAILURE, | 562 | return cmd_results_new(CMD_FAILURE, |
562 | "Invalid %s command " | 563 | "Invalid %s command " |
563 | "(expected switch state: unknown state %d)", | 564 | "(expected switch state: unknown state %s)", |
564 | bindtype, split->items[0]); | 565 | bindtype, (const char *)split->items[1]); |
565 | } | 566 | } |
566 | list_free_items_and_destroy(split); | 567 | list_free_items_and_destroy(split); |
567 | 568 | ||
diff --git a/sway/commands/client.c b/sway/commands/client.c index dd0694df..fd2ac7a8 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c | |||
@@ -5,9 +5,8 @@ | |||
5 | #include "sway/tree/container.h" | 5 | #include "sway/tree/container.h" |
6 | #include "util.h" | 6 | #include "util.h" |
7 | 7 | ||
8 | static void rebuild_textures_iterator(struct sway_container *con, void *data) { | 8 | static void container_update_iterator(struct sway_container *con, void *data) { |
9 | container_update_marks_textures(con); | 9 | container_update(con); |
10 | container_update_title_textures(con); | ||
11 | } | 10 | } |
12 | 11 | ||
13 | static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | 12 | static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, |
@@ -18,6 +17,12 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | |||
18 | return error; | 17 | return error; |
19 | } | 18 | } |
20 | 19 | ||
20 | if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) { | ||
21 | sway_log(SWAY_ERROR, | ||
22 | "Warning: indicator and child_border colors have no effect for %s", | ||
23 | cmd_name); | ||
24 | } | ||
25 | |||
21 | struct border_colors colors = {0}; | 26 | struct border_colors colors = {0}; |
22 | const char *ind_hex = argc > 3 ? argv[3] : default_indicator; | 27 | const char *ind_hex = argc > 3 ? argv[3] : default_indicator; |
23 | const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background | 28 | const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background |
@@ -45,12 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | |||
45 | memcpy(class, &colors, sizeof(struct border_colors)); | 50 | memcpy(class, &colors, sizeof(struct border_colors)); |
46 | 51 | ||
47 | if (config->active) { | 52 | if (config->active) { |
48 | root_for_each_container(rebuild_textures_iterator, NULL); | 53 | root_for_each_container(container_update_iterator, NULL); |
49 | |||
50 | for (int i = 0; i < root->outputs->length; ++i) { | ||
51 | struct sway_output *output = root->outputs->items[i]; | ||
52 | output_damage_whole(output); | ||
53 | } | ||
54 | } | 54 | } |
55 | 55 | ||
56 | return cmd_results_new(CMD_SUCCESS, NULL); | 56 | return cmd_results_new(CMD_SUCCESS, NULL); |
@@ -80,3 +80,13 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) { | |||
80 | sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); | 80 | sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); |
81 | return cmd_results_new(CMD_SUCCESS, NULL); | 81 | return cmd_results_new(CMD_SUCCESS, NULL); |
82 | } | 82 | } |
83 | |||
84 | struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) { | ||
85 | struct cmd_results *result = handle_command(argc, argv, | ||
86 | "client.focused_tab_title", | ||
87 | &config->border_colors.focused_tab_title, "#2e9ef4ff"); | ||
88 | if (result && result->status == CMD_SUCCESS) { | ||
89 | config->has_focused_tab_title = true; | ||
90 | } | ||
91 | return result; | ||
92 | } | ||
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index fce337d5..8bc1048c 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <stdint.h> | 2 | #include <stdint.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -7,6 +6,8 @@ | |||
7 | #include <signal.h> | 6 | #include <signal.h> |
8 | #include "sway/commands.h" | 7 | #include "sway/commands.h" |
9 | #include "sway/config.h" | 8 | #include "sway/config.h" |
9 | #include "sway/server.h" | ||
10 | #include "sway/desktop/launcher.h" | ||
10 | #include "sway/tree/container.h" | 11 | #include "sway/tree/container.h" |
11 | #include "sway/tree/root.h" | 12 | #include "sway/tree/root.h" |
12 | #include "sway/tree/workspace.h" | 13 | #include "sway/tree/workspace.h" |
@@ -24,11 +25,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) { | |||
24 | return error; | 25 | return error; |
25 | } | 26 | } |
26 | 27 | ||
28 | static void export_xdga_token(struct launcher_ctx *ctx) { | ||
29 | const char *token = launcher_ctx_get_token_name(ctx); | ||
30 | setenv("XDG_ACTIVATION_TOKEN", token, 1); | ||
31 | } | ||
32 | |||
33 | static void export_startup_id(struct launcher_ctx *ctx) { | ||
34 | const char *token = launcher_ctx_get_token_name(ctx); | ||
35 | setenv("DESKTOP_STARTUP_ID", token, 1); | ||
36 | } | ||
37 | |||
27 | struct cmd_results *cmd_exec_process(int argc, char **argv) { | 38 | struct cmd_results *cmd_exec_process(int argc, char **argv) { |
28 | struct cmd_results *error = NULL; | 39 | struct cmd_results *error = NULL; |
29 | char *cmd = NULL; | 40 | char *cmd = NULL; |
41 | bool no_startup_id = false; | ||
30 | if (strcmp(argv[0], "--no-startup-id") == 0) { | 42 | if (strcmp(argv[0], "--no-startup-id") == 0) { |
31 | sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); | 43 | no_startup_id = true; |
32 | --argc; ++argv; | 44 | --argc; ++argv; |
33 | if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { | 45 | if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { |
34 | return error; | 46 | return error; |
@@ -50,9 +62,11 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
50 | } | 62 | } |
51 | 63 | ||
52 | pid_t pid, child; | 64 | pid_t pid, child; |
65 | struct launcher_ctx *ctx = launcher_ctx_create_internal(); | ||
53 | // Fork process | 66 | // Fork process |
54 | if ((pid = fork()) == 0) { | 67 | if ((pid = fork()) == 0) { |
55 | // Fork child process again | 68 | // Fork child process again |
69 | restore_nofile_limit(); | ||
56 | setsid(); | 70 | setsid(); |
57 | sigset_t set; | 71 | sigset_t set; |
58 | sigemptyset(&set); | 72 | sigemptyset(&set); |
@@ -61,6 +75,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
61 | close(fd[0]); | 75 | close(fd[0]); |
62 | if ((child = fork()) == 0) { | 76 | if ((child = fork()) == 0) { |
63 | close(fd[1]); | 77 | close(fd[1]); |
78 | if (ctx) { | ||
79 | export_xdga_token(ctx); | ||
80 | } | ||
81 | if (ctx && !no_startup_id) { | ||
82 | export_startup_id(ctx); | ||
83 | } | ||
64 | execlp("sh", "sh", "-c", cmd, (void *)NULL); | 84 | execlp("sh", "sh", "-c", cmd, (void *)NULL); |
65 | sway_log_errno(SWAY_ERROR, "execlp failed"); | 85 | sway_log_errno(SWAY_ERROR, "execlp failed"); |
66 | _exit(1); | 86 | _exit(1); |
@@ -88,8 +108,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
88 | waitpid(pid, NULL, 0); | 108 | waitpid(pid, NULL, 0); |
89 | if (child > 0) { | 109 | if (child > 0) { |
90 | sway_log(SWAY_DEBUG, "Child process created with pid %d", child); | 110 | sway_log(SWAY_DEBUG, "Child process created with pid %d", child); |
91 | root_record_workspace_pid(child); | 111 | if (ctx != NULL) { |
112 | sway_log(SWAY_DEBUG, "Recording workspace for process %d", child); | ||
113 | ctx->pid = child; | ||
114 | } | ||
92 | } else { | 115 | } else { |
116 | launcher_ctx_destroy(ctx); | ||
93 | return cmd_results_new(CMD_FAILURE, "Second fork() failed"); | 117 | return cmd_results_new(CMD_FAILURE, "Second fork() failed"); |
94 | } | 118 | } |
95 | 119 | ||
diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c index 3a1d606a..e8c24ace 100644 --- a/sway/commands/floating_minmax_size.c +++ b/sway/commands/floating_minmax_size.c | |||
@@ -23,16 +23,16 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | |||
23 | char *err; | 23 | char *err; |
24 | int width = (int)strtol(argv[0], &err, 10); | 24 | int width = (int)strtol(argv[0], &err, 10); |
25 | if (*err) { | 25 | if (*err) { |
26 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | 26 | return cmd_results_new(CMD_INVALID, "%s", usage); |
27 | } | 27 | } |
28 | 28 | ||
29 | if (strcmp(argv[1], "x") != 0) { | 29 | if (strcmp(argv[1], "x") != 0) { |
30 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | 30 | return cmd_results_new(CMD_INVALID, "%s", usage); |
31 | } | 31 | } |
32 | 32 | ||
33 | int height = (int)strtol(argv[2], &err, 10); | 33 | int height = (int)strtol(argv[2], &err, 10); |
34 | if (*err) { | 34 | if (*err) { |
35 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | 35 | return cmd_results_new(CMD_INVALID, "%s", usage); |
36 | } | 36 | } |
37 | 37 | ||
38 | *config_width = width; | 38 | *config_width = width; |
diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 6771ca2f..facd82de 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c | |||
@@ -54,7 +54,7 @@ static bool get_direction_from_next_prev(struct sway_container *container, | |||
54 | } else { | 54 | } else { |
55 | return false; | 55 | return false; |
56 | } | 56 | } |
57 | 57 | ||
58 | return true; | 58 | return true; |
59 | } | 59 | } |
60 | 60 | ||
@@ -267,6 +267,11 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws, | |||
267 | new_focus = seat_get_focus_inactive_tiling(seat, ws); | 267 | new_focus = seat_get_focus_inactive_tiling(seat, ws); |
268 | } | 268 | } |
269 | if (new_focus) { | 269 | if (new_focus) { |
270 | struct sway_container *new_focus_view = | ||
271 | seat_get_focus_inactive_view(seat, &new_focus->node); | ||
272 | if (new_focus_view) { | ||
273 | new_focus = new_focus_view; | ||
274 | } | ||
270 | seat_set_focus_container(seat, new_focus); | 275 | seat_set_focus_container(seat, new_focus); |
271 | 276 | ||
272 | // If we're on the floating layer and the floating container area | 277 | // If we're on the floating layer and the floating container area |
@@ -280,7 +285,7 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws, | |||
280 | } | 285 | } |
281 | } else { | 286 | } else { |
282 | return cmd_results_new(CMD_FAILURE, | 287 | return cmd_results_new(CMD_FAILURE, |
283 | "Failed to find a %s container in workspace", | 288 | "Failed to find a %s container in workspace.", |
284 | floating ? "floating" : "tiling"); | 289 | floating ? "floating" : "tiling"); |
285 | } | 290 | } |
286 | return cmd_results_new(CMD_SUCCESS, NULL); | 291 | return cmd_results_new(CMD_SUCCESS, NULL); |
@@ -290,7 +295,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat, | |||
290 | int argc, char **argv) { | 295 | int argc, char **argv) { |
291 | if (!argc) { | 296 | if (!argc) { |
292 | return cmd_results_new(CMD_INVALID, | 297 | return cmd_results_new(CMD_INVALID, |
293 | "Expected 'focus output <direction|name>'"); | 298 | "Expected 'focus output <direction|name>'."); |
294 | } | 299 | } |
295 | char *identifier = join_args(argv, argc); | 300 | char *identifier = join_args(argv, argc); |
296 | struct sway_output *output = output_by_name_or_id(identifier); | 301 | struct sway_output *output = output_by_name_or_id(identifier); |
@@ -300,13 +305,13 @@ static struct cmd_results *focus_output(struct sway_seat *seat, | |||
300 | if (!parse_direction(identifier, &direction)) { | 305 | if (!parse_direction(identifier, &direction)) { |
301 | free(identifier); | 306 | free(identifier); |
302 | return cmd_results_new(CMD_INVALID, | 307 | return cmd_results_new(CMD_INVALID, |
303 | "There is no output with that name"); | 308 | "There is no output with that name."); |
304 | } | 309 | } |
305 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | 310 | struct sway_workspace *ws = seat_get_focused_workspace(seat); |
306 | if (!ws) { | 311 | if (!ws) { |
307 | free(identifier); | 312 | free(identifier); |
308 | return cmd_results_new(CMD_FAILURE, | 313 | return cmd_results_new(CMD_FAILURE, |
309 | "No focused workspace to base directions off of"); | 314 | "No focused workspace to base directions off of."); |
310 | } | 315 | } |
311 | output = output_get_in_direction(ws->output, direction); | 316 | output = output_get_in_direction(ws->output, direction); |
312 | 317 | ||
@@ -370,10 +375,14 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
370 | struct sway_seat *seat = config->handler_context.seat; | 375 | struct sway_seat *seat = config->handler_context.seat; |
371 | if (node->type < N_WORKSPACE) { | 376 | if (node->type < N_WORKSPACE) { |
372 | return cmd_results_new(CMD_FAILURE, | 377 | return cmd_results_new(CMD_FAILURE, |
373 | "Command 'focus' cannot be used above the workspace level"); | 378 | "Command 'focus' cannot be used above the workspace level."); |
374 | } | 379 | } |
375 | 380 | ||
376 | if (argc == 0 && container) { | 381 | if (argc == 0) { |
382 | if (!container) { | ||
383 | return cmd_results_new(CMD_FAILURE, "No container to focus was specified."); | ||
384 | } | ||
385 | |||
377 | if (container_is_scratchpad_hidden_or_child(container)) { | 386 | if (container_is_scratchpad_hidden_or_child(container)) { |
378 | root_scratchpad_show(container); | 387 | root_scratchpad_show(container); |
379 | } | 388 | } |
@@ -446,7 +455,8 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
446 | return cmd_results_new(CMD_FAILURE, ""); | 455 | return cmd_results_new(CMD_FAILURE, ""); |
447 | } | 456 | } |
448 | struct sway_node *next_focus = NULL; | 457 | struct sway_node *next_focus = NULL; |
449 | if (container_is_floating(container)) { | 458 | if (container_is_floating(container) && |
459 | container->pending.fullscreen_mode == FULLSCREEN_NONE) { | ||
450 | next_focus = node_get_in_direction_floating(container, seat, direction); | 460 | next_focus = node_get_in_direction_floating(container, seat, direction); |
451 | } else { | 461 | } else { |
452 | next_focus = node_get_in_direction_tiling(container, seat, direction, descend); | 462 | next_focus = node_get_in_direction_tiling(container, seat, direction, descend); |
diff --git a/sway/commands/font.c b/sway/commands/font.c index c54365b5..9920d03e 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c | |||
@@ -1,9 +1,9 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
5 | #include "log.h" | 4 | #include "log.h" |
6 | #include "stringop.h" | 5 | #include "stringop.h" |
6 | #include <pango/pangocairo.h> | ||
7 | 7 | ||
8 | struct cmd_results *cmd_font(int argc, char **argv) { | 8 | struct cmd_results *cmd_font(int argc, char **argv) { |
9 | struct cmd_results *error = NULL; | 9 | struct cmd_results *error = NULL; |
@@ -16,12 +16,34 @@ struct cmd_results *cmd_font(int argc, char **argv) { | |||
16 | if (strncmp(font, "pango:", 6) == 0) { | 16 | if (strncmp(font, "pango:", 6) == 0) { |
17 | config->pango_markup = true; | 17 | config->pango_markup = true; |
18 | config->font = strdup(font + 6); | 18 | config->font = strdup(font + 6); |
19 | free(font); | ||
19 | } else { | 20 | } else { |
20 | config->pango_markup = false; | 21 | config->pango_markup = false; |
21 | config->font = strdup(font); | 22 | config->font = font; |
22 | } | 23 | } |
23 | 24 | ||
24 | free(font); | 25 | // Parse the font early so we can reject it if it's not valid for pango. |
25 | config_update_font_height(true); | 26 | // Also avoids re-parsing each time we render text. |
27 | PangoFontDescription *font_description = pango_font_description_from_string(config->font); | ||
28 | |||
29 | const char *family = pango_font_description_get_family(font_description); | ||
30 | if (family == NULL) { | ||
31 | pango_font_description_free(font_description); | ||
32 | return cmd_results_new(CMD_FAILURE, "Invalid font family."); | ||
33 | } | ||
34 | |||
35 | const gint size = pango_font_description_get_size(font_description); | ||
36 | if (size == 0) { | ||
37 | pango_font_description_free(font_description); | ||
38 | return cmd_results_new(CMD_FAILURE, "Invalid font size."); | ||
39 | } | ||
40 | |||
41 | if (config->font_description != NULL) { | ||
42 | pango_font_description_free(config->font_description); | ||
43 | } | ||
44 | |||
45 | config->font_description = font_description; | ||
46 | config_update_font_height(); | ||
47 | |||
26 | return cmd_results_new(CMD_SUCCESS, NULL); | 48 | return cmd_results_new(CMD_SUCCESS, NULL); |
27 | } | 49 | } |
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c index ee9f4647..905e6776 100644 --- a/sway/commands/for_window.c +++ b/sway/commands/for_window.c | |||
@@ -14,7 +14,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) { | |||
14 | char *err_str = NULL; | 14 | char *err_str = NULL; |
15 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | 15 | struct criteria *criteria = criteria_parse(argv[0], &err_str); |
16 | if (!criteria) { | 16 | if (!criteria) { |
17 | error = cmd_results_new(CMD_INVALID, err_str); | 17 | error = cmd_results_new(CMD_INVALID, "%s", err_str); |
18 | free(err_str); | 18 | free(err_str); |
19 | return error; | 19 | return error; |
20 | } | 20 | } |
diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c new file mode 100644 index 00000000..90a20716 --- /dev/null +++ b/sway/commands/gesture.c | |||
@@ -0,0 +1,165 @@ | |||
1 | #include "sway/config.h" | ||
2 | |||
3 | #include "gesture.h" | ||
4 | #include "log.h" | ||
5 | #include "stringop.h" | ||
6 | #include "sway/commands.h" | ||
7 | |||
8 | void free_gesture_binding(struct sway_gesture_binding *binding) { | ||
9 | if (!binding) { | ||
10 | return; | ||
11 | } | ||
12 | free(binding->input); | ||
13 | free(binding->command); | ||
14 | free(binding); | ||
15 | } | ||
16 | |||
17 | /** | ||
18 | * Returns true if the bindings have the same gesture type, direction, etc | ||
19 | */ | ||
20 | static bool binding_gesture_equal(struct sway_gesture_binding *binding_a, | ||
21 | struct sway_gesture_binding *binding_b) { | ||
22 | if (strcmp(binding_a->input, binding_b->input) != 0) { | ||
23 | return false; | ||
24 | } | ||
25 | |||
26 | if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) { | ||
27 | return false; | ||
28 | } | ||
29 | |||
30 | if ((binding_a->flags & BINDING_EXACT) != | ||
31 | (binding_b->flags & BINDING_EXACT)) { | ||
32 | return false; | ||
33 | } | ||
34 | return true; | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | * Add gesture binding to config | ||
39 | */ | ||
40 | static struct cmd_results *gesture_binding_add( | ||
41 | struct sway_gesture_binding *binding, | ||
42 | const char *gesturecombo, bool warn) { | ||
43 | list_t *mode_bindings = config->current_mode->gesture_bindings; | ||
44 | // overwrite the binding if it already exists | ||
45 | bool overwritten = false; | ||
46 | for (int i = 0; i < mode_bindings->length; ++i) { | ||
47 | struct sway_gesture_binding *config_binding = mode_bindings->items[i]; | ||
48 | if (binding_gesture_equal(binding, config_binding)) { | ||
49 | sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", | ||
50 | gesturecombo, binding->command, config_binding->command); | ||
51 | if (warn) { | ||
52 | config_add_swaynag_warning("Overwriting binding" | ||
53 | "'%s' to `%s` from `%s`", | ||
54 | gesturecombo, binding->command, | ||
55 | config_binding->command); | ||
56 | } | ||
57 | free_gesture_binding(config_binding); | ||
58 | mode_bindings->items[i] = binding; | ||
59 | overwritten = true; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | if (!overwritten) { | ||
64 | list_add(mode_bindings, binding); | ||
65 | sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`", | ||
66 | gesturecombo, binding->command); | ||
67 | } | ||
68 | |||
69 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * Remove gesture binding from config | ||
74 | */ | ||
75 | static struct cmd_results *gesture_binding_remove( | ||
76 | struct sway_gesture_binding *binding, const char *gesturecombo) { | ||
77 | list_t *mode_bindings = config->current_mode->gesture_bindings; | ||
78 | for (int i = 0; i < mode_bindings->length; ++i) { | ||
79 | struct sway_gesture_binding *config_binding = mode_bindings->items[i]; | ||
80 | if (binding_gesture_equal(binding, config_binding)) { | ||
81 | free_gesture_binding(config_binding); | ||
82 | free_gesture_binding(binding); | ||
83 | list_del(mode_bindings, i); | ||
84 | sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture", | ||
85 | gesturecombo); | ||
86 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | free_gesture_binding(binding); | ||
91 | return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`", | ||
92 | gesturecombo); | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * Parse and execute bindgesture or unbindgesture command. | ||
97 | */ | ||
98 | static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) { | ||
99 | int minargs = 2; | ||
100 | char *bindtype = "bindgesture"; | ||
101 | if (unbind) { | ||
102 | minargs--; | ||
103 | bindtype = "unbindgesture"; | ||
104 | } | ||
105 | |||
106 | struct cmd_results *error = NULL; | ||
107 | if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { | ||
108 | return error; | ||
109 | } | ||
110 | struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding)); | ||
111 | if (!binding) { | ||
112 | return cmd_results_new(CMD_FAILURE, "Unable to allocate binding"); | ||
113 | } | ||
114 | binding->input = strdup("*"); | ||
115 | |||
116 | bool warn = true; | ||
117 | |||
118 | // Handle flags | ||
119 | while (argc > 0) { | ||
120 | if (strcmp("--exact", argv[0]) == 0) { | ||
121 | binding->flags |= BINDING_EXACT; | ||
122 | } else if (strcmp("--no-warn", argv[0]) == 0) { | ||
123 | warn = false; | ||
124 | } else if (strncmp("--input-device=", argv[0], | ||
125 | strlen("--input-device=")) == 0) { | ||
126 | free(binding->input); | ||
127 | binding->input = strdup(argv[0] + strlen("--input-device=")); | ||
128 | } else { | ||
129 | break; | ||
130 | } | ||
131 | argv++; | ||
132 | argc--; | ||
133 | } | ||
134 | |||
135 | if (argc < minargs) { | ||
136 | free(binding); | ||
137 | return cmd_results_new(CMD_FAILURE, | ||
138 | "Invalid %s command (expected at least %d " | ||
139 | "non-option arguments, got %d)", bindtype, minargs, argc); | ||
140 | } | ||
141 | |||
142 | char* errmsg = NULL; | ||
143 | if ((errmsg = gesture_parse(argv[0], &binding->gesture))) { | ||
144 | free(binding); | ||
145 | struct cmd_results *final = cmd_results_new(CMD_FAILURE, | ||
146 | "Invalid %s command (%s)", | ||
147 | bindtype, errmsg); | ||
148 | free(errmsg); | ||
149 | return final; | ||
150 | } | ||
151 | |||
152 | if (unbind) { | ||
153 | return gesture_binding_remove(binding, argv[0]); | ||
154 | } | ||
155 | binding->command = join_args(argv + 1, argc - 1); | ||
156 | return gesture_binding_add(binding, argv[0], warn); | ||
157 | } | ||
158 | |||
159 | struct cmd_results *cmd_bindgesture(int argc, char **argv) { | ||
160 | return cmd_bind_or_unbind_gesture(argc, argv, false); | ||
161 | } | ||
162 | |||
163 | struct cmd_results *cmd_unbindgesture(int argc, char **argv) { | ||
164 | return cmd_bind_or_unbind_gesture(argc, argv, true); | ||
165 | } | ||
diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index 9a1d8445..43bd6dc8 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c | |||
@@ -20,7 +20,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { | |||
20 | } | 20 | } |
21 | 21 | ||
22 | if (!argc) { | 22 | if (!argc) { |
23 | return cmd_results_new(CMD_INVALID, expected_syntax); | 23 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
24 | } | 24 | } |
25 | 25 | ||
26 | if (strcmp(argv[0], "none") == 0) { | 26 | if (strcmp(argv[0], "none") == 0) { |
@@ -38,7 +38,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { | |||
38 | config->hide_edge_borders = E_NONE; | 38 | config->hide_edge_borders = E_NONE; |
39 | config->hide_edge_borders_smart = ESMART_NO_GAPS; | 39 | config->hide_edge_borders_smart = ESMART_NO_GAPS; |
40 | } else { | 40 | } else { |
41 | return cmd_results_new(CMD_INVALID, expected_syntax); | 41 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
42 | } | 42 | } |
43 | config->hide_lone_tab = hide_lone_tab; | 43 | config->hide_lone_tab = hide_lone_tab; |
44 | 44 | ||
diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c index aebc2bf9..6125736a 100644 --- a/sway/commands/inhibit_idle.c +++ b/sway/commands/inhibit_idle.c | |||
@@ -41,7 +41,7 @@ struct cmd_results *cmd_inhibit_idle(int argc, char **argv) { | |||
41 | sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); | 41 | sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); |
42 | } else { | 42 | } else { |
43 | inhibitor->mode = mode; | 43 | inhibitor->mode = mode; |
44 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | 44 | sway_idle_inhibit_v1_check_active(); |
45 | } | 45 | } |
46 | } else if (!clear) { | 46 | } else if (!clear) { |
47 | sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); | 47 | sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); |
diff --git a/sway/commands/input.c b/sway/commands/input.c index 77acb671..306c40f7 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c | |||
@@ -14,6 +14,7 @@ static const struct cmd_handler input_handlers[] = { | |||
14 | { "drag", input_cmd_drag }, | 14 | { "drag", input_cmd_drag }, |
15 | { "drag_lock", input_cmd_drag_lock }, | 15 | { "drag_lock", input_cmd_drag_lock }, |
16 | { "dwt", input_cmd_dwt }, | 16 | { "dwt", input_cmd_dwt }, |
17 | { "dwtp", input_cmd_dwtp }, | ||
17 | { "events", input_cmd_events }, | 18 | { "events", input_cmd_events }, |
18 | { "left_handed", input_cmd_left_handed }, | 19 | { "left_handed", input_cmd_left_handed }, |
19 | { "map_from_region", input_cmd_map_from_region }, | 20 | { "map_from_region", input_cmd_map_from_region }, |
@@ -24,7 +25,9 @@ static const struct cmd_handler input_handlers[] = { | |||
24 | { "pointer_accel", input_cmd_pointer_accel }, | 25 | { "pointer_accel", input_cmd_pointer_accel }, |
25 | { "repeat_delay", input_cmd_repeat_delay }, | 26 | { "repeat_delay", input_cmd_repeat_delay }, |
26 | { "repeat_rate", input_cmd_repeat_rate }, | 27 | { "repeat_rate", input_cmd_repeat_rate }, |
28 | { "rotation_angle", input_cmd_rotation_angle }, | ||
27 | { "scroll_button", input_cmd_scroll_button }, | 29 | { "scroll_button", input_cmd_scroll_button }, |
30 | { "scroll_button_lock", input_cmd_scroll_button_lock }, | ||
28 | { "scroll_factor", input_cmd_scroll_factor }, | 31 | { "scroll_factor", input_cmd_scroll_factor }, |
29 | { "scroll_method", input_cmd_scroll_method }, | 32 | { "scroll_method", input_cmd_scroll_method }, |
30 | { "tap", input_cmd_tap }, | 33 | { "tap", input_cmd_tap }, |
diff --git a/sway/commands/input/calibration_matrix.c b/sway/commands/input/calibration_matrix.c index 38749fbb..53fe2c35 100644 --- a/sway/commands/input/calibration_matrix.c +++ b/sway/commands/input/calibration_matrix.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/input/dwtp.c b/sway/commands/input/dwtp.c new file mode 100644 index 00000000..232e2b26 --- /dev/null +++ b/sway/commands/input/dwtp.c | |||
@@ -0,0 +1,25 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/commands.h" | ||
5 | #include "sway/input/input-manager.h" | ||
6 | #include "util.h" | ||
7 | |||
8 | struct cmd_results *input_cmd_dwtp(int argc, char **argv) { | ||
9 | struct cmd_results *error = NULL; | ||
10 | if ((error = checkarg(argc, "dwtp", EXPECTED_AT_LEAST, 1))) { | ||
11 | return error; | ||
12 | } | ||
13 | struct input_config *ic = config->handler_context.input_config; | ||
14 | if (!ic) { | ||
15 | return cmd_results_new(CMD_FAILURE, "No input device defined."); | ||
16 | } | ||
17 | |||
18 | if (parse_boolean(argv[0], true)) { | ||
19 | ic->dwtp = LIBINPUT_CONFIG_DWTP_ENABLED; | ||
20 | } else { | ||
21 | ic->dwtp = LIBINPUT_CONFIG_DWTP_DISABLED; | ||
22 | } | ||
23 | |||
24 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
25 | } | ||
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 9405181a..08d99bf0 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c | |||
@@ -1,14 +1,19 @@ | |||
1 | #include <limits.h> | 1 | #include <limits.h> |
2 | #include <string.h> | 2 | #include <string.h> |
3 | #include <strings.h> | 3 | #include <strings.h> |
4 | #include <wlr/backend/libinput.h> | 4 | #include <wlr/config.h> |
5 | #include "sway/config.h" | 5 | #include "sway/config.h" |
6 | #include "sway/commands.h" | 6 | #include "sway/commands.h" |
7 | #include "sway/input/input-manager.h" | 7 | #include "sway/input/input-manager.h" |
8 | #include "log.h" | 8 | #include "log.h" |
9 | 9 | ||
10 | #if WLR_HAS_LIBINPUT_BACKEND | ||
11 | #include <wlr/backend/libinput.h> | ||
12 | #endif | ||
13 | |||
10 | static void toggle_supported_send_events_for_device(struct input_config *ic, | 14 | static void toggle_supported_send_events_for_device(struct input_config *ic, |
11 | struct sway_input_device *input_device) { | 15 | struct sway_input_device *input_device) { |
16 | #if WLR_HAS_LIBINPUT_BACKEND | ||
12 | struct wlr_input_device *wlr_device = input_device->wlr_device; | 17 | struct wlr_input_device *wlr_device = input_device->wlr_device; |
13 | if (!wlr_input_device_is_libinput(wlr_device)) { | 18 | if (!wlr_input_device_is_libinput(wlr_device)) { |
14 | return; | 19 | return; |
@@ -41,6 +46,7 @@ static void toggle_supported_send_events_for_device(struct input_config *ic, | |||
41 | } | 46 | } |
42 | 47 | ||
43 | ic->send_events = mode; | 48 | ic->send_events = mode; |
49 | #endif | ||
44 | } | 50 | } |
45 | 51 | ||
46 | static int mode_for_name(const char *name) { | 52 | static int mode_for_name(const char *name) { |
@@ -56,6 +62,7 @@ static int mode_for_name(const char *name) { | |||
56 | 62 | ||
57 | static void toggle_select_send_events_for_device(struct input_config *ic, | 63 | static void toggle_select_send_events_for_device(struct input_config *ic, |
58 | struct sway_input_device *input_device, int argc, char **argv) { | 64 | struct sway_input_device *input_device, int argc, char **argv) { |
65 | #if WLR_HAS_LIBINPUT_BACKEND | ||
59 | if (!wlr_input_device_is_libinput(input_device->wlr_device)) { | 66 | if (!wlr_input_device_is_libinput(input_device->wlr_device)) { |
60 | return; | 67 | return; |
61 | } | 68 | } |
@@ -72,6 +79,7 @@ static void toggle_select_send_events_for_device(struct input_config *ic, | |||
72 | } | 79 | } |
73 | } | 80 | } |
74 | ic->send_events = mode_for_name(argv[index % argc]); | 81 | ic->send_events = mode_for_name(argv[index % argc]); |
82 | #endif | ||
75 | } | 83 | } |
76 | 84 | ||
77 | static void toggle_send_events(int argc, char **argv) { | 85 | static void toggle_send_events(int argc, char **argv) { |
diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c index de00b714..2f8f753d 100644 --- a/sway/commands/input/map_from_region.c +++ b/sway/commands/input/map_from_region.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
@@ -11,11 +10,21 @@ static bool parse_coords(const char *str, double *x, double *y, bool *mm) { | |||
11 | *mm = false; | 10 | *mm = false; |
12 | 11 | ||
13 | char *end; | 12 | char *end; |
14 | *x = strtod(str, &end); | 13 | |
15 | if (end[0] != 'x') { | 14 | // Check for "0x" prefix to avoid strtod treating the string as hex |
16 | return false; | 15 | if (str[0] == '0' && str[1] == 'x') { |
16 | if (strlen(str) < 3) { | ||
17 | return false; | ||
18 | } | ||
19 | *x = 0; | ||
20 | end = (char *)str + 2; | ||
21 | } else { | ||
22 | *x = strtod(str, &end); | ||
23 | if (end[0] != 'x') { | ||
24 | return false; | ||
25 | } | ||
26 | ++end; | ||
17 | } | 27 | } |
18 | ++end; | ||
19 | 28 | ||
20 | *y = strtod(end, &end); | 29 | *y = strtod(end, &end); |
21 | if (end[0] == 'm') { | 30 | if (end[0] == 'm') { |
diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c index f60fb7d5..a7266baa 100644 --- a/sway/commands/input/map_to_output.c +++ b/sway/commands/input/map_to_output.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c index 284b57d0..9087c589 100644 --- a/sway/commands/input/map_to_region.c +++ b/sway/commands/input/map_to_region.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -49,5 +48,5 @@ struct cmd_results *input_cmd_map_to_region(int argc, char **argv) { | |||
49 | error: | 48 | error: |
50 | free(ic->mapped_to_region); | 49 | free(ic->mapped_to_region); |
51 | ic->mapped_to_region = NULL; | 50 | ic->mapped_to_region = NULL; |
52 | return cmd_results_new(CMD_FAILURE, errstr); | 51 | return cmd_results_new(CMD_FAILURE, "%s", errstr); |
53 | } | 52 | } |
diff --git a/sway/commands/input/rotation_angle.c b/sway/commands/input/rotation_angle.c new file mode 100644 index 00000000..5e278fff --- /dev/null +++ b/sway/commands/input/rotation_angle.c | |||
@@ -0,0 +1,29 @@ | |||
1 | #include <math.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | #include "sway/config.h" | ||
5 | #include "sway/commands.h" | ||
6 | #include "sway/input/input-manager.h" | ||
7 | #include "util.h" | ||
8 | |||
9 | struct cmd_results *input_cmd_rotation_angle(int argc, char **argv) { | ||
10 | struct cmd_results *error = NULL; | ||
11 | if ((error = checkarg(argc, "rotation_angle", EXPECTED_AT_LEAST, 1))) { | ||
12 | return error; | ||
13 | } | ||
14 | struct input_config *ic = config->handler_context.input_config; | ||
15 | if (!ic) { | ||
16 | return cmd_results_new(CMD_FAILURE, "No input device defined."); | ||
17 | } | ||
18 | |||
19 | float rotation_angle = parse_float(argv[0]); | ||
20 | if (isnan(rotation_angle)) { | ||
21 | return cmd_results_new(CMD_INVALID, | ||
22 | "Invalid rotation_angle; expected float."); | ||
23 | } if (rotation_angle < 0 || rotation_angle > 360) { | ||
24 | return cmd_results_new(CMD_INVALID, "Input out of range [0, 360)"); | ||
25 | } | ||
26 | ic->rotation_angle = rotation_angle; | ||
27 | |||
28 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
29 | } | ||
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c index 6b331419..81f69a6d 100644 --- a/sway/commands/input/scroll_button.c +++ b/sway/commands/input/scroll_button.c | |||
@@ -21,7 +21,7 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { | |||
21 | char *message = NULL; | 21 | char *message = NULL; |
22 | uint32_t button = get_mouse_button(*argv, &message); | 22 | uint32_t button = get_mouse_button(*argv, &message); |
23 | if (message) { | 23 | if (message) { |
24 | error = cmd_results_new(CMD_INVALID, message); | 24 | error = cmd_results_new(CMD_INVALID, "%s", message); |
25 | free(message); | 25 | free(message); |
26 | return error; | 26 | return error; |
27 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN | 27 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN |
diff --git a/sway/commands/input/scroll_button_lock.c b/sway/commands/input/scroll_button_lock.c new file mode 100644 index 00000000..f96b6514 --- /dev/null +++ b/sway/commands/input/scroll_button_lock.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #include <libinput.h> | ||
2 | #include <string.h> | ||
3 | #include <strings.h> | ||
4 | #include "sway/config.h" | ||
5 | #include "sway/commands.h" | ||
6 | #include "sway/input/input-manager.h" | ||
7 | #include "util.h" | ||
8 | |||
9 | struct cmd_results *input_cmd_scroll_button_lock(int argc, char **argv) { | ||
10 | struct cmd_results *error = NULL; | ||
11 | if ((error = checkarg(argc, "scroll_button_lock", EXPECTED_AT_LEAST, 1))) { | ||
12 | return error; | ||
13 | } | ||
14 | struct input_config *ic = config->handler_context.input_config; | ||
15 | if (!ic) { | ||
16 | return cmd_results_new(CMD_FAILURE, "No input device defined."); | ||
17 | } | ||
18 | |||
19 | if (parse_boolean(argv[0], true)) { | ||
20 | ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED; | ||
21 | } else { | ||
22 | ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED; | ||
23 | } | ||
24 | |||
25 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
26 | } | ||
diff --git a/sway/commands/input/xkb_file.c b/sway/commands/input/xkb_file.c index 493f94fb..056f00e5 100644 --- a/sway/commands/input/xkb_file.c +++ b/sway/commands/input/xkb_file.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <unistd.h> | 1 | #include <unistd.h> |
3 | #include <errno.h> | 2 | #include <errno.h> |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c index 22626517..1d01886c 100644 --- a/sway/commands/input/xkb_layout.c +++ b/sway/commands/input/xkb_layout.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c index f4a33de3..a9144a8a 100644 --- a/sway/commands/input/xkb_model.c +++ b/sway/commands/input/xkb_model.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c index 87d3e60c..bbe848fe 100644 --- a/sway/commands/input/xkb_numlock.c +++ b/sway/commands/input/xkb_numlock.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "util.h" | 3 | #include "util.h" |
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c index d609293f..7ca20777 100644 --- a/sway/commands/input/xkb_options.c +++ b/sway/commands/input/xkb_options.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c index 3b59622c..8fbd26fb 100644 --- a/sway/commands/input/xkb_rules.c +++ b/sway/commands/input/xkb_rules.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c index d6548a68..ecac8e6c 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c | |||
@@ -1,10 +1,15 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include <wlr/interfaces/wlr_keyboard.h> | ||
3 | #include "sway/config.h" | 3 | #include "sway/config.h" |
4 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
5 | #include "sway/input/input-manager.h" | 5 | #include "sway/input/input-manager.h" |
6 | #include "log.h" | 6 | #include "log.h" |
7 | 7 | ||
8 | struct xkb_switch_layout_action { | ||
9 | struct wlr_keyboard *keyboard; | ||
10 | xkb_layout_index_t layout; | ||
11 | }; | ||
12 | |||
8 | static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { | 13 | static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { |
9 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); | 14 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); |
10 | if (idx >= num_layouts) { | 15 | if (idx >= num_layouts) { |
@@ -28,10 +33,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) { | |||
28 | return layout_idx; | 33 | return layout_idx; |
29 | } | 34 | } |
30 | 35 | ||
31 | static void switch_layout_relative(struct wlr_keyboard *kbd, int dir) { | 36 | static xkb_layout_index_t get_layout_relative(struct wlr_keyboard *kbd, int dir) { |
32 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); | 37 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); |
33 | xkb_layout_index_t idx = get_current_layout_index(kbd); | 38 | xkb_layout_index_t idx = get_current_layout_index(kbd); |
34 | switch_layout(kbd, (idx + num_layouts + dir) % num_layouts); | 39 | return (idx + num_layouts + dir) % num_layouts; |
35 | } | 40 | } |
36 | 41 | ||
37 | struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | 42 | struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { |
@@ -66,6 +71,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | |||
66 | relative = 0; | 71 | relative = 0; |
67 | } | 72 | } |
68 | 73 | ||
74 | struct xkb_switch_layout_action *actions = calloc( | ||
75 | wl_list_length(&server.input->devices), | ||
76 | sizeof(struct xkb_switch_layout_action)); | ||
77 | size_t actions_len = 0; | ||
78 | |||
79 | if (!actions) { | ||
80 | return cmd_results_new(CMD_FAILURE, "Unable to allocate actions"); | ||
81 | } | ||
82 | |||
83 | /* Calculate new indexes first because switching a layout in one | ||
84 | keyboard may result in a change on other keyboards as well because | ||
85 | of keyboard groups. */ | ||
69 | struct sway_input_device *dev; | 86 | struct sway_input_device *dev; |
70 | wl_list_for_each(dev, &server.input->devices, link) { | 87 | wl_list_for_each(dev, &server.input->devices, link) { |
71 | if (strcmp(ic->identifier, "*") != 0 && | 88 | if (strcmp(ic->identifier, "*") != 0 && |
@@ -76,12 +93,22 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | |||
76 | if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { | 93 | if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { |
77 | continue; | 94 | continue; |
78 | } | 95 | } |
96 | |||
97 | struct xkb_switch_layout_action *action = | ||
98 | &actions[actions_len++]; | ||
99 | |||
100 | action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device); | ||
79 | if (relative) { | 101 | if (relative) { |
80 | switch_layout_relative(dev->wlr_device->keyboard, relative); | 102 | action->layout = get_layout_relative(action->keyboard, relative); |
81 | } else { | 103 | } else { |
82 | switch_layout(dev->wlr_device->keyboard, layout); | 104 | action->layout = layout; |
83 | } | 105 | } |
84 | } | 106 | } |
85 | 107 | ||
108 | for (size_t i = 0; i < actions_len; i++) { | ||
109 | switch_layout(actions[i].keyboard, actions[i].layout); | ||
110 | } | ||
111 | free(actions); | ||
112 | |||
86 | return cmd_results_new(CMD_SUCCESS, NULL); | 113 | return cmd_results_new(CMD_SUCCESS, NULL); |
87 | } | 114 | } |
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c index d0e21d77..2d14ea9c 100644 --- a/sway/commands/input/xkb_variant.c +++ b/sway/commands/input/xkb_variant.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 2ba61b38..12ce4839 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c | |||
@@ -153,7 +153,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
153 | workspace->output); | 153 | workspace->output); |
154 | } | 154 | } |
155 | if (new_layout == L_NONE) { | 155 | if (new_layout == L_NONE) { |
156 | return cmd_results_new(CMD_INVALID, expected_syntax); | 156 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
157 | } | 157 | } |
158 | if (new_layout != old_layout) { | 158 | if (new_layout != old_layout) { |
159 | if (container) { | 159 | if (container) { |
diff --git a/sway/commands/mark.c b/sway/commands/mark.c index aa5f185c..2bfc86b3 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -59,7 +58,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { | |||
59 | } | 58 | } |
60 | 59 | ||
61 | free(mark); | 60 | free(mark); |
62 | container_update_marks_textures(container); | 61 | container_update_marks(container); |
63 | if (container->view) { | 62 | if (container->view) { |
64 | view_execute_criteria(container->view); | 63 | view_execute_criteria(container->view); |
65 | } | 64 | } |
diff --git a/sway/commands/mode.c b/sway/commands/mode.c index e23e4ee4..b3216967 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -11,10 +10,12 @@ | |||
11 | // Must be in order for the bsearch | 10 | // Must be in order for the bsearch |
12 | static const struct cmd_handler mode_handlers[] = { | 11 | static const struct cmd_handler mode_handlers[] = { |
13 | { "bindcode", cmd_bindcode }, | 12 | { "bindcode", cmd_bindcode }, |
13 | { "bindgesture", cmd_bindgesture }, | ||
14 | { "bindswitch", cmd_bindswitch }, | 14 | { "bindswitch", cmd_bindswitch }, |
15 | { "bindsym", cmd_bindsym }, | 15 | { "bindsym", cmd_bindsym }, |
16 | { "set", cmd_set }, | 16 | { "set", cmd_set }, |
17 | { "unbindcode", cmd_unbindcode }, | 17 | { "unbindcode", cmd_unbindcode }, |
18 | { "unbindgesture", cmd_unbindgesture }, | ||
18 | { "unbindswitch", cmd_unbindswitch }, | 19 | { "unbindswitch", cmd_unbindswitch }, |
19 | { "unbindsym", cmd_unbindsym }, | 20 | { "unbindsym", cmd_unbindsym }, |
20 | }; | 21 | }; |
@@ -59,6 +60,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { | |||
59 | mode->keycode_bindings = create_list(); | 60 | mode->keycode_bindings = create_list(); |
60 | mode->mouse_bindings = create_list(); | 61 | mode->mouse_bindings = create_list(); |
61 | mode->switch_bindings = create_list(); | 62 | mode->switch_bindings = create_list(); |
63 | mode->gesture_bindings = create_list(); | ||
62 | mode->pango = pango; | 64 | mode->pango = pango; |
63 | list_add(config->modes, mode); | 65 | list_add(config->modes, mode); |
64 | } | 66 | } |
diff --git a/sway/commands/move.c b/sway/commands/move.c index f2702fa1..8addf26e 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <math.h> | 2 | #include <math.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
@@ -206,9 +205,17 @@ static void container_move_to_workspace(struct sway_container *container, | |||
206 | container_detach(container); | 205 | container_detach(container); |
207 | workspace_add_floating(workspace, container); | 206 | workspace_add_floating(workspace, container); |
208 | container_handle_fullscreen_reparent(container); | 207 | container_handle_fullscreen_reparent(container); |
209 | // If changing output, center it within the workspace | 208 | // If changing output, adjust the coordinates of the window. |
210 | if (old_output != workspace->output && !container->pending.fullscreen_mode) { | 209 | if (old_output != workspace->output && !container->pending.fullscreen_mode) { |
211 | container_floating_move_to_center(container); | 210 | struct wlr_box workspace_box, old_workspace_box; |
211 | workspace_get_box(workspace, &workspace_box); | ||
212 | workspace_get_box(old_workspace, &old_workspace_box); | ||
213 | floating_fix_coordinates(container, &old_workspace_box, &workspace_box); | ||
214 | if (container->scratchpad && workspace->output) { | ||
215 | struct wlr_box output_box; | ||
216 | output_get_box(workspace->output, &output_box); | ||
217 | container->transform = workspace_box; | ||
218 | } | ||
212 | } | 219 | } |
213 | } else { | 220 | } else { |
214 | container_detach(container); | 221 | container_detach(container); |
@@ -462,7 +469,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, | |||
462 | if (strcasecmp(argv[1], "number") == 0) { | 469 | if (strcasecmp(argv[1], "number") == 0) { |
463 | // move [window|container] [to] "workspace number x" | 470 | // move [window|container] [to] "workspace number x" |
464 | if (argc < 3) { | 471 | if (argc < 3) { |
465 | return cmd_results_new(CMD_INVALID, expected_syntax); | 472 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
466 | } | 473 | } |
467 | if (!isdigit(argv[2][0])) { | 474 | if (!isdigit(argv[2][0])) { |
468 | return cmd_results_new(CMD_INVALID, | 475 | return cmd_results_new(CMD_INVALID, |
@@ -522,7 +529,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, | |||
522 | } | 529 | } |
523 | destination = &dest_con->node; | 530 | destination = &dest_con->node; |
524 | } else { | 531 | } else { |
525 | return cmd_results_new(CMD_INVALID, expected_syntax); | 532 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
526 | } | 533 | } |
527 | 534 | ||
528 | if (destination->type == N_CONTAINER && | 535 | if (destination->type == N_CONTAINER && |
@@ -686,6 +693,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) { | |||
686 | arrange_output(old_output); | 693 | arrange_output(old_output); |
687 | arrange_output(new_output); | 694 | arrange_output(new_output); |
688 | 695 | ||
696 | struct sway_seat *seat = config->handler_context.seat; | ||
697 | seat_consider_warp_to_focus(seat); | ||
698 | |||
689 | return cmd_results_new(CMD_SUCCESS, NULL); | 699 | return cmd_results_new(CMD_SUCCESS, NULL); |
690 | } | 700 | } |
691 | 701 | ||
@@ -759,15 +769,6 @@ static struct cmd_results *cmd_move_in_direction( | |||
759 | ipc_event_window(container, "move"); | 769 | ipc_event_window(container, "move"); |
760 | } | 770 | } |
761 | 771 | ||
762 | // Hack to re-focus container | ||
763 | seat_set_raw_focus(config->handler_context.seat, &new_ws->node); | ||
764 | seat_set_focus_container(config->handler_context.seat, container); | ||
765 | |||
766 | if (old_ws != new_ws) { | ||
767 | ipc_event_workspace(old_ws, new_ws, "focus"); | ||
768 | workspace_detect_urgent(old_ws); | ||
769 | workspace_detect_urgent(new_ws); | ||
770 | } | ||
771 | container_end_mouse_operation(container); | 772 | container_end_mouse_operation(container); |
772 | 773 | ||
773 | return cmd_results_new(CMD_SUCCESS, NULL); | 774 | return cmd_results_new(CMD_SUCCESS, NULL); |
@@ -788,15 +789,15 @@ static struct cmd_results *cmd_move_to_position_pointer( | |||
788 | struct wlr_output *output = wlr_output_layout_output_at( | 789 | struct wlr_output *output = wlr_output_layout_output_at( |
789 | root->output_layout, cursor->x, cursor->y); | 790 | root->output_layout, cursor->x, cursor->y); |
790 | if (output) { | 791 | if (output) { |
791 | struct wlr_box *box = | 792 | struct wlr_box box; |
792 | wlr_output_layout_get_box(root->output_layout, output); | 793 | wlr_output_layout_get_box(root->output_layout, output, &box); |
793 | lx = fmax(lx, box->x); | 794 | lx = fmax(lx, box.x); |
794 | ly = fmax(ly, box->y); | 795 | ly = fmax(ly, box.y); |
795 | if (lx + container->pending.width > box->x + box->width) { | 796 | if (lx + container->pending.width > box.x + box.width) { |
796 | lx = box->x + box->width - container->pending.width; | 797 | lx = box.x + box.width - container->pending.width; |
797 | } | 798 | } |
798 | if (ly + container->pending.height > box->y + box->height) { | 799 | if (ly + container->pending.height > box.y + box.height) { |
799 | ly = box->y + box->height - container->pending.height; | 800 | ly = box.y + box.height - container->pending.height; |
800 | } | 801 | } |
801 | } | 802 | } |
802 | 803 | ||
@@ -818,7 +819,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
818 | } | 819 | } |
819 | 820 | ||
820 | if (!argc) { | 821 | if (!argc) { |
821 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 822 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
822 | } | 823 | } |
823 | 824 | ||
824 | bool absolute = false; | 825 | bool absolute = false; |
@@ -828,19 +829,19 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
828 | ++argv; | 829 | ++argv; |
829 | } | 830 | } |
830 | if (!argc) { | 831 | if (!argc) { |
831 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 832 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
832 | } | 833 | } |
833 | if (strcmp(argv[0], "position") == 0) { | 834 | if (strcmp(argv[0], "position") == 0) { |
834 | --argc; | 835 | --argc; |
835 | ++argv; | 836 | ++argv; |
836 | } | 837 | } |
837 | if (!argc) { | 838 | if (!argc) { |
838 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 839 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
839 | } | 840 | } |
840 | if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || | 841 | if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || |
841 | strcmp(argv[0], "pointer") == 0) { | 842 | strcmp(argv[0], "pointer") == 0) { |
842 | if (absolute) { | 843 | if (absolute) { |
843 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 844 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
844 | } | 845 | } |
845 | return cmd_move_to_position_pointer(container); | 846 | return cmd_move_to_position_pointer(container); |
846 | } else if (strcmp(argv[0], "center") == 0) { | 847 | } else if (strcmp(argv[0], "center") == 0) { |
@@ -862,7 +863,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
862 | } | 863 | } |
863 | 864 | ||
864 | if (argc < 2) { | 865 | if (argc < 2) { |
865 | return cmd_results_new(CMD_FAILURE, expected_position_syntax); | 866 | return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); |
866 | } | 867 | } |
867 | 868 | ||
868 | struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; | 869 | struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; |
@@ -874,13 +875,17 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
874 | return cmd_results_new(CMD_INVALID, "Invalid x position specified"); | 875 | return cmd_results_new(CMD_INVALID, "Invalid x position specified"); |
875 | } | 876 | } |
876 | 877 | ||
878 | if (argc < 1) { | ||
879 | return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); | ||
880 | } | ||
881 | |||
877 | struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; | 882 | struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; |
878 | // Y direction | 883 | // Y direction |
879 | num_consumed_args = parse_movement_amount(argc, argv, &ly); | 884 | num_consumed_args = parse_movement_amount(argc, argv, &ly); |
880 | argc -= num_consumed_args; | 885 | argc -= num_consumed_args; |
881 | argv += num_consumed_args; | 886 | argv += num_consumed_args; |
882 | if (argc > 0) { | 887 | if (argc > 0) { |
883 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 888 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
884 | } | 889 | } |
885 | if (ly.unit == MOVEMENT_UNIT_INVALID) { | 890 | if (ly.unit == MOVEMENT_UNIT_INVALID) { |
886 | return cmd_results_new(CMD_INVALID, "Invalid y position specified"); | 891 | return cmd_results_new(CMD_INVALID, "Invalid y position specified"); |
@@ -1026,13 +1031,13 @@ struct cmd_results *cmd_move(int argc, char **argv) { | |||
1026 | } | 1031 | } |
1027 | 1032 | ||
1028 | if (!argc) { | 1033 | if (!argc) { |
1029 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1034 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1030 | } | 1035 | } |
1031 | 1036 | ||
1032 | // Only `move [window|container] [to] workspace` supports | 1037 | // Only `move [window|container] [to] workspace` supports |
1033 | // `--no-auto-back-and-forth` so treat others as invalid syntax | 1038 | // `--no-auto-back-and-forth` so treat others as invalid syntax |
1034 | if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { | 1039 | if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { |
1035 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1040 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1036 | } | 1041 | } |
1037 | 1042 | ||
1038 | if (strcasecmp(argv[0], "workspace") == 0 || | 1043 | if (strcasecmp(argv[0], "workspace") == 0 || |
@@ -1046,5 +1051,5 @@ struct cmd_results *cmd_move(int argc, char **argv) { | |||
1046 | strcasecmp(argv[1], "position") == 0)) { | 1051 | strcasecmp(argv[1], "position") == 0)) { |
1047 | return cmd_move_to_position(argc, argv); | 1052 | return cmd_move_to_position(argc, argv); |
1048 | } | 1053 | } |
1049 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1054 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1050 | } | 1055 | } |
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c index 2001e04f..ccfdec82 100644 --- a/sway/commands/no_focus.c +++ b/sway/commands/no_focus.c | |||
@@ -13,7 +13,7 @@ struct cmd_results *cmd_no_focus(int argc, char **argv) { | |||
13 | char *err_str = NULL; | 13 | char *err_str = NULL; |
14 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | 14 | struct criteria *criteria = criteria_parse(argv[0], &err_str); |
15 | if (!criteria) { | 15 | if (!criteria) { |
16 | error = cmd_results_new(CMD_INVALID, err_str); | 16 | error = cmd_results_new(CMD_INVALID, "%s", err_str); |
17 | free(err_str); | 17 | free(err_str); |
18 | return error; | 18 | return error; |
19 | } | 19 | } |
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index 96e6228e..610cecc6 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c | |||
@@ -37,6 +37,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { | |||
37 | } | 37 | } |
38 | 38 | ||
39 | con->alpha = val; | 39 | con->alpha = val; |
40 | container_damage_whole(con); | 40 | container_update(con); |
41 | |||
41 | return cmd_results_new(CMD_SUCCESS, NULL); | 42 | return cmd_results_new(CMD_SUCCESS, NULL); |
42 | } | 43 | } |
diff --git a/sway/commands/output.c b/sway/commands/output.c index 4418f23f..5e5d31b3 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c | |||
@@ -15,8 +15,11 @@ static const struct cmd_handler output_handlers[] = { | |||
15 | { "enable", output_cmd_enable }, | 15 | { "enable", output_cmd_enable }, |
16 | { "max_render_time", output_cmd_max_render_time }, | 16 | { "max_render_time", output_cmd_max_render_time }, |
17 | { "mode", output_cmd_mode }, | 17 | { "mode", output_cmd_mode }, |
18 | { "modeline", output_cmd_modeline }, | ||
18 | { "pos", output_cmd_position }, | 19 | { "pos", output_cmd_position }, |
19 | { "position", output_cmd_position }, | 20 | { "position", output_cmd_position }, |
21 | { "power", output_cmd_power }, | ||
22 | { "render_bit_depth", output_cmd_render_bit_depth }, | ||
20 | { "res", output_cmd_mode }, | 23 | { "res", output_cmd_mode }, |
21 | { "resolution", output_cmd_mode }, | 24 | { "resolution", output_cmd_mode }, |
22 | { "scale", output_cmd_scale }, | 25 | { "scale", output_cmd_scale }, |
@@ -24,6 +27,7 @@ static const struct cmd_handler output_handlers[] = { | |||
24 | { "subpixel", output_cmd_subpixel }, | 27 | { "subpixel", output_cmd_subpixel }, |
25 | { "toggle", output_cmd_toggle }, | 28 | { "toggle", output_cmd_toggle }, |
26 | { "transform", output_cmd_transform }, | 29 | { "transform", output_cmd_transform }, |
30 | { "unplug", output_cmd_unplug }, | ||
27 | }; | 31 | }; |
28 | 32 | ||
29 | struct cmd_results *cmd_output(int argc, char **argv) { | 33 | struct cmd_results *cmd_output(int argc, char **argv) { |
@@ -32,9 +36,9 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
32 | return error; | 36 | return error; |
33 | } | 37 | } |
34 | 38 | ||
35 | // The NOOP-1 output is a dummy output used when there's no outputs | 39 | // The HEADLESS-1 output is a dummy output used when there's no outputs |
36 | // connected. It should never be configured. | 40 | // connected. It should never be configured. |
37 | if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { | 41 | if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) { |
38 | return cmd_results_new(CMD_FAILURE, | 42 | return cmd_results_new(CMD_FAILURE, |
39 | "Refusing to configure the no op output"); | 43 | "Refusing to configure the no op output"); |
40 | } | 44 | } |
@@ -51,7 +55,7 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
51 | if (!sway_output) { | 55 | if (!sway_output) { |
52 | return cmd_results_new(CMD_FAILURE, "Unknown output"); | 56 | return cmd_results_new(CMD_FAILURE, "Unknown output"); |
53 | } | 57 | } |
54 | if (sway_output == root->noop_output) { | 58 | if (sway_output == root->fallback_output) { |
55 | return cmd_results_new(CMD_FAILURE, | 59 | return cmd_results_new(CMD_FAILURE, |
56 | "Refusing to configure the no op output"); | 60 | "Refusing to configure the no op output"); |
57 | } | 61 | } |
@@ -99,15 +103,18 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
99 | 103 | ||
100 | bool background = output->background; | 104 | bool background = output->background; |
101 | 105 | ||
102 | output = store_output_config(output); | 106 | store_output_config(output); |
103 | 107 | ||
104 | // If reloading, the output configs will be applied after reading the | 108 | // If reloading, the output configs will be applied after reading the |
105 | // entire config and before the deferred commands so that an auto generated | 109 | // entire config and before the deferred commands so that an auto generated |
106 | // workspace name is not given to re-enabled outputs. | 110 | // workspace name is not given to re-enabled outputs. |
107 | if (!config->reloading && !config->validating) { | 111 | if (!config->reloading && !config->validating) { |
108 | apply_output_config_to_outputs(output); | 112 | apply_all_output_configs(); |
109 | if (background) { | 113 | if (background) { |
110 | spawn_swaybg(); | 114 | if (!spawn_swaybg()) { |
115 | return cmd_results_new(CMD_FAILURE, | ||
116 | "Failed to apply background configuration"); | ||
117 | } | ||
111 | } | 118 | } |
112 | } | 119 | } |
113 | 120 | ||
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 68ee9fe1..55bd7671 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <libgen.h> | 1 | #include <libgen.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -102,19 +101,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { | |||
102 | } | 101 | } |
103 | 102 | ||
104 | char *conf_path = dirname(conf); | 103 | char *conf_path = dirname(conf); |
105 | char *rel_path = src; | 104 | char *real_src = malloc(strlen(conf_path) + strlen(src) + 2); |
106 | src = malloc(strlen(conf_path) + strlen(src) + 2); | 105 | if (!real_src) { |
107 | if (!src) { | 106 | free(src); |
108 | free(rel_path); | ||
109 | free(conf); | 107 | free(conf); |
110 | sway_log(SWAY_ERROR, "Unable to allocate memory"); | 108 | sway_log(SWAY_ERROR, "Unable to allocate memory"); |
111 | return cmd_results_new(CMD_FAILURE, | 109 | return cmd_results_new(CMD_FAILURE, |
112 | "Unable to allocate resources"); | 110 | "Unable to allocate resources"); |
113 | } | 111 | } |
114 | 112 | ||
115 | sprintf(src, "%s/%s", conf_path, rel_path); | 113 | snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src); |
116 | free(rel_path); | 114 | free(src); |
117 | free(conf); | 115 | free(conf); |
116 | src = real_src; | ||
118 | } | 117 | } |
119 | 118 | ||
120 | bool can_access = access(src, F_OK) != -1; | 119 | bool can_access = access(src, F_OK) != -1; |
@@ -123,7 +122,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { | |||
123 | src); | 122 | src); |
124 | config_add_swaynag_warning("Unable to access background file '%s'", | 123 | config_add_swaynag_warning("Unable to access background file '%s'", |
125 | src); | 124 | src); |
125 | struct cmd_results *result = cmd_results_new(CMD_FAILURE, | ||
126 | "unable to access background file '%s'", src); | ||
126 | free(src); | 127 | free(src); |
128 | return result; | ||
127 | } else { | 129 | } else { |
128 | output->background = src; | 130 | output->background = src; |
129 | output->background_option = strdup(mode); | 131 | output->background_option = strdup(mode); |
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c index 638c0ade..c7adbd58 100644 --- a/sway/commands/output/dpms.c +++ b/sway/commands/output/dpms.c | |||
@@ -1,45 +1,8 @@ | |||
1 | #include "log.h" | ||
1 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
2 | #include "sway/config.h" | ||
3 | #include "sway/output.h" | ||
4 | #include "util.h" | ||
5 | #include <strings.h> | ||
6 | 3 | ||
7 | struct cmd_results *output_cmd_dpms(int argc, char **argv) { | 4 | struct cmd_results *output_cmd_dpms(int argc, char **argv) { |
8 | if (!config->handler_context.output_config) { | 5 | sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, " |
9 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | 6 | "use \"output power\" instead"); |
10 | } | 7 | return output_cmd_power(argc, argv); |
11 | if (!argc) { | ||
12 | return cmd_results_new(CMD_INVALID, "Missing dpms argument."); | ||
13 | } | ||
14 | |||
15 | enum config_dpms current_dpms = DPMS_ON; | ||
16 | |||
17 | if (strcasecmp(argv[0], "toggle") == 0) { | ||
18 | |||
19 | const char *oc_name = config->handler_context.output_config->name; | ||
20 | if (strcmp(oc_name, "*") == 0) { | ||
21 | return cmd_results_new(CMD_INVALID, | ||
22 | "Cannot apply toggle to all outputs."); | ||
23 | } | ||
24 | |||
25 | struct sway_output *sway_output = all_output_by_name_or_id(oc_name); | ||
26 | if (!sway_output || !sway_output->wlr_output) { | ||
27 | return cmd_results_new(CMD_FAILURE, | ||
28 | "Cannot apply toggle to unknown output %s", oc_name); | ||
29 | } | ||
30 | |||
31 | if (sway_output->enabled && !sway_output->wlr_output->enabled) { | ||
32 | current_dpms = DPMS_OFF; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | if (parse_boolean(argv[0], current_dpms == DPMS_ON)) { | ||
37 | config->handler_context.output_config->dpms_state = DPMS_ON; | ||
38 | } else { | ||
39 | config->handler_context.output_config->dpms_state = DPMS_OFF; | ||
40 | } | ||
41 | |||
42 | config->handler_context.leftovers.argc = argc - 1; | ||
43 | config->handler_context.leftovers.argv = argv + 1; | ||
44 | return NULL; | ||
45 | } | 8 | } |
diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c index 5b710713..019d625a 100644 --- a/sway/commands/output/mode.c +++ b/sway/commands/output/mode.c | |||
@@ -20,6 +20,9 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) { | |||
20 | output->custom_mode = 0; | 20 | output->custom_mode = 0; |
21 | } | 21 | } |
22 | 22 | ||
23 | // Reset custom modeline, if any | ||
24 | output->drm_mode.type = 0; | ||
25 | |||
23 | char *end; | 26 | char *end; |
24 | output->width = strtol(*argv, &end, 10); | 27 | output->width = strtol(*argv, &end, 10); |
25 | if (*end) { | 28 | if (*end) { |
@@ -58,3 +61,58 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) { | |||
58 | return NULL; | 61 | return NULL; |
59 | } | 62 | } |
60 | 63 | ||
64 | static bool parse_modeline(char **argv, drmModeModeInfo *mode) { | ||
65 | mode->type = DRM_MODE_TYPE_USERDEF; | ||
66 | mode->clock = strtof(argv[0], NULL) * 1000; | ||
67 | mode->hdisplay = strtol(argv[1], NULL, 10); | ||
68 | mode->hsync_start = strtol(argv[2], NULL, 10); | ||
69 | mode->hsync_end = strtol(argv[3], NULL, 10); | ||
70 | mode->htotal = strtol(argv[4], NULL, 10); | ||
71 | mode->vdisplay = strtol(argv[5], NULL, 10); | ||
72 | mode->vsync_start = strtol(argv[6], NULL, 10); | ||
73 | mode->vsync_end = strtol(argv[7], NULL, 10); | ||
74 | mode->vtotal = strtol(argv[8], NULL, 10); | ||
75 | |||
76 | mode->vrefresh = mode->clock * 1000.0 * 1000.0 | ||
77 | / mode->htotal / mode->vtotal; | ||
78 | if (strcasecmp(argv[9], "+hsync") == 0) { | ||
79 | mode->flags |= DRM_MODE_FLAG_PHSYNC; | ||
80 | } else if (strcasecmp(argv[9], "-hsync") == 0) { | ||
81 | mode->flags |= DRM_MODE_FLAG_NHSYNC; | ||
82 | } else { | ||
83 | return false; | ||
84 | } | ||
85 | |||
86 | if (strcasecmp(argv[10], "+vsync") == 0) { | ||
87 | mode->flags |= DRM_MODE_FLAG_PVSYNC; | ||
88 | } else if (strcasecmp(argv[10], "-vsync") == 0) { | ||
89 | mode->flags |= DRM_MODE_FLAG_NVSYNC; | ||
90 | } else { | ||
91 | return false; | ||
92 | } | ||
93 | |||
94 | snprintf(mode->name, sizeof(mode->name), "%dx%d@%d", | ||
95 | mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000); | ||
96 | |||
97 | return true; | ||
98 | } | ||
99 | |||
100 | struct cmd_results *output_cmd_modeline(int argc, char **argv) { | ||
101 | if (!config->handler_context.output_config) { | ||
102 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
103 | } | ||
104 | if (!argc) { | ||
105 | return cmd_results_new(CMD_INVALID, "Missing modeline argument."); | ||
106 | } | ||
107 | |||
108 | struct output_config *output = config->handler_context.output_config; | ||
109 | |||
110 | if (argc != 11 || !parse_modeline(argv, &output->drm_mode)) { | ||
111 | return cmd_results_new(CMD_INVALID, "Invalid modeline"); | ||
112 | } | ||
113 | |||
114 | config->handler_context.leftovers.argc = argc - 12; | ||
115 | config->handler_context.leftovers.argv = argv + 12; | ||
116 | return NULL; | ||
117 | } | ||
118 | |||
diff --git a/sway/commands/output/power.c b/sway/commands/output/power.c new file mode 100644 index 00000000..e6ae2852 --- /dev/null +++ b/sway/commands/output/power.c | |||
@@ -0,0 +1,43 @@ | |||
1 | #include <strings.h> | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/output.h" | ||
5 | #include "util.h" | ||
6 | |||
7 | struct cmd_results *output_cmd_power(int argc, char **argv) { | ||
8 | if (!config->handler_context.output_config) { | ||
9 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
10 | } | ||
11 | if (argc == 0) { | ||
12 | return cmd_results_new(CMD_INVALID, "Missing power argument"); | ||
13 | } | ||
14 | |||
15 | bool current = true; | ||
16 | if (strcasecmp(argv[0], "toggle") == 0) { | ||
17 | const char *oc_name = config->handler_context.output_config->name; | ||
18 | if (strcmp(oc_name, "*") == 0) { | ||
19 | return cmd_results_new(CMD_INVALID, | ||
20 | "Cannot apply toggle to all outputs"); | ||
21 | } | ||
22 | |||
23 | struct sway_output *sway_output = all_output_by_name_or_id(oc_name); | ||
24 | if (!sway_output || !sway_output->wlr_output) { | ||
25 | return cmd_results_new(CMD_FAILURE, | ||
26 | "Cannot apply toggle to unknown output %s", oc_name); | ||
27 | } | ||
28 | |||
29 | if (sway_output->enabled && !sway_output->wlr_output->enabled) { | ||
30 | current = false; | ||
31 | } | ||
32 | } | ||
33 | |||
34 | if (parse_boolean(argv[0], current)) { | ||
35 | config->handler_context.output_config->power = 1; | ||
36 | } else { | ||
37 | config->handler_context.output_config->power = 0; | ||
38 | } | ||
39 | |||
40 | config->handler_context.leftovers.argc = argc - 1; | ||
41 | config->handler_context.leftovers.argv = argv + 1; | ||
42 | return NULL; | ||
43 | } | ||
diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c new file mode 100644 index 00000000..c419321e --- /dev/null +++ b/sway/commands/output/render_bit_depth.c | |||
@@ -0,0 +1,29 @@ | |||
1 | #include <drm_fourcc.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/config.h" | ||
5 | |||
6 | struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) { | ||
7 | if (!config->handler_context.output_config) { | ||
8 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
9 | } | ||
10 | if (!argc) { | ||
11 | return cmd_results_new(CMD_INVALID, "Missing bit depth argument."); | ||
12 | } | ||
13 | |||
14 | if (strcmp(*argv, "8") == 0) { | ||
15 | config->handler_context.output_config->render_bit_depth = | ||
16 | RENDER_BIT_DEPTH_8; | ||
17 | } else if (strcmp(*argv, "10") == 0) { | ||
18 | config->handler_context.output_config->render_bit_depth = | ||
19 | RENDER_BIT_DEPTH_10; | ||
20 | } else { | ||
21 | return cmd_results_new(CMD_INVALID, | ||
22 | "Invalid bit depth. Must be a value in (8|10)."); | ||
23 | } | ||
24 | |||
25 | config->handler_context.leftovers.argc = argc - 1; | ||
26 | config->handler_context.leftovers.argv = argv + 1; | ||
27 | return NULL; | ||
28 | } | ||
29 | |||
diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c index 6342d526..c6b72845 100644 --- a/sway/commands/output/toggle.c +++ b/sway/commands/output/toggle.c | |||
@@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) { | |||
29 | config->handler_context.output_config->enabled = 1; | 29 | config->handler_context.output_config->enabled = 1; |
30 | } | 30 | } |
31 | 31 | ||
32 | free(oc); | 32 | free_output_config(oc); |
33 | config->handler_context.leftovers.argc = argc; | 33 | config->handler_context.leftovers.argc = argc; |
34 | config->handler_context.leftovers.argv = argv; | 34 | config->handler_context.leftovers.argv = argv; |
35 | return NULL; | 35 | return NULL; |
diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c index f4fcc8c9..8db71bb3 100644 --- a/sway/commands/output/transform.c +++ b/sway/commands/output/transform.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <wlr/util/transform.h> | ||
2 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
3 | #include "sway/config.h" | 4 | #include "sway/config.h" |
4 | #include "log.h" | 5 | #include "log.h" |
diff --git a/sway/commands/output/unplug.c b/sway/commands/output/unplug.c new file mode 100644 index 00000000..dfef626f --- /dev/null +++ b/sway/commands/output/unplug.c | |||
@@ -0,0 +1,54 @@ | |||
1 | #include <strings.h> | ||
2 | #include <wlr/config.h> | ||
3 | #include <wlr/backend/headless.h> | ||
4 | #include <wlr/backend/wayland.h> | ||
5 | #if WLR_HAS_X11_BACKEND | ||
6 | #include <wlr/backend/x11.h> | ||
7 | #endif | ||
8 | #include "sway/commands.h" | ||
9 | #include "sway/config.h" | ||
10 | #include "sway/output.h" | ||
11 | |||
12 | static bool is_backend_allowed(struct wlr_backend *backend) { | ||
13 | if (wlr_backend_is_headless(backend)) { | ||
14 | return true; | ||
15 | } | ||
16 | if (wlr_backend_is_wl(backend)) { | ||
17 | return true; | ||
18 | } | ||
19 | #if WLR_HAS_X11_BACKEND | ||
20 | if (wlr_backend_is_x11(backend)) { | ||
21 | return true; | ||
22 | } | ||
23 | #endif | ||
24 | return false; | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * This command is intended for developer use only. | ||
29 | */ | ||
30 | struct cmd_results *output_cmd_unplug(int argc, char **argv) { | ||
31 | if (!config->handler_context.output_config) { | ||
32 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
33 | } | ||
34 | |||
35 | const char *oc_name = config->handler_context.output_config->name; | ||
36 | if (strcmp(oc_name, "*") == 0) { | ||
37 | return cmd_results_new(CMD_INVALID, "Won't unplug all outputs"); | ||
38 | } | ||
39 | |||
40 | struct sway_output *sway_output = all_output_by_name_or_id(oc_name); | ||
41 | if (!sway_output) { | ||
42 | return cmd_results_new(CMD_INVALID, | ||
43 | "Cannot unplug unknown output %s", oc_name); | ||
44 | } | ||
45 | |||
46 | if (!is_backend_allowed(sway_output->wlr_output->backend)) { | ||
47 | return cmd_results_new(CMD_INVALID, | ||
48 | "Can only unplug outputs with headless, wayland or x11 backend"); | ||
49 | } | ||
50 | |||
51 | wlr_output_destroy(sway_output->wlr_output); | ||
52 | |||
53 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
54 | } | ||
diff --git a/sway/commands/primary_selection.c b/sway/commands/primary_selection.c new file mode 100644 index 00000000..9e2689c2 --- /dev/null +++ b/sway/commands/primary_selection.c | |||
@@ -0,0 +1,25 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/commands.h" | ||
5 | #include "util.h" | ||
6 | |||
7 | struct cmd_results *cmd_primary_selection(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "primary_selection", EXPECTED_EQUAL_TO, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | |||
13 | bool primary_selection = parse_boolean(argv[0], true); | ||
14 | |||
15 | // config->primary_selection is reset to the previous value on reload in | ||
16 | // load_main_config() | ||
17 | if (config->reloading && config->primary_selection != primary_selection) { | ||
18 | return cmd_results_new(CMD_FAILURE, | ||
19 | "primary_selection can only be enabled/disabled at launch"); | ||
20 | } | ||
21 | |||
22 | config->primary_selection = primary_selection; | ||
23 | |||
24 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
25 | } | ||
diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 3c994d54..6c0aac26 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -9,9 +8,8 @@ | |||
9 | #include "list.h" | 8 | #include "list.h" |
10 | #include "log.h" | 9 | #include "log.h" |
11 | 10 | ||
12 | static void rebuild_textures_iterator(struct sway_container *con, void *data) { | 11 | static void title_bar_update_iterator(struct sway_container *con, void *data) { |
13 | container_update_marks_textures(con); | 12 | container_update_title_bar(con); |
14 | container_update_title_textures(con); | ||
15 | } | 13 | } |
16 | 14 | ||
17 | static void do_reload(void *data) { | 15 | static void do_reload(void *data) { |
@@ -48,8 +46,7 @@ static void do_reload(void *data) { | |||
48 | } | 46 | } |
49 | list_free_items_and_destroy(bar_ids); | 47 | list_free_items_and_destroy(bar_ids); |
50 | 48 | ||
51 | config_update_font_height(true); | 49 | root_for_each_container(title_bar_update_iterator, NULL); |
52 | root_for_each_container(rebuild_textures_iterator, NULL); | ||
53 | 50 | ||
54 | arrange_root(); | 51 | arrange_root(); |
55 | } | 52 | } |
diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 3b855fdf..0d36cc21 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "sway/config.h" | 7 | #include "sway/config.h" |
8 | #include "sway/ipc-server.h" | 8 | #include "sway/ipc-server.h" |
9 | #include "sway/output.h" | 9 | #include "sway/output.h" |
10 | #include "sway/desktop/launcher.h" | ||
10 | #include "sway/tree/container.h" | 11 | #include "sway/tree/container.h" |
11 | #include "sway/tree/workspace.h" | 12 | #include "sway/tree/workspace.h" |
12 | #include "sway/tree/root.h" | 13 | #include "sway/tree/root.h" |
@@ -25,7 +26,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { | |||
25 | "Can't run this command while there's no outputs connected."); | 26 | "Can't run this command while there's no outputs connected."); |
26 | } | 27 | } |
27 | if (strcasecmp(argv[0], "workspace") != 0) { | 28 | if (strcasecmp(argv[0], "workspace") != 0) { |
28 | return cmd_results_new(CMD_INVALID, expected_syntax); | 29 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
29 | } | 30 | } |
30 | 31 | ||
31 | int argn = 1; | 32 | int argn = 1; |
@@ -64,7 +65,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { | |||
64 | ++argn; // move past "to" | 65 | ++argn; // move past "to" |
65 | 66 | ||
66 | if (argn >= argc) { | 67 | if (argn >= argc) { |
67 | return cmd_results_new(CMD_INVALID, expected_syntax); | 68 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
68 | } | 69 | } |
69 | 70 | ||
70 | char *new_name = join_args(argv + argn, argc - argn); | 71 | char *new_name = join_args(argv + argn, argc - argn); |
@@ -91,8 +92,6 @@ struct cmd_results *cmd_rename(int argc, char **argv) { | |||
91 | 92 | ||
92 | sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); | 93 | sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); |
93 | 94 | ||
94 | root_rename_pid_workspaces(workspace->name, new_name); | ||
95 | |||
96 | free(workspace->name); | 95 | free(workspace->name); |
97 | workspace->name = new_name; | 96 | workspace->name = new_name; |
98 | 97 | ||
diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 425069de..32b746ea 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c | |||
@@ -75,6 +75,10 @@ void container_resize_tiled(struct sway_container *con, | |||
75 | return; | 75 | return; |
76 | } | 76 | } |
77 | 77 | ||
78 | if (container_is_scratchpad_hidden_or_child(con)) { | ||
79 | return; | ||
80 | } | ||
81 | |||
78 | // For HORIZONTAL or VERTICAL, we are growing in two directions so select | 82 | // For HORIZONTAL or VERTICAL, we are growing in two directions so select |
79 | // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. | 83 | // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. |
80 | // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to | 84 | // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to |
@@ -249,16 +253,35 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis, | |||
249 | struct movement_amount *amount) { | 253 | struct movement_amount *amount) { |
250 | struct sway_container *current = config->handler_context.container; | 254 | struct sway_container *current = config->handler_context.container; |
251 | 255 | ||
256 | if (container_is_scratchpad_hidden_or_child(current)) { | ||
257 | return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container"); | ||
258 | } | ||
259 | |||
252 | if (amount->unit == MOVEMENT_UNIT_DEFAULT) { | 260 | if (amount->unit == MOVEMENT_UNIT_DEFAULT) { |
253 | amount->unit = MOVEMENT_UNIT_PPT; | 261 | amount->unit = MOVEMENT_UNIT_PPT; |
254 | } | 262 | } |
255 | if (amount->unit == MOVEMENT_UNIT_PPT) { | 263 | if (amount->unit == MOVEMENT_UNIT_PPT) { |
264 | struct sway_container *parent = current->pending.parent; | ||
256 | float pct = amount->amount / 100.0f; | 265 | float pct = amount->amount / 100.0f; |
257 | 266 | ||
258 | if (is_horizontal(axis)) { | 267 | if (is_horizontal(axis)) { |
259 | amount->amount = (float)current->pending.width * pct; | 268 | while (parent && parent->pending.layout != L_HORIZ) { |
269 | parent = parent->pending.parent; | ||
270 | } | ||
271 | if (parent) { | ||
272 | amount->amount = (float)parent->pending.width * pct; | ||
273 | } else { | ||
274 | amount->amount = (float)current->pending.workspace->width * pct; | ||
275 | } | ||
260 | } else { | 276 | } else { |
261 | amount->amount = (float)current->pending.height * pct; | 277 | while (parent && parent->pending.layout != L_VERT) { |
278 | parent = parent->pending.parent; | ||
279 | } | ||
280 | if (parent) { | ||
281 | amount->amount = (float)parent->pending.height * pct; | ||
282 | } else { | ||
283 | amount->amount = (float)current->pending.workspace->height * pct; | ||
284 | } | ||
262 | } | 285 | } |
263 | } | 286 | } |
264 | 287 | ||
@@ -277,6 +300,11 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis, | |||
277 | */ | 300 | */ |
278 | static struct cmd_results *resize_set_tiled(struct sway_container *con, | 301 | static struct cmd_results *resize_set_tiled(struct sway_container *con, |
279 | struct movement_amount *width, struct movement_amount *height) { | 302 | struct movement_amount *width, struct movement_amount *height) { |
303 | |||
304 | if (container_is_scratchpad_hidden_or_child(con)) { | ||
305 | return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container"); | ||
306 | } | ||
307 | |||
280 | if (width->amount) { | 308 | if (width->amount) { |
281 | if (width->unit == MOVEMENT_UNIT_PPT || | 309 | if (width->unit == MOVEMENT_UNIT_PPT || |
282 | width->unit == MOVEMENT_UNIT_DEFAULT) { | 310 | width->unit == MOVEMENT_UNIT_DEFAULT) { |
@@ -415,7 +443,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { | |||
415 | argc -= num_consumed_args; | 443 | argc -= num_consumed_args; |
416 | argv += num_consumed_args; | 444 | argv += num_consumed_args; |
417 | if (width.unit == MOVEMENT_UNIT_INVALID) { | 445 | if (width.unit == MOVEMENT_UNIT_INVALID) { |
418 | return cmd_results_new(CMD_INVALID, usage); | 446 | return cmd_results_new(CMD_INVALID, "%s", usage); |
419 | } | 447 | } |
420 | } | 448 | } |
421 | 449 | ||
@@ -427,10 +455,10 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { | |||
427 | } | 455 | } |
428 | int num_consumed_args = parse_movement_amount(argc, argv, &height); | 456 | int num_consumed_args = parse_movement_amount(argc, argv, &height); |
429 | if (argc > num_consumed_args) { | 457 | if (argc > num_consumed_args) { |
430 | return cmd_results_new(CMD_INVALID, usage); | 458 | return cmd_results_new(CMD_INVALID, "%s", usage); |
431 | } | 459 | } |
432 | if (width.unit == MOVEMENT_UNIT_INVALID) { | 460 | if (width.unit == MOVEMENT_UNIT_INVALID) { |
433 | return cmd_results_new(CMD_INVALID, usage); | 461 | return cmd_results_new(CMD_INVALID, "%s", usage); |
434 | } | 462 | } |
435 | } | 463 | } |
436 | 464 | ||
@@ -462,7 +490,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, | |||
462 | "[<amount> px|ppt [or <amount> px|ppt]]'"; | 490 | "[<amount> px|ppt [or <amount> px|ppt]]'"; |
463 | uint32_t axis = parse_resize_axis(*argv); | 491 | uint32_t axis = parse_resize_axis(*argv); |
464 | if (axis == WLR_EDGE_NONE) { | 492 | if (axis == WLR_EDGE_NONE) { |
465 | return cmd_results_new(CMD_INVALID, usage); | 493 | return cmd_results_new(CMD_INVALID, "%s", usage); |
466 | } | 494 | } |
467 | --argc; ++argv; | 495 | --argc; ++argv; |
468 | 496 | ||
@@ -473,7 +501,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, | |||
473 | argc -= num_consumed_args; | 501 | argc -= num_consumed_args; |
474 | argv += num_consumed_args; | 502 | argv += num_consumed_args; |
475 | if (first_amount.unit == MOVEMENT_UNIT_INVALID) { | 503 | if (first_amount.unit == MOVEMENT_UNIT_INVALID) { |
476 | return cmd_results_new(CMD_INVALID, usage); | 504 | return cmd_results_new(CMD_INVALID, "%s", usage); |
477 | } | 505 | } |
478 | } else { | 506 | } else { |
479 | first_amount.amount = 10; | 507 | first_amount.amount = 10; |
@@ -483,7 +511,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, | |||
483 | // "or" | 511 | // "or" |
484 | if (argc) { | 512 | if (argc) { |
485 | if (strcmp(*argv, "or") != 0) { | 513 | if (strcmp(*argv, "or") != 0) { |
486 | return cmd_results_new(CMD_INVALID, usage); | 514 | return cmd_results_new(CMD_INVALID, "%s", usage); |
487 | } | 515 | } |
488 | --argc; ++argv; | 516 | --argc; ++argv; |
489 | } | 517 | } |
@@ -493,10 +521,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, | |||
493 | if (argc) { | 521 | if (argc) { |
494 | int num_consumed_args = parse_movement_amount(argc, argv, &second_amount); | 522 | int num_consumed_args = parse_movement_amount(argc, argv, &second_amount); |
495 | if (argc > num_consumed_args) { | 523 | if (argc > num_consumed_args) { |
496 | return cmd_results_new(CMD_INVALID, usage); | 524 | return cmd_results_new(CMD_INVALID, "%s", usage); |
497 | } | 525 | } |
498 | if (second_amount.unit == MOVEMENT_UNIT_INVALID) { | 526 | if (second_amount.unit == MOVEMENT_UNIT_INVALID) { |
499 | return cmd_results_new(CMD_INVALID, usage); | 527 | return cmd_results_new(CMD_INVALID, "%s", usage); |
500 | } | 528 | } |
501 | } else { | 529 | } else { |
502 | second_amount.amount = 0; | 530 | second_amount.amount = 0; |
@@ -566,5 +594,5 @@ struct cmd_results *cmd_resize(int argc, char **argv) { | |||
566 | const char usage[] = "Expected 'resize <shrink|grow> " | 594 | const char usage[] = "Expected 'resize <shrink|grow> " |
567 | "<width|height|up|down|left|right> [<amount>] [px|ppt]'"; | 595 | "<width|height|up|down|left|right> [<amount>] [px|ppt]'"; |
568 | 596 | ||
569 | return cmd_results_new(CMD_INVALID, usage); | 597 | return cmd_results_new(CMD_INVALID, "%s", usage); |
570 | } | 598 | } |
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c index 00bfdab6..47d18546 100644 --- a/sway/commands/seat/attach.c +++ b/sway/commands/seat/attach.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 749235eb..df7c379d 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <linux/input-event-codes.h> | 1 | #include <linux/input-event-codes.h> |
3 | 2 | ||
4 | #include <strings.h> | 3 | #include <strings.h> |
@@ -18,7 +17,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, | |||
18 | int argc, char **argv) { | 17 | int argc, char **argv) { |
19 | if (strcasecmp(argv[0], "move") == 0) { | 18 | if (strcasecmp(argv[0], "move") == 0) { |
20 | if (argc < 3) { | 19 | if (argc < 3) { |
21 | return cmd_results_new(CMD_INVALID, expected_syntax); | 20 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
22 | } | 21 | } |
23 | int delta_x = strtol(argv[1], NULL, 10); | 22 | int delta_x = strtol(argv[1], NULL, 10); |
24 | int delta_y = strtol(argv[2], NULL, 10); | 23 | int delta_y = strtol(argv[2], NULL, 10); |
@@ -27,7 +26,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, | |||
27 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 26 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
28 | } else if (strcasecmp(argv[0], "set") == 0) { | 27 | } else if (strcasecmp(argv[0], "set") == 0) { |
29 | if (argc < 3) { | 28 | if (argc < 3) { |
30 | return cmd_results_new(CMD_INVALID, expected_syntax); | 29 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
31 | } | 30 | } |
32 | // map absolute coords (0..1,0..1) to root container coords | 31 | // map absolute coords (0..1,0..1) to root container coords |
33 | float x = strtof(argv[1], NULL) / root->width; | 32 | float x = strtof(argv[1], NULL) / root->width; |
@@ -37,7 +36,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, | |||
37 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 36 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
38 | } else { | 37 | } else { |
39 | if (argc < 2) { | 38 | if (argc < 2) { |
40 | return cmd_results_new(CMD_INVALID, expected_syntax); | 39 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
41 | } | 40 | } |
42 | struct cmd_results *error = NULL; | 41 | struct cmd_results *error = NULL; |
43 | if ((error = press_or_release(cursor, argv[0], argv[1]))) { | 42 | if ((error = press_or_release(cursor, argv[0], argv[1]))) { |
@@ -85,36 +84,36 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) { | |||
85 | 84 | ||
86 | static struct cmd_results *press_or_release(struct sway_cursor *cursor, | 85 | static struct cmd_results *press_or_release(struct sway_cursor *cursor, |
87 | char *action, char *button_str) { | 86 | char *action, char *button_str) { |
88 | enum wlr_button_state state; | 87 | enum wl_pointer_button_state state; |
89 | uint32_t button; | 88 | uint32_t button; |
90 | if (strcasecmp(action, "press") == 0) { | 89 | if (strcasecmp(action, "press") == 0) { |
91 | state = WLR_BUTTON_PRESSED; | 90 | state = WL_POINTER_BUTTON_STATE_PRESSED; |
92 | } else if (strcasecmp(action, "release") == 0) { | 91 | } else if (strcasecmp(action, "release") == 0) { |
93 | state = WLR_BUTTON_RELEASED; | 92 | state = WL_POINTER_BUTTON_STATE_RELEASED; |
94 | } else { | 93 | } else { |
95 | return cmd_results_new(CMD_INVALID, expected_syntax); | 94 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
96 | } | 95 | } |
97 | 96 | ||
98 | char *message = NULL; | 97 | char *message = NULL; |
99 | button = get_mouse_button(button_str, &message); | 98 | button = get_mouse_button(button_str, &message); |
100 | if (message) { | 99 | if (message) { |
101 | struct cmd_results *error = | 100 | struct cmd_results *error = |
102 | cmd_results_new(CMD_INVALID, message); | 101 | cmd_results_new(CMD_INVALID, "%s", message); |
103 | free(message); | 102 | free(message); |
104 | return error; | 103 | return error; |
105 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN | 104 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN |
106 | || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { | 105 | || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { |
107 | // Dispatch axis event | 106 | // Dispatch axis event |
108 | enum wlr_axis_orientation orientation = | 107 | enum wl_pointer_axis orientation = |
109 | (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) | 108 | (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) |
110 | ? WLR_AXIS_ORIENTATION_VERTICAL | 109 | ? WL_POINTER_AXIS_VERTICAL_SCROLL |
111 | : WLR_AXIS_ORIENTATION_HORIZONTAL; | 110 | : WL_POINTER_AXIS_HORIZONTAL_SCROLL; |
112 | double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) | 111 | double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) |
113 | ? -1 : 1; | 112 | ? -1 : 1; |
114 | struct wlr_event_pointer_axis event = { | 113 | struct wlr_pointer_axis_event event = { |
115 | .device = NULL, | 114 | .pointer = NULL, |
116 | .time_msec = 0, | 115 | .time_msec = 0, |
117 | .source = WLR_AXIS_SOURCE_WHEEL, | 116 | .source = WL_POINTER_AXIS_SOURCE_WHEEL, |
118 | .orientation = orientation, | 117 | .orientation = orientation, |
119 | .delta = delta * 15, | 118 | .delta = delta * 15, |
120 | .delta_discrete = delta | 119 | .delta_discrete = delta |
diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c index e09b82d9..f5177a47 100644 --- a/sway/commands/seat/hide_cursor.c +++ b/sway/commands/seat/hide_cursor.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c index 82428f2c..2974453e 100644 --- a/sway/commands/seat/idle.c +++ b/sway/commands/seat/idle.c | |||
@@ -1,8 +1,8 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
5 | #include <stdint.h> | 4 | #include <stdint.h> |
5 | #include "log.h" | ||
6 | #include "sway/commands.h" | 6 | #include "sway/commands.h" |
7 | #include "sway/config.h" | 7 | #include "sway/config.h" |
8 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
@@ -69,5 +69,10 @@ struct cmd_results *seat_cmd_idle_wake(int argc, char **argv) { | |||
69 | return cmd_results_new(CMD_FAILURE, "Invalid idle source"); | 69 | return cmd_results_new(CMD_FAILURE, "Invalid idle source"); |
70 | } | 70 | } |
71 | config->handler_context.seat_config->idle_wake_sources = sources; | 71 | config->handler_context.seat_config->idle_wake_sources = sources; |
72 | sway_log(SWAY_INFO, "Warning: seat idle_wake is deprecated"); | ||
73 | if (config->reading) { | ||
74 | config_add_swaynag_warning("seat idle_wake is deprecated. " | ||
75 | "Only seat idle_inhibit is supported."); | ||
76 | } | ||
72 | return cmd_results_new(CMD_SUCCESS, NULL); | 77 | return cmd_results_new(CMD_SUCCESS, NULL); |
73 | } | 78 | } |
diff --git a/sway/commands/seat/xcursor_theme.c b/sway/commands/seat/xcursor_theme.c index 202f35b9..61322a57 100644 --- a/sway/commands/seat/xcursor_theme.c +++ b/sway/commands/seat/xcursor_theme.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/set.c b/sway/commands/set.c index c539e9fc..ba384c7c 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index 0d373b80..60cef9fa 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -10,8 +9,8 @@ | |||
10 | #include "stringop.h" | 9 | #include "stringop.h" |
11 | #include "util.h" | 10 | #include "util.h" |
12 | 11 | ||
13 | static void rebuild_marks_iterator(struct sway_container *con, void *data) { | 12 | static void title_bar_update_iterator(struct sway_container *con, void *data) { |
14 | container_update_marks_textures(con); | 13 | container_update_marks(con); |
15 | } | 14 | } |
16 | 15 | ||
17 | struct cmd_results *cmd_show_marks(int argc, char **argv) { | 16 | struct cmd_results *cmd_show_marks(int argc, char **argv) { |
@@ -23,12 +22,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { | |||
23 | config->show_marks = parse_boolean(argv[0], config->show_marks); | 22 | config->show_marks = parse_boolean(argv[0], config->show_marks); |
24 | 23 | ||
25 | if (config->show_marks) { | 24 | if (config->show_marks) { |
26 | root_for_each_container(rebuild_marks_iterator, NULL); | 25 | root_for_each_container(title_bar_update_iterator, NULL); |
27 | } | ||
28 | |||
29 | for (int i = 0; i < root->outputs->length; ++i) { | ||
30 | struct sway_output *output = root->outputs->items[i]; | ||
31 | output_damage_whole(output); | ||
32 | } | 26 | } |
33 | 27 | ||
34 | return cmd_results_new(CMD_SUCCESS, NULL); | 28 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c index b27f9ccd..a6d165dc 100644 --- a/sway/commands/smart_gaps.c +++ b/sway/commands/smart_gaps.c | |||
@@ -15,7 +15,12 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) { | |||
15 | return error; | 15 | return error; |
16 | } | 16 | } |
17 | 17 | ||
18 | config->smart_gaps = parse_boolean(argv[0], config->smart_gaps); | 18 | if (strcmp(argv[0], "inverse_outer") == 0) { |
19 | config->smart_gaps = SMART_GAPS_INVERSE_OUTER; | ||
20 | } else { | ||
21 | config->smart_gaps = parse_boolean(argv[0], config->smart_gaps) | ||
22 | ? SMART_GAPS_ON : SMART_GAPS_OFF; | ||
23 | } | ||
19 | 24 | ||
20 | arrange_root(); | 25 | arrange_root(); |
21 | 26 | ||
diff --git a/sway/commands/split.c b/sway/commands/split.c index 3e25c6f7..500a497d 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c | |||
@@ -32,6 +32,24 @@ static struct cmd_results *do_split(int layout) { | |||
32 | return cmd_results_new(CMD_SUCCESS, NULL); | 32 | return cmd_results_new(CMD_SUCCESS, NULL); |
33 | } | 33 | } |
34 | 34 | ||
35 | static struct cmd_results *do_unsplit(void) { | ||
36 | struct sway_container *con = config->handler_context.container; | ||
37 | struct sway_workspace *ws = config->handler_context.workspace; | ||
38 | |||
39 | if (con && con->pending.parent && con->pending.parent->pending.children->length == 1) { | ||
40 | container_flatten(con->pending.parent); | ||
41 | } else { | ||
42 | return cmd_results_new(CMD_FAILURE, "Can only flatten a child container with no siblings"); | ||
43 | } | ||
44 | |||
45 | if (root->fullscreen_global) { | ||
46 | arrange_root(); | ||
47 | } else { | ||
48 | arrange_workspace(ws); | ||
49 | } | ||
50 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
51 | } | ||
52 | |||
35 | struct cmd_results *cmd_split(int argc, char **argv) { | 53 | struct cmd_results *cmd_split(int argc, char **argv) { |
36 | struct cmd_results *error = NULL; | 54 | struct cmd_results *error = NULL; |
37 | if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { | 55 | if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { |
@@ -55,6 +73,9 @@ struct cmd_results *cmd_split(int argc, char **argv) { | |||
55 | } else { | 73 | } else { |
56 | return do_split(L_VERT); | 74 | return do_split(L_VERT); |
57 | } | 75 | } |
76 | } else if (strcasecmp(argv[0], "n") == 0 || | ||
77 | strcasecmp(argv[0], "none") == 0) { | ||
78 | return do_unsplit(); | ||
58 | } else { | 79 | } else { |
59 | return cmd_results_new(CMD_FAILURE, | 80 | return cmd_results_new(CMD_FAILURE, |
60 | "Invalid split command (expected either horizontal or vertical)."); | 81 | "Invalid split command (expected either horizontal or vertical)."); |
diff --git a/sway/commands/swap.c b/sway/commands/swap.c index ce5e5128..e142eede 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c | |||
@@ -1,10 +1,10 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <strings.h> | 1 | #include <strings.h> |
3 | #include "config.h" | 2 | #include "config.h" |
4 | #include "log.h" | 3 | #include "log.h" |
5 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
6 | #include "sway/output.h" | 5 | #include "sway/output.h" |
7 | #include "sway/tree/arrange.h" | 6 | #include "sway/tree/arrange.h" |
7 | #include "sway/tree/container.h" | ||
8 | #include "sway/tree/root.h" | 8 | #include "sway/tree/root.h" |
9 | #include "sway/tree/view.h" | 9 | #include "sway/tree/view.h" |
10 | #include "sway/tree/workspace.h" | 10 | #include "sway/tree/workspace.h" |
@@ -13,180 +13,6 @@ | |||
13 | static const char expected_syntax[] = | 13 | static const char expected_syntax[] = |
14 | "Expected 'swap container with id|con_id|mark <arg>'"; | 14 | "Expected 'swap container with id|con_id|mark <arg>'"; |
15 | 15 | ||
16 | static void swap_places(struct sway_container *con1, | ||
17 | struct sway_container *con2) { | ||
18 | struct sway_container *temp = malloc(sizeof(struct sway_container)); | ||
19 | temp->pending.x = con1->pending.x; | ||
20 | temp->pending.y = con1->pending.y; | ||
21 | temp->pending.width = con1->pending.width; | ||
22 | temp->pending.height = con1->pending.height; | ||
23 | temp->width_fraction = con1->width_fraction; | ||
24 | temp->height_fraction = con1->height_fraction; | ||
25 | temp->pending.parent = con1->pending.parent; | ||
26 | temp->pending.workspace = con1->pending.workspace; | ||
27 | bool temp_floating = container_is_floating(con1); | ||
28 | |||
29 | con1->pending.x = con2->pending.x; | ||
30 | con1->pending.y = con2->pending.y; | ||
31 | con1->pending.width = con2->pending.width; | ||
32 | con1->pending.height = con2->pending.height; | ||
33 | con1->width_fraction = con2->width_fraction; | ||
34 | con1->height_fraction = con2->height_fraction; | ||
35 | |||
36 | con2->pending.x = temp->pending.x; | ||
37 | con2->pending.y = temp->pending.y; | ||
38 | con2->pending.width = temp->pending.width; | ||
39 | con2->pending.height = temp->pending.height; | ||
40 | con2->width_fraction = temp->width_fraction; | ||
41 | con2->height_fraction = temp->height_fraction; | ||
42 | |||
43 | int temp_index = container_sibling_index(con1); | ||
44 | if (con2->pending.parent) { | ||
45 | container_insert_child(con2->pending.parent, con1, | ||
46 | container_sibling_index(con2)); | ||
47 | } else if (container_is_floating(con2)) { | ||
48 | workspace_add_floating(con2->pending.workspace, con1); | ||
49 | } else { | ||
50 | workspace_insert_tiling(con2->pending.workspace, con1, | ||
51 | container_sibling_index(con2)); | ||
52 | } | ||
53 | if (temp->pending.parent) { | ||
54 | container_insert_child(temp->pending.parent, con2, temp_index); | ||
55 | } else if (temp_floating) { | ||
56 | workspace_add_floating(temp->pending.workspace, con2); | ||
57 | } else { | ||
58 | workspace_insert_tiling(temp->pending.workspace, con2, temp_index); | ||
59 | } | ||
60 | |||
61 | free(temp); | ||
62 | } | ||
63 | |||
64 | static void swap_focus(struct sway_container *con1, | ||
65 | struct sway_container *con2, struct sway_seat *seat, | ||
66 | struct sway_container *focus) { | ||
67 | if (focus == con1 || focus == con2) { | ||
68 | struct sway_workspace *ws1 = con1->pending.workspace; | ||
69 | struct sway_workspace *ws2 = con2->pending.workspace; | ||
70 | enum sway_container_layout layout1 = container_parent_layout(con1); | ||
71 | enum sway_container_layout layout2 = container_parent_layout(con2); | ||
72 | if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { | ||
73 | if (workspace_is_visible(ws2)) { | ||
74 | seat_set_focus(seat, &con2->node); | ||
75 | } | ||
76 | seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); | ||
77 | } else if (focus == con2 && (layout1 == L_TABBED | ||
78 | || layout1 == L_STACKED)) { | ||
79 | if (workspace_is_visible(ws1)) { | ||
80 | seat_set_focus(seat, &con1->node); | ||
81 | } | ||
82 | seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); | ||
83 | } else if (ws1 != ws2) { | ||
84 | seat_set_focus_container(seat, focus == con1 ? con2 : con1); | ||
85 | } else { | ||
86 | seat_set_focus_container(seat, focus); | ||
87 | } | ||
88 | } else { | ||
89 | seat_set_focus_container(seat, focus); | ||
90 | } | ||
91 | |||
92 | if (root->fullscreen_global) { | ||
93 | seat_set_focus(seat, | ||
94 | seat_get_focus_inactive(seat, &root->fullscreen_global->node)); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | void container_swap(struct sway_container *con1, struct sway_container *con2) { | ||
99 | if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { | ||
100 | return; | ||
101 | } | ||
102 | if (!sway_assert(!container_has_ancestor(con1, con2) | ||
103 | && !container_has_ancestor(con2, con1), | ||
104 | "Cannot swap ancestor and descendant")) { | ||
105 | return; | ||
106 | } | ||
107 | |||
108 | sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", | ||
109 | con1->node.id, con2->node.id); | ||
110 | |||
111 | bool scratch1 = con1->scratchpad; | ||
112 | bool hidden1 = container_is_scratchpad_hidden(con1); | ||
113 | bool scratch2 = con2->scratchpad; | ||
114 | bool hidden2 = container_is_scratchpad_hidden(con2); | ||
115 | if (scratch1) { | ||
116 | if (hidden1) { | ||
117 | root_scratchpad_show(con1); | ||
118 | } | ||
119 | root_scratchpad_remove_container(con1); | ||
120 | } | ||
121 | if (scratch2) { | ||
122 | if (hidden2) { | ||
123 | root_scratchpad_show(con2); | ||
124 | } | ||
125 | root_scratchpad_remove_container(con2); | ||
126 | } | ||
127 | |||
128 | enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode; | ||
129 | enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; | ||
130 | if (fs1) { | ||
131 | container_fullscreen_disable(con1); | ||
132 | } | ||
133 | if (fs2) { | ||
134 | container_fullscreen_disable(con2); | ||
135 | } | ||
136 | |||
137 | struct sway_seat *seat = config->handler_context.seat; | ||
138 | struct sway_container *focus = seat_get_focused_container(seat); | ||
139 | struct sway_workspace *vis1 = | ||
140 | output_get_active_workspace(con1->pending.workspace->output); | ||
141 | struct sway_workspace *vis2 = | ||
142 | output_get_active_workspace(con2->pending.workspace->output); | ||
143 | if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" | ||
144 | "workspace. This should not happen")) { | ||
145 | return; | ||
146 | } | ||
147 | |||
148 | char *stored_prev_name = NULL; | ||
149 | if (seat->prev_workspace_name) { | ||
150 | stored_prev_name = strdup(seat->prev_workspace_name); | ||
151 | } | ||
152 | |||
153 | swap_places(con1, con2); | ||
154 | |||
155 | if (!workspace_is_visible(vis1)) { | ||
156 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); | ||
157 | } | ||
158 | if (!workspace_is_visible(vis2)) { | ||
159 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); | ||
160 | } | ||
161 | |||
162 | swap_focus(con1, con2, seat, focus); | ||
163 | |||
164 | if (stored_prev_name) { | ||
165 | free(seat->prev_workspace_name); | ||
166 | seat->prev_workspace_name = stored_prev_name; | ||
167 | } | ||
168 | |||
169 | if (scratch1) { | ||
170 | root_scratchpad_add_container(con2, NULL); | ||
171 | if (!hidden1) { | ||
172 | root_scratchpad_show(con2); | ||
173 | } | ||
174 | } | ||
175 | if (scratch2) { | ||
176 | root_scratchpad_add_container(con1, NULL); | ||
177 | if (!hidden2) { | ||
178 | root_scratchpad_show(con1); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | if (fs1) { | ||
183 | container_set_fullscreen(con2, fs1); | ||
184 | } | ||
185 | if (fs2) { | ||
186 | container_set_fullscreen(con1, fs2); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | static bool test_con_id(struct sway_container *container, void *data) { | 16 | static bool test_con_id(struct sway_container *container, void *data) { |
191 | size_t *con_id = data; | 17 | size_t *con_id = data; |
192 | return container->node.id == *con_id; | 18 | return container->node.id == *con_id; |
@@ -219,7 +45,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
219 | } | 45 | } |
220 | 46 | ||
221 | if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { | 47 | if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { |
222 | return cmd_results_new(CMD_INVALID, expected_syntax); | 48 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
223 | } | 49 | } |
224 | 50 | ||
225 | struct sway_container *current = config->handler_context.container; | 51 | struct sway_container *current = config->handler_context.container; |
@@ -238,7 +64,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
238 | other = root_find_container(test_mark, value); | 64 | other = root_find_container(test_mark, value); |
239 | } else { | 65 | } else { |
240 | free(value); | 66 | free(value); |
241 | return cmd_results_new(CMD_INVALID, expected_syntax); | 67 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
242 | } | 68 | } |
243 | 69 | ||
244 | if (!other) { | 70 | if (!other) { |
@@ -247,6 +73,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
247 | } else if (!current) { | 73 | } else if (!current) { |
248 | error = cmd_results_new(CMD_FAILURE, | 74 | error = cmd_results_new(CMD_FAILURE, |
249 | "Can only swap with containers and views"); | 75 | "Can only swap with containers and views"); |
76 | } else if (current == other) { | ||
77 | error = cmd_results_new(CMD_FAILURE, | ||
78 | "Cannot swap a container with itself"); | ||
250 | } else if (container_has_ancestor(current, other) | 79 | } else if (container_has_ancestor(current, other) |
251 | || container_has_ancestor(other, current)) { | 80 | || container_has_ancestor(other, current)) { |
252 | error = cmd_results_new(CMD_FAILURE, | 81 | error = cmd_results_new(CMD_FAILURE, |
diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c index c30355de..be298a29 100644 --- a/sway/commands/title_align.c +++ b/sway/commands/title_align.c | |||
@@ -4,6 +4,10 @@ | |||
4 | #include "sway/tree/container.h" | 4 | #include "sway/tree/container.h" |
5 | #include "sway/tree/root.h" | 5 | #include "sway/tree/root.h" |
6 | 6 | ||
7 | static void arrange_title_bar_iterator(struct sway_container *con, void *data) { | ||
8 | container_arrange_title_bar(con); | ||
9 | } | ||
10 | |||
7 | struct cmd_results *cmd_title_align(int argc, char **argv) { | 11 | struct cmd_results *cmd_title_align(int argc, char **argv) { |
8 | struct cmd_results *error = NULL; | 12 | struct cmd_results *error = NULL; |
9 | if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { | 13 | if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { |
@@ -21,10 +25,7 @@ struct cmd_results *cmd_title_align(int argc, char **argv) { | |||
21 | "Expected 'title_align left|center|right'"); | 25 | "Expected 'title_align left|center|right'"); |
22 | } | 26 | } |
23 | 27 | ||
24 | for (int i = 0; i < root->outputs->length; ++i) { | 28 | root_for_each_container(arrange_title_bar_iterator, NULL); |
25 | struct sway_output *output = root->outputs->items[i]; | ||
26 | output_damage_whole(output); | ||
27 | } | ||
28 | 29 | ||
29 | return cmd_results_new(CMD_SUCCESS, NULL); | 30 | return cmd_results_new(CMD_SUCCESS, NULL); |
30 | } | 31 | } |
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c index 9d312470..0b2ea265 100644 --- a/sway/commands/title_format.c +++ b/sway/commands/title_format.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -23,6 +22,5 @@ struct cmd_results *cmd_title_format(int argc, char **argv) { | |||
23 | } | 22 | } |
24 | view->title_format = format; | 23 | view->title_format = format; |
25 | view_update_title(view, true); | 24 | view_update_title(view, true); |
26 | config_update_font_height(true); | ||
27 | return cmd_results_new(CMD_SUCCESS, NULL); | 25 | return cmd_results_new(CMD_SUCCESS, NULL); |
28 | } | 26 | } |
diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c index 7c27c163..fa3db3c5 100644 --- a/sway/commands/titlebar_border_thickness.c +++ b/sway/commands/titlebar_border_thickness.c | |||
@@ -27,7 +27,6 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) { | |||
27 | "Expected output to have a workspace"); | 27 | "Expected output to have a workspace"); |
28 | } | 28 | } |
29 | arrange_workspace(ws); | 29 | arrange_workspace(ws); |
30 | output_damage_whole(output); | ||
31 | } | 30 | } |
32 | 31 | ||
33 | return cmd_results_new(CMD_SUCCESS, NULL); | 32 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c index 29ce59ff..6999f7a2 100644 --- a/sway/commands/titlebar_padding.c +++ b/sway/commands/titlebar_padding.c | |||
@@ -33,7 +33,6 @@ struct cmd_results *cmd_titlebar_padding(int argc, char **argv) { | |||
33 | for (int i = 0; i < root->outputs->length; ++i) { | 33 | for (int i = 0; i < root->outputs->length; ++i) { |
34 | struct sway_output *output = root->outputs->items[i]; | 34 | struct sway_output *output = root->outputs->items[i]; |
35 | arrange_workspace(output_get_active_workspace(output)); | 35 | arrange_workspace(output_get_active_workspace(output)); |
36 | output_damage_whole(output); | ||
37 | } | 36 | } |
38 | 37 | ||
39 | return cmd_results_new(CMD_SUCCESS, NULL); | 38 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c index 19274dfb..4aba5bae 100644 --- a/sway/commands/unmark.c +++ b/sway/commands/unmark.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -8,9 +7,13 @@ | |||
8 | #include "log.h" | 7 | #include "log.h" |
9 | #include "stringop.h" | 8 | #include "stringop.h" |
10 | 9 | ||
11 | static void remove_all_marks_iterator(struct sway_container *con, void *data) { | 10 | static void remove_mark(struct sway_container *con) { |
12 | container_clear_marks(con); | 11 | container_clear_marks(con); |
13 | container_update_marks_textures(con); | 12 | container_update_marks(con); |
13 | } | ||
14 | |||
15 | static void remove_all_marks_iterator(struct sway_container *con, void *data) { | ||
16 | remove_mark(con); | ||
14 | } | 17 | } |
15 | 18 | ||
16 | // unmark Remove all marks from all views | 19 | // unmark Remove all marks from all views |
@@ -38,8 +41,7 @@ struct cmd_results *cmd_unmark(int argc, char **argv) { | |||
38 | } | 41 | } |
39 | } else if (con && !mark) { | 42 | } else if (con && !mark) { |
40 | // Clear all marks from the given container | 43 | // Clear all marks from the given container |
41 | container_clear_marks(con); | 44 | remove_mark(con); |
42 | container_update_marks_textures(con); | ||
43 | } else if (!con && mark) { | 45 | } else if (!con && mark) { |
44 | // Remove mark from whichever container has it | 46 | // Remove mark from whichever container has it |
45 | container_find_and_unmark(mark); | 47 | container_find_and_unmark(mark); |
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index a6a0beda..37a201b4 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <limits.h> | 2 | #include <limits.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -61,7 +60,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, | |||
61 | const char expected[] = "Expected 'workspace <name> gaps " | 60 | const char expected[] = "Expected 'workspace <name> gaps " |
62 | "inner|outer|horizontal|vertical|top|right|bottom|left <px>'"; | 61 | "inner|outer|horizontal|vertical|top|right|bottom|left <px>'"; |
63 | if (gaps_location == 0) { | 62 | if (gaps_location == 0) { |
64 | return cmd_results_new(CMD_INVALID, expected); | 63 | return cmd_results_new(CMD_INVALID, "%s", expected); |
65 | } | 64 | } |
66 | struct cmd_results *error = NULL; | 65 | struct cmd_results *error = NULL; |
67 | if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, | 66 | if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, |
@@ -79,7 +78,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, | |||
79 | char *end; | 78 | char *end; |
80 | int amount = strtol(argv[gaps_location + 2], &end, 10); | 79 | int amount = strtol(argv[gaps_location + 2], &end, 10); |
81 | if (strlen(end)) { | 80 | if (strlen(end)) { |
82 | return cmd_results_new(CMD_FAILURE, expected); | 81 | return cmd_results_new(CMD_FAILURE, "%s", expected); |
83 | } | 82 | } |
84 | 83 | ||
85 | bool valid = false; | 84 | bool valid = false; |
@@ -110,7 +109,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, | |||
110 | } | 109 | } |
111 | } | 110 | } |
112 | if (!valid) { | 111 | if (!valid) { |
113 | return cmd_results_new(CMD_INVALID, expected); | 112 | return cmd_results_new(CMD_INVALID, "%s", expected); |
114 | } | 113 | } |
115 | 114 | ||
116 | // Prevent invalid gaps configurations. | 115 | // Prevent invalid gaps configurations. |
@@ -158,6 +157,10 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
158 | return cmd_results_new(CMD_FAILURE, | 157 | return cmd_results_new(CMD_FAILURE, |
159 | "Unable to allocate workspace output"); | 158 | "Unable to allocate workspace output"); |
160 | } | 159 | } |
160 | if (output_location + 1 < argc) { | ||
161 | list_free_items_and_destroy(wsc->outputs); | ||
162 | wsc->outputs = create_list(); | ||
163 | } | ||
161 | for (int i = output_location + 1; i < argc; ++i) { | 164 | for (int i = output_location + 1; i < argc; ++i) { |
162 | list_add(wsc->outputs, strdup(argv[i])); | 165 | list_add(wsc->outputs, strdup(argv[i])); |
163 | } | 166 | } |
@@ -174,7 +177,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
174 | } | 177 | } |
175 | 178 | ||
176 | if (root->fullscreen_global) { | 179 | if (root->fullscreen_global) { |
177 | return cmd_results_new(CMD_FAILURE, "workspace", | 180 | return cmd_results_new(CMD_FAILURE, |
178 | "Can't switch workspaces while fullscreen global"); | 181 | "Can't switch workspaces while fullscreen global"); |
179 | } | 182 | } |
180 | 183 | ||
diff --git a/sway/commands/xwayland.c b/sway/commands/xwayland.c index 6ca26923..584a8e3a 100644 --- a/sway/commands/xwayland.c +++ b/sway/commands/xwayland.c | |||
@@ -20,6 +20,8 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) { | |||
20 | xwayland = XWAYLAND_MODE_DISABLED; | 20 | xwayland = XWAYLAND_MODE_DISABLED; |
21 | } | 21 | } |
22 | 22 | ||
23 | // config->xwayland is reset to the previous value on reload in | ||
24 | // load_main_config() | ||
23 | if (config->reloading && config->xwayland != xwayland) { | 25 | if (config->reloading && config->xwayland != xwayland) { |
24 | return cmd_results_new(CMD_FAILURE, | 26 | return cmd_results_new(CMD_FAILURE, |
25 | "xwayland can only be enabled/disabled at launch"); | 27 | "xwayland can only be enabled/disabled at launch"); |
diff --git a/sway/config.c b/sway/config.c index fde386c7..f9131e0f 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #undef _POSIX_C_SOURCE | ||
1 | #define _XOPEN_SOURCE 700 // for realpath | 2 | #define _XOPEN_SOURCE 700 // for realpath |
2 | #include <stdio.h> | 3 | #include <stdio.h> |
3 | #include <stdbool.h> | 4 | #include <stdbool.h> |
@@ -36,19 +37,26 @@ | |||
36 | struct sway_config *config = NULL; | 37 | struct sway_config *config = NULL; |
37 | 38 | ||
38 | static struct xkb_state *keysym_translation_state_create( | 39 | static struct xkb_state *keysym_translation_state_create( |
39 | struct xkb_rule_names rules) { | 40 | struct xkb_rule_names rules, uint32_t context_flags) { |
40 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | 41 | struct xkb_context *context = xkb_context_new(context_flags | XKB_CONTEXT_NO_SECURE_GETENV); |
41 | struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( | 42 | struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( |
42 | context, | 43 | context, |
43 | &rules, | 44 | &rules, |
44 | XKB_KEYMAP_COMPILE_NO_FLAGS); | 45 | XKB_KEYMAP_COMPILE_NO_FLAGS); |
45 | |||
46 | xkb_context_unref(context); | 46 | xkb_context_unref(context); |
47 | if (xkb_keymap == NULL) { | ||
48 | sway_log(SWAY_ERROR, "Failed to compile keysym translation XKB keymap"); | ||
49 | return NULL; | ||
50 | } | ||
51 | |||
47 | return xkb_state_new(xkb_keymap); | 52 | return xkb_state_new(xkb_keymap); |
48 | } | 53 | } |
49 | 54 | ||
50 | static void keysym_translation_state_destroy( | 55 | static void keysym_translation_state_destroy( |
51 | struct xkb_state *state) { | 56 | struct xkb_state *state) { |
57 | if (state == NULL) { | ||
58 | return; | ||
59 | } | ||
52 | xkb_keymap_unref(xkb_state_get_keymap(state)); | 60 | xkb_keymap_unref(xkb_state_get_keymap(state)); |
53 | xkb_state_unref(state); | 61 | xkb_state_unref(state); |
54 | } | 62 | } |
@@ -82,6 +90,12 @@ static void free_mode(struct sway_mode *mode) { | |||
82 | } | 90 | } |
83 | list_free(mode->switch_bindings); | 91 | list_free(mode->switch_bindings); |
84 | } | 92 | } |
93 | if (mode->gesture_bindings) { | ||
94 | for (int i = 0; i < mode->gesture_bindings->length; i++) { | ||
95 | free_gesture_binding(mode->gesture_bindings->items[i]); | ||
96 | } | ||
97 | list_free(mode->gesture_bindings); | ||
98 | } | ||
85 | free(mode); | 99 | free(mode); |
86 | } | 100 | } |
87 | 101 | ||
@@ -222,6 +236,7 @@ static void config_defaults(struct sway_config *config) { | |||
222 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; | 236 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; |
223 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; | 237 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; |
224 | if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; | 238 | if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; |
239 | if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup; | ||
225 | list_add(config->modes, config->current_mode); | 240 | list_add(config->modes, config->current_mode); |
226 | 241 | ||
227 | config->floating_mod = 0; | 242 | config->floating_mod = 0; |
@@ -236,7 +251,7 @@ static void config_defaults(struct sway_config *config) { | |||
236 | config->default_layout = L_NONE; | 251 | config->default_layout = L_NONE; |
237 | config->default_orientation = L_NONE; | 252 | config->default_orientation = L_NONE; |
238 | if (!(config->font = strdup("monospace 10"))) goto cleanup; | 253 | if (!(config->font = strdup("monospace 10"))) goto cleanup; |
239 | config->font_height = 17; // height of monospace 10 | 254 | config->font_description = pango_font_description_from_string(config->font); |
240 | config->urgent_timeout = 500; | 255 | config->urgent_timeout = 500; |
241 | config->focus_on_window_activation = FOWA_URGENT; | 256 | config->focus_on_window_activation = FOWA_URGENT; |
242 | config->popup_during_fullscreen = POPUP_SMART; | 257 | config->popup_during_fullscreen = POPUP_SMART; |
@@ -266,8 +281,9 @@ static void config_defaults(struct sway_config *config) { | |||
266 | config->title_align = ALIGN_LEFT; | 281 | config->title_align = ALIGN_LEFT; |
267 | config->tiling_drag = true; | 282 | config->tiling_drag = true; |
268 | config->tiling_drag_threshold = 9; | 283 | config->tiling_drag_threshold = 9; |
284 | config->primary_selection = true; | ||
269 | 285 | ||
270 | config->smart_gaps = false; | 286 | config->smart_gaps = SMART_GAPS_OFF; |
271 | config->gaps_inner = 0; | 287 | config->gaps_inner = 0; |
272 | config->gaps_outer.top = 0; | 288 | config->gaps_outer.top = 0; |
273 | config->gaps_outer.right = 0; | 289 | config->gaps_outer.right = 0; |
@@ -291,6 +307,8 @@ static void config_defaults(struct sway_config *config) { | |||
291 | config->hide_edge_borders_smart = ESMART_OFF; | 307 | config->hide_edge_borders_smart = ESMART_OFF; |
292 | config->hide_lone_tab = false; | 308 | config->hide_lone_tab = false; |
293 | 309 | ||
310 | config->has_focused_tab_title = false; | ||
311 | |||
294 | // border colors | 312 | // border colors |
295 | color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); | 313 | color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); |
296 | color_to_rgba(config->border_colors.focused.background, 0x285577FF); | 314 | color_to_rgba(config->border_colors.focused.background, 0x285577FF); |
@@ -326,8 +344,14 @@ static void config_defaults(struct sway_config *config) { | |||
326 | 344 | ||
327 | // The keysym to keycode translation | 345 | // The keysym to keycode translation |
328 | struct xkb_rule_names rules = {0}; | 346 | struct xkb_rule_names rules = {0}; |
329 | config->keysym_translation_state = | 347 | config->keysym_translation_state = keysym_translation_state_create(rules, 0); |
330 | keysym_translation_state_create(rules); | 348 | if (config->keysym_translation_state == NULL) { |
349 | config->keysym_translation_state = keysym_translation_state_create(rules, | ||
350 | XKB_CONTEXT_NO_ENVIRONMENT_NAMES); | ||
351 | } | ||
352 | if (config->keysym_translation_state == NULL) { | ||
353 | goto cleanup; | ||
354 | } | ||
331 | 355 | ||
332 | return; | 356 | return; |
333 | cleanup: | 357 | cleanup: |
@@ -342,13 +366,7 @@ static char *config_path(const char *prefix, const char *config_folder) { | |||
342 | if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { | 366 | if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { |
343 | return NULL; | 367 | return NULL; |
344 | } | 368 | } |
345 | 369 | return format_str("%s/%s/config", prefix, config_folder); | |
346 | const char *filename = "config"; | ||
347 | |||
348 | size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename); | ||
349 | char *path = calloc(size, sizeof(char)); | ||
350 | snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename); | ||
351 | return path; | ||
352 | } | 370 | } |
353 | 371 | ||
354 | static char *get_config_path(void) { | 372 | static char *get_config_path(void) { |
@@ -358,10 +376,7 @@ static char *get_config_path(void) { | |||
358 | 376 | ||
359 | const char *config_home = getenv("XDG_CONFIG_HOME"); | 377 | const char *config_home = getenv("XDG_CONFIG_HOME"); |
360 | if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { | 378 | if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { |
361 | size_t size_fallback = 1 + strlen(home) + strlen("/.config"); | 379 | config_home_fallback = format_str("%s/.config", home); |
362 | config_home_fallback = calloc(size_fallback, sizeof(char)); | ||
363 | if (config_home_fallback != NULL) | ||
364 | snprintf(config_home_fallback, size_fallback, "%s/.config", home); | ||
365 | config_home = config_home_fallback; | 380 | config_home = config_home_fallback; |
366 | } | 381 | } |
367 | 382 | ||
@@ -465,6 +480,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
465 | old_config->xwayland ? "enabled" : "disabled"); | 480 | old_config->xwayland ? "enabled" : "disabled"); |
466 | config->xwayland = old_config->xwayland; | 481 | config->xwayland = old_config->xwayland; |
467 | 482 | ||
483 | // primary_selection can only be enabled/disabled at launch | ||
484 | sway_log(SWAY_DEBUG, "primary_selection will remain %s", | ||
485 | old_config->primary_selection ? "enabled" : "disabled"); | ||
486 | config->primary_selection = old_config->primary_selection; | ||
487 | |||
468 | if (!config->validating) { | 488 | if (!config->validating) { |
469 | if (old_config->swaybg_client != NULL) { | 489 | if (old_config->swaybg_client != NULL) { |
470 | wl_client_destroy(old_config->swaybg_client); | 490 | wl_client_destroy(old_config->swaybg_client); |
@@ -484,56 +504,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
484 | 504 | ||
485 | config->reading = true; | 505 | config->reading = true; |
486 | 506 | ||
487 | // Read security configs | 507 | bool success = load_config(path, config, &config->swaynag_config_errors); |
488 | // TODO: Security | ||
489 | bool success = true; | ||
490 | /* | ||
491 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); | ||
492 | if (!dir) { | ||
493 | sway_log(SWAY_ERROR, | ||
494 | "%s does not exist, sway will have no security configuration" | ||
495 | " and will probably be broken", SYSCONFDIR "/sway/security.d"); | ||
496 | } else { | ||
497 | list_t *secconfigs = create_list(); | ||
498 | char *base = SYSCONFDIR "/sway/security.d/"; | ||
499 | struct dirent *ent = readdir(dir); | ||
500 | struct stat s; | ||
501 | while (ent != NULL) { | ||
502 | char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1); | ||
503 | strcpy(_path, base); | ||
504 | strcat(_path, ent->d_name); | ||
505 | lstat(_path, &s); | ||
506 | if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') { | ||
507 | list_add(secconfigs, _path); | ||
508 | } | ||
509 | else { | ||
510 | free(_path); | ||
511 | } | ||
512 | ent = readdir(dir); | ||
513 | } | ||
514 | closedir(dir); | ||
515 | |||
516 | list_qsort(secconfigs, qstrcmp); | ||
517 | for (int i = 0; i < secconfigs->length; ++i) { | ||
518 | char *_path = secconfigs->items[i]; | ||
519 | if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || | ||
520 | (((s.st_mode & 0777) != 0644) && | ||
521 | (s.st_mode & 0777) != 0444)) { | ||
522 | sway_log(SWAY_ERROR, | ||
523 | "Refusing to load %s - it must be owned by root " | ||
524 | "and mode 644 or 444", _path); | ||
525 | success = false; | ||
526 | } else { | ||
527 | success = success && load_config(_path, config); | ||
528 | } | ||
529 | } | ||
530 | |||
531 | list_free_items_and_destroy(secconfigs); | ||
532 | } | ||
533 | */ | ||
534 | |||
535 | success = success && load_config(path, config, | ||
536 | &config->swaynag_config_errors); | ||
537 | 508 | ||
538 | if (validating) { | 509 | if (validating) { |
539 | free_config(config); | 510 | free_config(config); |
@@ -541,6 +512,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
541 | return success; | 512 | return success; |
542 | } | 513 | } |
543 | 514 | ||
515 | // Only really necessary if not explicitly `font` is set in the config. | ||
516 | config_update_font_height(); | ||
517 | |||
544 | if (is_active && !validating) { | 518 | if (is_active && !validating) { |
545 | input_manager_verify_fallback_seat(); | 519 | input_manager_verify_fallback_seat(); |
546 | 520 | ||
@@ -558,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
558 | } | 532 | } |
559 | sway_switch_retrigger_bindings_for_all(); | 533 | sway_switch_retrigger_bindings_for_all(); |
560 | 534 | ||
561 | reset_outputs(); | 535 | apply_all_output_configs(); |
562 | spawn_swaybg(); | 536 | spawn_swaybg(); |
563 | 537 | ||
564 | config->reloading = false; | 538 | config->reloading = false; |
@@ -911,23 +885,18 @@ void config_add_swaynag_warning(char *fmt, ...) { | |||
911 | if (config->reading && !config->validating) { | 885 | if (config->reading && !config->validating) { |
912 | va_list args; | 886 | va_list args; |
913 | va_start(args, fmt); | 887 | va_start(args, fmt); |
914 | size_t length = vsnprintf(NULL, 0, fmt, args) + 1; | 888 | char *str = vformat_str(fmt, args); |
915 | va_end(args); | 889 | va_end(args); |
916 | 890 | if (str == NULL) { | |
917 | char *temp = malloc(length + 1); | ||
918 | if (!temp) { | ||
919 | sway_log(SWAY_ERROR, "Failed to allocate buffer for warning."); | ||
920 | return; | 891 | return; |
921 | } | 892 | } |
922 | 893 | ||
923 | va_start(args, fmt); | ||
924 | vsnprintf(temp, length, fmt, args); | ||
925 | va_end(args); | ||
926 | |||
927 | swaynag_log(config->swaynag_command, &config->swaynag_config_errors, | 894 | swaynag_log(config->swaynag_command, &config->swaynag_config_errors, |
928 | "Warning on line %i (%s) '%s': %s", | 895 | "Warning on line %i (%s) '%s': %s", |
929 | config->current_config_line_number, config->current_config_path, | 896 | config->current_config_line_number, config->current_config_path, |
930 | config->current_config_line, temp); | 897 | config->current_config_line, str); |
898 | |||
899 | free(str); | ||
931 | } | 900 | } |
932 | } | 901 | } |
933 | 902 | ||
@@ -967,7 +936,7 @@ char *do_var_replacement(char *str) { | |||
967 | int offset = find - str; | 936 | int offset = find - str; |
968 | strncpy(newptr, str, offset); | 937 | strncpy(newptr, str, offset); |
969 | newptr += offset; | 938 | newptr += offset; |
970 | strncpy(newptr, var->value, vvlen); | 939 | memcpy(newptr, var->value, vvlen); |
971 | newptr += vvlen; | 940 | newptr += vvlen; |
972 | strcpy(newptr, find + vnlen); | 941 | strcpy(newptr, find + vnlen); |
973 | free(str); | 942 | free(str); |
@@ -991,31 +960,11 @@ int workspace_output_cmp_workspace(const void *a, const void *b) { | |||
991 | return lenient_strcmp(wsa->workspace, wsb->workspace); | 960 | return lenient_strcmp(wsa->workspace, wsb->workspace); |
992 | } | 961 | } |
993 | 962 | ||
994 | static void find_font_height_iterator(struct sway_container *con, void *data) { | ||
995 | size_t amount_below_baseline = con->title_height - con->title_baseline; | ||
996 | size_t extended_height = config->font_baseline + amount_below_baseline; | ||
997 | if (extended_height > config->font_height) { | ||
998 | config->font_height = extended_height; | ||
999 | } | ||
1000 | } | ||
1001 | |||
1002 | static void find_baseline_iterator(struct sway_container *con, void *data) { | ||
1003 | bool *recalculate = data; | ||
1004 | if (*recalculate) { | ||
1005 | container_calculate_title_height(con); | ||
1006 | } | ||
1007 | if (con->title_baseline > config->font_baseline) { | ||
1008 | config->font_baseline = con->title_baseline; | ||
1009 | } | ||
1010 | } | ||
1011 | 963 | ||
1012 | void config_update_font_height(bool recalculate) { | 964 | void config_update_font_height(void) { |
1013 | size_t prev_max_height = config->font_height; | 965 | int prev_max_height = config->font_height; |
1014 | config->font_height = 0; | ||
1015 | config->font_baseline = 0; | ||
1016 | 966 | ||
1017 | root_for_each_container(find_baseline_iterator, &recalculate); | 967 | get_text_metrics(config->font_description, &config->font_height, &config->font_baseline); |
1018 | root_for_each_container(find_font_height_iterator, NULL); | ||
1019 | 968 | ||
1020 | if (config->font_height != prev_max_height) { | 969 | if (config->font_height != prev_max_height) { |
1021 | arrange_root(); | 970 | arrange_root(); |
@@ -1049,8 +998,12 @@ void translate_keysyms(struct input_config *input_config) { | |||
1049 | 998 | ||
1050 | struct xkb_rule_names rules = {0}; | 999 | struct xkb_rule_names rules = {0}; |
1051 | input_config_fill_rule_names(input_config, &rules); | 1000 | input_config_fill_rule_names(input_config, &rules); |
1052 | config->keysym_translation_state = | 1001 | config->keysym_translation_state = keysym_translation_state_create(rules, 0); |
1053 | keysym_translation_state_create(rules); | 1002 | if (config->keysym_translation_state == NULL) { |
1003 | sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state " | ||
1004 | "for device '%s'", input_config->identifier); | ||
1005 | return; | ||
1006 | } | ||
1054 | 1007 | ||
1055 | for (int i = 0; i < config->modes->length; ++i) { | 1008 | for (int i = 0; i < config->modes->length; ++i) { |
1056 | struct sway_mode *mode = config->modes->items[i]; | 1009 | struct sway_mode *mode = config->modes->items[i]; |
diff --git a/sway/config/bar.c b/sway/config/bar.c index e09add44..908b2865 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <signal.h> | 1 | #include <signal.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdio.h> | 3 | #include <stdio.h> |
@@ -219,6 +218,8 @@ static void invoke_swaybar(struct bar_config *bar) { | |||
219 | sigprocmask(SIG_SETMASK, &set, NULL); | 218 | sigprocmask(SIG_SETMASK, &set, NULL); |
220 | signal(SIGPIPE, SIG_DFL); | 219 | signal(SIGPIPE, SIG_DFL); |
221 | 220 | ||
221 | restore_nofile_limit(); | ||
222 | |||
222 | pid = fork(); | 223 | pid = fork(); |
223 | if (pid < 0) { | 224 | if (pid < 0) { |
224 | sway_log_errno(SWAY_ERROR, "fork failed"); | 225 | sway_log_errno(SWAY_ERROR, "fork failed"); |
@@ -254,7 +255,6 @@ static void invoke_swaybar(struct bar_config *bar) { | |||
254 | } | 255 | } |
255 | 256 | ||
256 | sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); | 257 | sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); |
257 | return; | ||
258 | } | 258 | } |
259 | 259 | ||
260 | void load_swaybar(struct bar_config *bar) { | 260 | void load_swaybar(struct bar_config *bar) { |
diff --git a/sway/config/input.c b/sway/config/input.c index a998e170..de3b21ed 100644 --- a/sway/config/input.c +++ b/sway/config/input.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <limits.h> | 2 | #include <limits.h> |
4 | #include <float.h> | 3 | #include <float.h> |
@@ -25,14 +24,17 @@ struct input_config *new_input_config(const char* identifier) { | |||
25 | input->drag = INT_MIN; | 24 | input->drag = INT_MIN; |
26 | input->drag_lock = INT_MIN; | 25 | input->drag_lock = INT_MIN; |
27 | input->dwt = INT_MIN; | 26 | input->dwt = INT_MIN; |
27 | input->dwtp = INT_MIN; | ||
28 | input->send_events = INT_MIN; | 28 | input->send_events = INT_MIN; |
29 | input->click_method = INT_MIN; | 29 | input->click_method = INT_MIN; |
30 | input->middle_emulation = INT_MIN; | 30 | input->middle_emulation = INT_MIN; |
31 | input->natural_scroll = INT_MIN; | 31 | input->natural_scroll = INT_MIN; |
32 | input->accel_profile = INT_MIN; | 32 | input->accel_profile = INT_MIN; |
33 | input->rotation_angle = FLT_MIN; | ||
33 | input->pointer_accel = FLT_MIN; | 34 | input->pointer_accel = FLT_MIN; |
34 | input->scroll_factor = FLT_MIN; | 35 | input->scroll_factor = FLT_MIN; |
35 | input->scroll_button = INT_MIN; | 36 | input->scroll_button = INT_MIN; |
37 | input->scroll_button_lock = INT_MIN; | ||
36 | input->scroll_method = INT_MIN; | 38 | input->scroll_method = INT_MIN; |
37 | input->left_handed = INT_MIN; | 39 | input->left_handed = INT_MIN; |
38 | input->repeat_delay = INT_MIN; | 40 | input->repeat_delay = INT_MIN; |
@@ -61,6 +63,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { | |||
61 | if (src->dwt != INT_MIN) { | 63 | if (src->dwt != INT_MIN) { |
62 | dst->dwt = src->dwt; | 64 | dst->dwt = src->dwt; |
63 | } | 65 | } |
66 | if (src->dwtp != INT_MIN) { | ||
67 | dst->dwtp = src->dwtp; | ||
68 | } | ||
64 | if (src->left_handed != INT_MIN) { | 69 | if (src->left_handed != INT_MIN) { |
65 | dst->left_handed = src->left_handed; | 70 | dst->left_handed = src->left_handed; |
66 | } | 71 | } |
@@ -70,6 +75,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { | |||
70 | if (src->natural_scroll != INT_MIN) { | 75 | if (src->natural_scroll != INT_MIN) { |
71 | dst->natural_scroll = src->natural_scroll; | 76 | dst->natural_scroll = src->natural_scroll; |
72 | } | 77 | } |
78 | if (src->rotation_angle != FLT_MIN) { | ||
79 | dst->rotation_angle = src->rotation_angle; | ||
80 | } | ||
73 | if (src->pointer_accel != FLT_MIN) { | 81 | if (src->pointer_accel != FLT_MIN) { |
74 | dst->pointer_accel = src->pointer_accel; | 82 | dst->pointer_accel = src->pointer_accel; |
75 | } | 83 | } |
@@ -88,6 +96,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { | |||
88 | if (src->scroll_button != INT_MIN) { | 96 | if (src->scroll_button != INT_MIN) { |
89 | dst->scroll_button = src->scroll_button; | 97 | dst->scroll_button = src->scroll_button; |
90 | } | 98 | } |
99 | if (src->scroll_button_lock != INT_MIN) { | ||
100 | dst->scroll_button_lock = src->scroll_button_lock; | ||
101 | } | ||
91 | if (src->send_events != INT_MIN) { | 102 | if (src->send_events != INT_MIN) { |
92 | dst->send_events = src->send_events; | 103 | dst->send_events = src->send_events; |
93 | } | 104 | } |
diff --git a/sway/config/output.c b/sway/config/output.c index 7d0ed395..54af5d8e 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -1,13 +1,15 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include <drm_fourcc.h> | ||
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <sys/socket.h> | 5 | #include <sys/socket.h> |
6 | #include <sys/wait.h> | 6 | #include <sys/wait.h> |
7 | #include <unistd.h> | 7 | #include <unistd.h> |
8 | #include <wlr/config.h> | ||
8 | #include <wlr/types/wlr_cursor.h> | 9 | #include <wlr/types/wlr_cursor.h> |
9 | #include <wlr/types/wlr_output_layout.h> | 10 | #include <wlr/types/wlr_output_layout.h> |
10 | #include <wlr/types/wlr_output.h> | 11 | #include <wlr/types/wlr_output.h> |
12 | #include <wlr/types/wlr_output_swapchain_manager.h> | ||
11 | #include "sway/config.h" | 13 | #include "sway/config.h" |
12 | #include "sway/input/cursor.h" | 14 | #include "sway/input/cursor.h" |
13 | #include "sway/output.h" | 15 | #include "sway/output.h" |
@@ -15,6 +17,10 @@ | |||
15 | #include "log.h" | 17 | #include "log.h" |
16 | #include "util.h" | 18 | #include "util.h" |
17 | 19 | ||
20 | #if WLR_HAS_DRM_BACKEND | ||
21 | #include <wlr/backend/drm.h> | ||
22 | #endif | ||
23 | |||
18 | int output_name_cmp(const void *item, const void *data) { | 24 | int output_name_cmp(const void *item, const void *data) { |
19 | const struct output_config *output = item; | 25 | const struct output_config *output = item; |
20 | const char *name = data; | 26 | const char *name = data; |
@@ -25,8 +31,10 @@ int output_name_cmp(const void *item, const void *data) { | |||
25 | void output_get_identifier(char *identifier, size_t len, | 31 | void output_get_identifier(char *identifier, size_t len, |
26 | struct sway_output *output) { | 32 | struct sway_output *output) { |
27 | struct wlr_output *wlr_output = output->wlr_output; | 33 | struct wlr_output *wlr_output = output->wlr_output; |
28 | snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, | 34 | snprintf(identifier, len, "%s %s %s", |
29 | wlr_output->serial); | 35 | wlr_output->make ? wlr_output->make : "Unknown", |
36 | wlr_output->model ? wlr_output->model : "Unknown", | ||
37 | wlr_output->serial ? wlr_output->serial : "Unknown"); | ||
30 | } | 38 | } |
31 | 39 | ||
32 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { | 40 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { |
@@ -58,6 +66,7 @@ struct output_config *new_output_config(const char *name) { | |||
58 | oc->width = oc->height = -1; | 66 | oc->width = oc->height = -1; |
59 | oc->refresh_rate = -1; | 67 | oc->refresh_rate = -1; |
60 | oc->custom_mode = -1; | 68 | oc->custom_mode = -1; |
69 | oc->drm_mode.type = -1; | ||
61 | oc->x = oc->y = -1; | 70 | oc->x = oc->y = -1; |
62 | oc->scale = -1; | 71 | oc->scale = -1; |
63 | oc->scale_filter = SCALE_FILTER_DEFAULT; | 72 | oc->scale_filter = SCALE_FILTER_DEFAULT; |
@@ -65,10 +74,77 @@ struct output_config *new_output_config(const char *name) { | |||
65 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | 74 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; |
66 | oc->max_render_time = -1; | 75 | oc->max_render_time = -1; |
67 | oc->adaptive_sync = -1; | 76 | oc->adaptive_sync = -1; |
77 | oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; | ||
78 | oc->power = -1; | ||
68 | return oc; | 79 | return oc; |
69 | } | 80 | } |
70 | 81 | ||
71 | void merge_output_config(struct output_config *dst, struct output_config *src) { | 82 | // supersede_output_config clears all fields in dst that were set in src |
83 | static void supersede_output_config(struct output_config *dst, struct output_config *src) { | ||
84 | if (src->enabled != -1) { | ||
85 | dst->enabled = -1; | ||
86 | } | ||
87 | if (src->width != -1) { | ||
88 | dst->width = -1; | ||
89 | } | ||
90 | if (src->height != -1) { | ||
91 | dst->height = -1; | ||
92 | } | ||
93 | if (src->x != -1) { | ||
94 | dst->x = -1; | ||
95 | } | ||
96 | if (src->y != -1) { | ||
97 | dst->y = -1; | ||
98 | } | ||
99 | if (src->scale != -1) { | ||
100 | dst->scale = -1; | ||
101 | } | ||
102 | if (src->scale_filter != SCALE_FILTER_DEFAULT) { | ||
103 | dst->scale_filter = SCALE_FILTER_DEFAULT; | ||
104 | } | ||
105 | if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { | ||
106 | dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | ||
107 | } | ||
108 | if (src->refresh_rate != -1) { | ||
109 | dst->refresh_rate = -1; | ||
110 | } | ||
111 | if (src->custom_mode != -1) { | ||
112 | dst->custom_mode = -1; | ||
113 | } | ||
114 | if (src->drm_mode.type != (uint32_t) -1) { | ||
115 | dst->drm_mode.type = -1; | ||
116 | } | ||
117 | if (src->transform != -1) { | ||
118 | dst->transform = -1; | ||
119 | } | ||
120 | if (src->max_render_time != -1) { | ||
121 | dst->max_render_time = -1; | ||
122 | } | ||
123 | if (src->adaptive_sync != -1) { | ||
124 | dst->adaptive_sync = -1; | ||
125 | } | ||
126 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
127 | dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; | ||
128 | } | ||
129 | if (src->background) { | ||
130 | free(dst->background); | ||
131 | dst->background = NULL; | ||
132 | } | ||
133 | if (src->background_option) { | ||
134 | free(dst->background_option); | ||
135 | dst->background_option = NULL; | ||
136 | } | ||
137 | if (src->background_fallback) { | ||
138 | free(dst->background_fallback); | ||
139 | dst->background_fallback = NULL; | ||
140 | } | ||
141 | if (src->power != -1) { | ||
142 | dst->power = -1; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | // merge_output_config sets all fields in dst that were set in src | ||
147 | static void merge_output_config(struct output_config *dst, struct output_config *src) { | ||
72 | if (src->enabled != -1) { | 148 | if (src->enabled != -1) { |
73 | dst->enabled = src->enabled; | 149 | dst->enabled = src->enabled; |
74 | } | 150 | } |
@@ -99,6 +175,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
99 | if (src->custom_mode != -1) { | 175 | if (src->custom_mode != -1) { |
100 | dst->custom_mode = src->custom_mode; | 176 | dst->custom_mode = src->custom_mode; |
101 | } | 177 | } |
178 | if (src->drm_mode.type != (uint32_t) -1) { | ||
179 | memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode)); | ||
180 | } | ||
102 | if (src->transform != -1) { | 181 | if (src->transform != -1) { |
103 | dst->transform = src->transform; | 182 | dst->transform = src->transform; |
104 | } | 183 | } |
@@ -108,6 +187,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
108 | if (src->adaptive_sync != -1) { | 187 | if (src->adaptive_sync != -1) { |
109 | dst->adaptive_sync = src->adaptive_sync; | 188 | dst->adaptive_sync = src->adaptive_sync; |
110 | } | 189 | } |
190 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
191 | dst->render_bit_depth = src->render_bit_depth; | ||
192 | } | ||
111 | if (src->background) { | 193 | if (src->background) { |
112 | free(dst->background); | 194 | free(dst->background); |
113 | dst->background = strdup(src->background); | 195 | dst->background = strdup(src->background); |
@@ -120,155 +202,124 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
120 | free(dst->background_fallback); | 202 | free(dst->background_fallback); |
121 | dst->background_fallback = strdup(src->background_fallback); | 203 | dst->background_fallback = strdup(src->background_fallback); |
122 | } | 204 | } |
123 | if (src->dpms_state != 0) { | 205 | if (src->power != -1) { |
124 | dst->dpms_state = src->dpms_state; | 206 | dst->power = src->power; |
125 | } | 207 | } |
126 | } | 208 | } |
127 | 209 | ||
128 | static void merge_wildcard_on_all(struct output_config *wildcard) { | 210 | void store_output_config(struct output_config *oc) { |
129 | for (int i = 0; i < config->output_configs->length; i++) { | 211 | bool merged = false; |
130 | struct output_config *oc = config->output_configs->items[i]; | 212 | bool wildcard = strcmp(oc->name, "*") == 0; |
131 | if (strcmp(wildcard->name, oc->name) != 0) { | 213 | struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name); |
132 | sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name); | ||
133 | merge_output_config(oc, wildcard); | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | 214 | ||
138 | static void merge_id_on_name(struct output_config *oc) { | ||
139 | char *id_on_name = NULL; | ||
140 | char id[128]; | 215 | char id[128]; |
141 | char *name = NULL; | 216 | if (output) { |
142 | struct sway_output *output; | ||
143 | wl_list_for_each(output, &root->all_outputs, link) { | ||
144 | name = output->wlr_output->name; | ||
145 | output_get_identifier(id, sizeof(id), output); | 217 | output_get_identifier(id, sizeof(id), output); |
146 | if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) { | ||
147 | size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1; | ||
148 | id_on_name = malloc(length); | ||
149 | if (!id_on_name) { | ||
150 | sway_log(SWAY_ERROR, "Failed to allocate id on name string"); | ||
151 | return; | ||
152 | } | ||
153 | snprintf(id_on_name, length, "%s on %s", id, name); | ||
154 | break; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | if (!id_on_name) { | ||
159 | return; | ||
160 | } | 218 | } |
161 | 219 | ||
162 | int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); | 220 | for (int i = 0; i < config->output_configs->length; i++) { |
163 | if (i >= 0) { | 221 | struct output_config *old = config->output_configs->items[i]; |
164 | sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); | 222 | |
165 | merge_output_config(config->output_configs->items[i], oc); | 223 | // If the old config matches the new config's name, regardless of |
166 | } else { | 224 | // whether it was name or identifier, merge on top of the existing |
167 | // If both a name and identifier config, exist generate an id on name | 225 | // config. If the new config is a wildcard, this also merges on top of |
168 | int ni = list_seq_find(config->output_configs, output_name_cmp, name); | 226 | // old wildcard configs. |
169 | int ii = list_seq_find(config->output_configs, output_name_cmp, id); | 227 | if (strcmp(old->name, oc->name) == 0) { |
170 | if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) | 228 | merge_output_config(old, oc); |
171 | || (ii >= 0 && strcmp(oc->name, name) == 0)) { | 229 | merged = true; |
172 | struct output_config *ion_oc = new_output_config(id_on_name); | 230 | continue; |
173 | if (ni >= 0) { | ||
174 | merge_output_config(ion_oc, config->output_configs->items[ni]); | ||
175 | } | ||
176 | if (ii >= 0) { | ||
177 | merge_output_config(ion_oc, config->output_configs->items[ii]); | ||
178 | } | ||
179 | merge_output_config(ion_oc, oc); | ||
180 | list_add(config->output_configs, ion_oc); | ||
181 | sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" | ||
182 | " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " | ||
183 | "transform %d) (bg %s %s) (dpms %d) (max render time: %d)", | ||
184 | ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, | ||
185 | ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, | ||
186 | ion_oc->transform, ion_oc->background, | ||
187 | ion_oc->background_option, ion_oc->dpms_state, | ||
188 | ion_oc->max_render_time); | ||
189 | } | 231 | } |
190 | } | ||
191 | free(id_on_name); | ||
192 | } | ||
193 | 232 | ||
194 | struct output_config *store_output_config(struct output_config *oc) { | 233 | // If the new config is a wildcard config we supersede all non-wildcard |
195 | bool wildcard = strcmp(oc->name, "*") == 0; | 234 | // configs. Old wildcard configs have already been handled above. |
196 | if (wildcard) { | 235 | if (wildcard) { |
197 | merge_wildcard_on_all(oc); | 236 | supersede_output_config(old, oc); |
198 | } else { | 237 | continue; |
199 | merge_id_on_name(oc); | 238 | } |
200 | } | ||
201 | 239 | ||
202 | int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); | 240 | // If the new config matches an output's name, and the old config |
203 | if (i >= 0) { | 241 | // matches on that output's identifier, supersede it. |
204 | sway_log(SWAY_DEBUG, "Merging on top of existing output config"); | 242 | if (output && strcmp(old->name, id) == 0 && |
205 | struct output_config *current = config->output_configs->items[i]; | 243 | strcmp(oc->name, output->wlr_output->name) == 0) { |
206 | merge_output_config(current, oc); | 244 | supersede_output_config(old, oc); |
207 | free_output_config(oc); | ||
208 | oc = current; | ||
209 | } else if (!wildcard) { | ||
210 | sway_log(SWAY_DEBUG, "Adding non-wildcard output config"); | ||
211 | i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
212 | if (i >= 0) { | ||
213 | sway_log(SWAY_DEBUG, "Merging on top of output * config"); | ||
214 | struct output_config *current = new_output_config(oc->name); | ||
215 | merge_output_config(current, config->output_configs->items[i]); | ||
216 | merge_output_config(current, oc); | ||
217 | free_output_config(oc); | ||
218 | oc = current; | ||
219 | } | 245 | } |
220 | list_add(config->output_configs, oc); | ||
221 | } else { | ||
222 | // New wildcard config. Just add it | ||
223 | sway_log(SWAY_DEBUG, "Adding output * config"); | ||
224 | list_add(config->output_configs, oc); | ||
225 | } | 246 | } |
226 | 247 | ||
227 | sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " | 248 | sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " |
228 | "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) " | 249 | "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " |
229 | "(max render time: %d)", | 250 | "(max render time: %d)", |
230 | oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, | 251 | oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, |
231 | oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), | 252 | oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), |
232 | oc->transform, oc->background, oc->background_option, oc->dpms_state, | 253 | oc->transform, oc->background, oc->background_option, oc->power, |
233 | oc->max_render_time); | 254 | oc->max_render_time); |
234 | 255 | ||
235 | return oc; | 256 | // If the configuration was not merged into an existing configuration, add |
257 | // it to the list. Otherwise we're done with it and can free it. | ||
258 | if (!merged) { | ||
259 | list_add(config->output_configs, oc); | ||
260 | } else { | ||
261 | free_output_config(oc); | ||
262 | } | ||
236 | } | 263 | } |
237 | 264 | ||
238 | static void set_mode(struct wlr_output *output, int width, int height, | 265 | static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, |
239 | float refresh_rate, bool custom) { | 266 | int width, int height, float refresh_rate, bool custom) { |
240 | // Not all floating point integers can be represented exactly | 267 | // Not all floating point integers can be represented exactly |
241 | // as (int)(1000 * mHz / 1000.f) | 268 | // as (int)(1000 * mHz / 1000.f) |
242 | // round() the result to avoid any error | 269 | // round() the result to avoid any error |
243 | int mhz = (int)round(refresh_rate * 1000); | 270 | int mhz = (int)roundf(refresh_rate * 1000); |
271 | // If no target refresh rate is given, match highest available | ||
272 | mhz = mhz <= 0 ? INT_MAX : mhz; | ||
244 | 273 | ||
245 | if (wl_list_empty(&output->modes) || custom) { | 274 | if (wl_list_empty(&output->modes) || custom) { |
246 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); | 275 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); |
247 | wlr_output_set_custom_mode(output, width, height, | 276 | wlr_output_state_set_custom_mode(pending, width, height, |
248 | refresh_rate > 0 ? mhz : 0); | 277 | refresh_rate > 0 ? mhz : 0); |
249 | return; | 278 | return; |
250 | } | 279 | } |
251 | 280 | ||
252 | struct wlr_output_mode *mode, *best = NULL; | 281 | struct wlr_output_mode *mode, *best = NULL; |
282 | int best_diff_mhz = INT_MAX; | ||
253 | wl_list_for_each(mode, &output->modes, link) { | 283 | wl_list_for_each(mode, &output->modes, link) { |
254 | if (mode->width == width && mode->height == height) { | 284 | if (mode->width == width && mode->height == height) { |
255 | if (mode->refresh == mhz) { | 285 | int diff_mhz = abs(mode->refresh - mhz); |
256 | best = mode; | 286 | if (diff_mhz < best_diff_mhz) { |
257 | break; | 287 | best_diff_mhz = diff_mhz; |
258 | } | ||
259 | if (best == NULL || mode->refresh > best->refresh) { | ||
260 | best = mode; | 288 | best = mode; |
289 | if (best_diff_mhz == 0) { | ||
290 | break; | ||
291 | } | ||
261 | } | 292 | } |
262 | } | 293 | } |
263 | } | 294 | } |
264 | if (!best) { | 295 | if (best) { |
265 | sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); | 296 | sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s", |
266 | sway_log(SWAY_INFO, "Picking preferred mode instead"); | 297 | best->width, best->height, best->refresh / 1000.f, output->name); |
267 | best = wlr_output_preferred_mode(output); | ||
268 | } else { | 298 | } else { |
269 | sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); | 299 | best = wlr_output_preferred_mode(output); |
300 | sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, " | ||
301 | "applying preferred mode (%dx%d@%.3fHz)", | ||
302 | width, height, refresh_rate, | ||
303 | best->width, best->height, best->refresh / 1000.f); | ||
270 | } | 304 | } |
271 | wlr_output_set_mode(output, best); | 305 | wlr_output_state_set_mode(pending, best); |
306 | } | ||
307 | |||
308 | static void set_modeline(struct wlr_output *output, | ||
309 | struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { | ||
310 | #if WLR_HAS_DRM_BACKEND | ||
311 | if (!wlr_output_is_drm(output)) { | ||
312 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | ||
313 | return; | ||
314 | } | ||
315 | sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); | ||
316 | struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); | ||
317 | if (mode) { | ||
318 | wlr_output_state_set_mode(pending, mode); | ||
319 | } | ||
320 | #else | ||
321 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | ||
322 | #endif | ||
272 | } | 323 | } |
273 | 324 | ||
274 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical | 325 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical |
@@ -289,23 +340,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) { | |||
289 | // 1 inch = 25.4 mm | 340 | // 1 inch = 25.4 mm |
290 | #define MM_PER_INCH 25.4 | 341 | #define MM_PER_INCH 25.4 |
291 | 342 | ||
292 | static int compute_default_scale(struct wlr_output *output) { | 343 | static int compute_default_scale(struct wlr_output *output, |
344 | struct wlr_output_state *pending) { | ||
293 | struct wlr_box box = { .width = output->width, .height = output->height }; | 345 | struct wlr_box box = { .width = output->width, .height = output->height }; |
294 | if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { | 346 | if (pending->committed & WLR_OUTPUT_STATE_MODE) { |
295 | switch (output->pending.mode_type) { | 347 | switch (pending->mode_type) { |
296 | case WLR_OUTPUT_STATE_MODE_FIXED: | 348 | case WLR_OUTPUT_STATE_MODE_FIXED: |
297 | box.width = output->pending.mode->width; | 349 | box.width = pending->mode->width; |
298 | box.height = output->pending.mode->height; | 350 | box.height = pending->mode->height; |
299 | break; | 351 | break; |
300 | case WLR_OUTPUT_STATE_MODE_CUSTOM: | 352 | case WLR_OUTPUT_STATE_MODE_CUSTOM: |
301 | box.width = output->pending.custom_mode.width; | 353 | box.width = pending->custom_mode.width; |
302 | box.height = output->pending.custom_mode.height; | 354 | box.height = pending->custom_mode.height; |
303 | break; | 355 | break; |
304 | } | 356 | } |
305 | } | 357 | } |
306 | enum wl_output_transform transform = output->transform; | 358 | enum wl_output_transform transform = output->transform; |
307 | if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { | 359 | if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) { |
308 | transform = output->pending.transform; | 360 | transform = pending->transform; |
309 | } | 361 | } |
310 | wlr_box_transform(&box, &box, transform, box.width, box.height); | 362 | wlr_box_transform(&box, &box, transform, box.width, box.height); |
311 | 363 | ||
@@ -334,42 +386,90 @@ static int compute_default_scale(struct wlr_output *output) { | |||
334 | return 2; | 386 | return 2; |
335 | } | 387 | } |
336 | 388 | ||
389 | /* Lists of formats to try, in order, when a specific render bit depth has | ||
390 | * been asked for. The second to last format in each list should always | ||
391 | * be XRGB8888, as a reliable backup in case the others are not available; | ||
392 | * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ | ||
393 | static const uint32_t *bit_depth_preferences[] = { | ||
394 | [RENDER_BIT_DEPTH_8] = (const uint32_t []){ | ||
395 | DRM_FORMAT_XRGB8888, | ||
396 | DRM_FORMAT_INVALID, | ||
397 | }, | ||
398 | [RENDER_BIT_DEPTH_10] = (const uint32_t []){ | ||
399 | DRM_FORMAT_XRGB2101010, | ||
400 | DRM_FORMAT_XBGR2101010, | ||
401 | DRM_FORMAT_XRGB8888, | ||
402 | DRM_FORMAT_INVALID, | ||
403 | }, | ||
404 | }; | ||
405 | |||
337 | static void queue_output_config(struct output_config *oc, | 406 | static void queue_output_config(struct output_config *oc, |
338 | struct sway_output *output) { | 407 | struct sway_output *output, struct wlr_output_state *pending) { |
339 | if (output == root->noop_output) { | 408 | if (output == root->fallback_output) { |
340 | return; | 409 | return; |
341 | } | 410 | } |
342 | 411 | ||
343 | struct wlr_output *wlr_output = output->wlr_output; | 412 | struct wlr_output *wlr_output = output->wlr_output; |
344 | 413 | ||
345 | if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { | 414 | if (oc && (!oc->enabled || oc->power == 0)) { |
346 | sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); | 415 | sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); |
347 | wlr_output_enable(wlr_output, false); | 416 | wlr_output_state_set_enabled(pending, false); |
348 | return; | 417 | return; |
349 | } | 418 | } |
350 | 419 | ||
351 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); | 420 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); |
352 | wlr_output_enable(wlr_output, true); | 421 | wlr_output_state_set_enabled(pending, true); |
353 | 422 | ||
354 | if (oc && oc->width > 0 && oc->height > 0) { | 423 | if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { |
424 | sway_log(SWAY_DEBUG, "Set %s modeline", | ||
425 | wlr_output->name); | ||
426 | set_modeline(wlr_output, pending, &oc->drm_mode); | ||
427 | } else if (oc && oc->width > 0 && oc->height > 0) { | ||
355 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", | 428 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", |
356 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); | 429 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); |
357 | set_mode(wlr_output, oc->width, oc->height, | 430 | set_mode(wlr_output, pending, oc->width, oc->height, |
358 | oc->refresh_rate, oc->custom_mode == 1); | 431 | oc->refresh_rate, oc->custom_mode == 1); |
359 | } else if (!wl_list_empty(&wlr_output->modes)) { | 432 | } else if (!wl_list_empty(&wlr_output->modes)) { |
360 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); | 433 | sway_log(SWAY_DEBUG, "Set preferred mode"); |
361 | wlr_output_set_mode(wlr_output, mode); | 434 | struct wlr_output_mode *preferred_mode = |
435 | wlr_output_preferred_mode(wlr_output); | ||
436 | wlr_output_state_set_mode(pending, preferred_mode); | ||
437 | |||
438 | if (!wlr_output_test_state(wlr_output, pending)) { | ||
439 | sway_log(SWAY_DEBUG, "Preferred mode rejected, " | ||
440 | "falling back to another mode"); | ||
441 | struct wlr_output_mode *mode; | ||
442 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
443 | if (mode == preferred_mode) { | ||
444 | continue; | ||
445 | } | ||
446 | |||
447 | wlr_output_state_set_mode(pending, mode); | ||
448 | if (wlr_output_test_state(wlr_output, pending)) { | ||
449 | break; | ||
450 | } | ||
451 | } | ||
452 | } | ||
362 | } | 453 | } |
363 | 454 | ||
364 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { | 455 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { |
365 | sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, | 456 | sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, |
366 | sway_wl_output_subpixel_to_string(oc->subpixel)); | 457 | sway_wl_output_subpixel_to_string(oc->subpixel)); |
367 | wlr_output_set_subpixel(wlr_output, oc->subpixel); | 458 | wlr_output_state_set_subpixel(pending, oc->subpixel); |
368 | } | 459 | } |
369 | 460 | ||
461 | enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; | ||
370 | if (oc && oc->transform >= 0) { | 462 | if (oc && oc->transform >= 0) { |
371 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); | 463 | tr = oc->transform; |
372 | wlr_output_set_transform(wlr_output, oc->transform); | 464 | #if WLR_HAS_DRM_BACKEND |
465 | } else if (wlr_output_is_drm(wlr_output)) { | ||
466 | tr = wlr_drm_connector_get_panel_orientation(wlr_output); | ||
467 | sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); | ||
468 | #endif | ||
469 | } | ||
470 | if (wlr_output->transform != tr) { | ||
471 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); | ||
472 | wlr_output_state_set_transform(pending, tr); | ||
373 | } | 473 | } |
374 | 474 | ||
375 | // Apply the scale last before the commit, because the scale auto-detection | 475 | // Apply the scale last before the commit, because the scale auto-detection |
@@ -377,50 +477,58 @@ static void queue_output_config(struct output_config *oc, | |||
377 | float scale; | 477 | float scale; |
378 | if (oc && oc->scale > 0) { | 478 | if (oc && oc->scale > 0) { |
379 | scale = oc->scale; | 479 | scale = oc->scale; |
480 | |||
481 | // The factional-scale-v1 protocol uses increments of 120ths to send | ||
482 | // the scale factor to the client. Adjust the scale so that we use the | ||
483 | // same value as the clients'. | ||
484 | float adjusted_scale = round(scale * 120) / 120; | ||
485 | if (scale != adjusted_scale) { | ||
486 | sway_log(SWAY_INFO, "Adjusting output scale from %f to %f", | ||
487 | scale, adjusted_scale); | ||
488 | scale = adjusted_scale; | ||
489 | } | ||
380 | } else { | 490 | } else { |
381 | scale = compute_default_scale(wlr_output); | 491 | scale = compute_default_scale(wlr_output, pending); |
382 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); | 492 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); |
383 | } | 493 | } |
384 | if (scale != wlr_output->scale) { | 494 | if (scale != wlr_output->scale) { |
385 | sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); | 495 | sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); |
386 | wlr_output_set_scale(wlr_output, scale); | 496 | wlr_output_state_set_scale(pending, scale); |
387 | } | 497 | } |
388 | 498 | ||
389 | if (oc && oc->adaptive_sync != -1) { | 499 | if (oc && oc->adaptive_sync != -1) { |
390 | sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, | 500 | sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, |
391 | oc->adaptive_sync); | 501 | oc->adaptive_sync); |
392 | wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); | 502 | wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); |
393 | } | 503 | if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) { |
394 | } | 504 | sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring"); |
395 | 505 | wlr_output_state_set_adaptive_sync_enabled(pending, false); | |
396 | bool apply_output_config(struct output_config *oc, struct sway_output *output) { | 506 | } |
397 | if (output == root->noop_output) { | ||
398 | return false; | ||
399 | } | 507 | } |
400 | 508 | ||
401 | struct wlr_output *wlr_output = output->wlr_output; | 509 | if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { |
402 | 510 | const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; | |
403 | // Flag to prevent the output mode event handler from calling us | 511 | assert(fmts); |
404 | output->enabling = (!oc || oc->enabled); | ||
405 | 512 | ||
406 | queue_output_config(oc, output); | 513 | for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { |
514 | wlr_output_state_set_render_format(pending, fmts[i]); | ||
515 | if (wlr_output_test_state(wlr_output, pending)) { | ||
516 | break; | ||
517 | } | ||
407 | 518 | ||
408 | if (!oc || oc->dpms_state != DPMS_OFF) { | 519 | sway_log(SWAY_DEBUG, "Preferred output format 0x%08x " |
409 | output->current_mode = wlr_output->pending.mode; | 520 | "failed to work, falling back to next in " |
521 | "list, 0x%08x", fmts[i], fmts[i + 1]); | ||
522 | } | ||
410 | } | 523 | } |
524 | } | ||
411 | 525 | ||
412 | sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); | 526 | static bool finalize_output_config(struct output_config *oc, struct sway_output *output) { |
413 | if (!wlr_output_commit(wlr_output)) { | 527 | if (output == root->fallback_output) { |
414 | // Failed to commit output changes, maybe the output is missing a CRTC. | ||
415 | // Leave the output disabled for now and try again when the output gets | ||
416 | // the mode we asked for. | ||
417 | sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name); | ||
418 | output->enabling = false; | ||
419 | return false; | 528 | return false; |
420 | } | 529 | } |
421 | 530 | ||
422 | output->enabling = false; | 531 | struct wlr_output *wlr_output = output->wlr_output; |
423 | |||
424 | if (oc && !oc->enabled) { | 532 | if (oc && !oc->enabled) { |
425 | sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); | 533 | sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); |
426 | if (output->enabled) { | 534 | if (output->enabled) { |
@@ -430,10 +538,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
430 | return true; | 538 | return true; |
431 | } | 539 | } |
432 | 540 | ||
433 | if (config->reloading) { | ||
434 | output_damage_whole(output); | ||
435 | } | ||
436 | |||
437 | if (oc) { | 541 | if (oc) { |
438 | enum scale_filter_mode scale_filter_old = output->scale_filter; | 542 | enum scale_filter_mode scale_filter_old = output->scale_filter; |
439 | switch (oc->scale_filter) { | 543 | switch (oc->scale_filter) { |
@@ -450,6 +554,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
450 | if (scale_filter_old != output->scale_filter) { | 554 | if (scale_filter_old != output->scale_filter) { |
451 | sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, | 555 | sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, |
452 | sway_output_scale_filter_to_string(output->scale_filter)); | 556 | sway_output_scale_filter_to_string(output->scale_filter)); |
557 | wlr_damage_ring_add_whole(&output->scene_output->damage_ring); | ||
453 | } | 558 | } |
454 | } | 559 | } |
455 | 560 | ||
@@ -462,12 +567,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
462 | } | 567 | } |
463 | 568 | ||
464 | // Update output->{lx, ly, width, height} | 569 | // Update output->{lx, ly, width, height} |
465 | struct wlr_box *output_box = | 570 | struct wlr_box output_box; |
466 | wlr_output_layout_get_box(root->output_layout, wlr_output); | 571 | wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box); |
467 | output->lx = output_box->x; | 572 | output->lx = output_box.x; |
468 | output->ly = output_box->y; | 573 | output->ly = output_box.y; |
469 | output->width = output_box->width; | 574 | output->width = output_box.width; |
470 | output->height = output_box->height; | 575 | output->height = output_box.height; |
471 | 576 | ||
472 | if (!output->enabled) { | 577 | if (!output->enabled) { |
473 | output_enable(output); | 578 | output_enable(output); |
@@ -479,29 +584,13 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
479 | output->max_render_time = oc->max_render_time; | 584 | output->max_render_time = oc->max_render_time; |
480 | } | 585 | } |
481 | 586 | ||
482 | // Reconfigure all devices, since input config may have been applied before | ||
483 | // this output came online, and some config items (like map_to_output) are | ||
484 | // dependent on an output being present. | ||
485 | input_manager_configure_all_inputs(); | ||
486 | // Reconfigure the cursor images, since the scale may have changed. | ||
487 | input_manager_configure_xcursor(); | ||
488 | return true; | 587 | return true; |
489 | } | 588 | } |
490 | 589 | ||
491 | bool test_output_config(struct output_config *oc, struct sway_output *output) { | ||
492 | if (output == root->noop_output) { | ||
493 | return false; | ||
494 | } | ||
495 | |||
496 | queue_output_config(oc, output); | ||
497 | bool ok = wlr_output_test(output->wlr_output); | ||
498 | wlr_output_rollback(output->wlr_output); | ||
499 | return ok; | ||
500 | } | ||
501 | |||
502 | static void default_output_config(struct output_config *oc, | 590 | static void default_output_config(struct output_config *oc, |
503 | struct wlr_output *wlr_output) { | 591 | struct wlr_output *wlr_output) { |
504 | oc->enabled = 1; | 592 | oc->enabled = 1; |
593 | oc->power = 1; | ||
505 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); | 594 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); |
506 | if (mode != NULL) { | 595 | if (mode != NULL) { |
507 | oc->width = mode->width; | 596 | oc->width = mode->width; |
@@ -514,147 +603,170 @@ static void default_output_config(struct output_config *oc, | |||
514 | struct sway_output *output = wlr_output->data; | 603 | struct sway_output *output = wlr_output->data; |
515 | oc->subpixel = output->detected_subpixel; | 604 | oc->subpixel = output->detected_subpixel; |
516 | oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; | 605 | oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; |
517 | oc->dpms_state = DPMS_ON; | ||
518 | oc->max_render_time = 0; | 606 | oc->max_render_time = 0; |
519 | } | 607 | } |
520 | 608 | ||
521 | static struct output_config *get_output_config(char *identifier, | 609 | // find_output_config returns a merged output_config containing all stored |
522 | struct sway_output *sway_output) { | 610 | // configuration that applies to the specified output. |
611 | struct output_config *find_output_config(struct sway_output *sway_output) { | ||
523 | const char *name = sway_output->wlr_output->name; | 612 | const char *name = sway_output->wlr_output->name; |
613 | struct output_config *oc = NULL; | ||
524 | 614 | ||
525 | struct output_config *oc_id_on_name = NULL; | 615 | struct output_config *result = new_output_config(name); |
526 | struct output_config *oc_name = NULL; | 616 | if (config->reloading) { |
527 | struct output_config *oc_id = NULL; | 617 | default_output_config(result, sway_output->wlr_output); |
618 | } | ||
528 | 619 | ||
529 | size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; | 620 | char id[128]; |
530 | char *id_on_name = malloc(length); | 621 | output_get_identifier(id, sizeof(id), sway_output); |
531 | snprintf(id_on_name, length, "%s on %s", identifier, name); | ||
532 | int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); | ||
533 | if (i >= 0) { | ||
534 | oc_id_on_name = config->output_configs->items[i]; | ||
535 | } else { | ||
536 | i = list_seq_find(config->output_configs, output_name_cmp, name); | ||
537 | if (i >= 0) { | ||
538 | oc_name = config->output_configs->items[i]; | ||
539 | } | ||
540 | 622 | ||
541 | i = list_seq_find(config->output_configs, output_name_cmp, identifier); | 623 | int i; |
542 | if (i >= 0) { | 624 | bool match = false; |
543 | oc_id = config->output_configs->items[i]; | 625 | if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) { |
544 | } | 626 | match = true; |
627 | oc = config->output_configs->items[i]; | ||
628 | merge_output_config(result, oc); | ||
545 | } | 629 | } |
546 | 630 | if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) { | |
547 | struct output_config *result = new_output_config("temp"); | 631 | match = true; |
548 | if (config->reloading) { | 632 | oc = config->output_configs->items[i]; |
549 | default_output_config(result, sway_output->wlr_output); | 633 | merge_output_config(result, oc); |
550 | } | 634 | } |
551 | if (oc_id_on_name) { | 635 | if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) { |
552 | // Already have an identifier on name config, use that | 636 | match = true; |
553 | free(result->name); | 637 | oc = config->output_configs->items[i]; |
554 | result->name = strdup(id_on_name); | 638 | merge_output_config(result, oc); |
555 | merge_output_config(result, oc_id_on_name); | 639 | } |
556 | } else if (oc_name && oc_id) { | 640 | |
557 | // Generate a config named `<identifier> on <name>` which contains a | 641 | if (!match && !config->reloading) { |
558 | // merged copy of the identifier on name. This will make sure that both | 642 | // No name, identifier, or wildcard config. Since we are not |
559 | // identifier and name configs are respected, with identifier getting | 643 | // reloading with defaults, the output config will be empty, so |
560 | // priority | 644 | // just return NULL |
561 | struct output_config *temp = new_output_config(id_on_name); | 645 | free_output_config(result); |
562 | merge_output_config(temp, oc_name); | 646 | return NULL; |
563 | merge_output_config(temp, oc_id); | ||
564 | list_add(config->output_configs, temp); | ||
565 | |||
566 | free(result->name); | ||
567 | result->name = strdup(id_on_name); | ||
568 | merge_output_config(result, temp); | ||
569 | |||
570 | sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" | ||
571 | " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" | ||
572 | " (dpms %d) (max render time: %d)", result->name, result->enabled, | ||
573 | result->width, result->height, result->refresh_rate, | ||
574 | result->x, result->y, result->scale, result->transform, | ||
575 | result->background, result->background_option, result->dpms_state, | ||
576 | result->max_render_time); | ||
577 | } else if (oc_name) { | ||
578 | // No identifier config, just return a copy of the name config | ||
579 | free(result->name); | ||
580 | result->name = strdup(name); | ||
581 | merge_output_config(result, oc_name); | ||
582 | } else if (oc_id) { | ||
583 | // No name config, just return a copy of the identifier config | ||
584 | free(result->name); | ||
585 | result->name = strdup(identifier); | ||
586 | merge_output_config(result, oc_id); | ||
587 | } else { | ||
588 | i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
589 | if (i >= 0) { | ||
590 | // No name or identifier config, but there is a wildcard config | ||
591 | free(result->name); | ||
592 | result->name = strdup("*"); | ||
593 | merge_output_config(result, config->output_configs->items[i]); | ||
594 | } else if (!config->reloading) { | ||
595 | // No name, identifier, or wildcard config. Since we are not | ||
596 | // reloading with defaults, the output config will be empty, so | ||
597 | // just return NULL | ||
598 | free_output_config(result); | ||
599 | result = NULL; | ||
600 | } | ||
601 | } | 647 | } |
602 | 648 | ||
603 | free(id_on_name); | ||
604 | return result; | 649 | return result; |
605 | } | 650 | } |
606 | 651 | ||
607 | struct output_config *find_output_config(struct sway_output *output) { | 652 | bool apply_output_configs(struct matched_output_config *configs, |
608 | char id[128]; | 653 | size_t configs_len, bool test_only) { |
609 | output_get_identifier(id, sizeof(id), output); | 654 | struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); |
610 | return get_output_config(id, output); | 655 | if (!states) { |
611 | } | 656 | return false; |
657 | } | ||
612 | 658 | ||
613 | void apply_output_config_to_outputs(struct output_config *oc) { | 659 | sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len); |
614 | // Try to find the output container and apply configuration now. If | 660 | for (size_t idx = 0; idx < configs_len; idx++) { |
615 | // this is during startup then there will be no container and config | 661 | struct matched_output_config *cfg = &configs[idx]; |
616 | // will be applied during normal "new output" event from wlroots. | 662 | struct wlr_backend_output_state *backend_state = &states[idx]; |
617 | bool wildcard = strcmp(oc->name, "*") == 0; | ||
618 | char id[128]; | ||
619 | struct sway_output *sway_output, *tmp; | ||
620 | wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { | ||
621 | char *name = sway_output->wlr_output->name; | ||
622 | output_get_identifier(id, sizeof(id), sway_output); | ||
623 | if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { | ||
624 | struct output_config *current = get_output_config(id, sway_output); | ||
625 | if (!current) { | ||
626 | // No stored output config matched, apply oc directly | ||
627 | sway_log(SWAY_DEBUG, "Applying oc directly"); | ||
628 | current = new_output_config(oc->name); | ||
629 | merge_output_config(current, oc); | ||
630 | } | ||
631 | apply_output_config(current, sway_output); | ||
632 | free_output_config(current); | ||
633 | 663 | ||
634 | if (!wildcard) { | 664 | backend_state->output = cfg->output->wlr_output; |
635 | // Stop looking if the output config isn't applicable to all | 665 | wlr_output_state_init(&backend_state->base); |
636 | // outputs | 666 | |
637 | break; | 667 | sway_log(SWAY_DEBUG, "Preparing config for %s", |
638 | } | 668 | cfg->output->wlr_output->name); |
669 | queue_output_config(cfg->config, cfg->output, &backend_state->base); | ||
670 | } | ||
671 | |||
672 | struct wlr_output_swapchain_manager swapchain_mgr; | ||
673 | wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend); | ||
674 | |||
675 | bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len); | ||
676 | if (!ok) { | ||
677 | sway_log(SWAY_ERROR, "Swapchain prepare failed"); | ||
678 | goto out; | ||
679 | } | ||
680 | |||
681 | if (test_only) { | ||
682 | // The swapchain manager already did a test for us | ||
683 | goto out; | ||
684 | } | ||
685 | |||
686 | for (size_t idx = 0; idx < configs_len; idx++) { | ||
687 | struct matched_output_config *cfg = &configs[idx]; | ||
688 | struct wlr_backend_output_state *backend_state = &states[idx]; | ||
689 | |||
690 | struct wlr_scene_output_state_options opts = { | ||
691 | .swapchain = wlr_output_swapchain_manager_get_swapchain( | ||
692 | &swapchain_mgr, backend_state->output), | ||
693 | }; | ||
694 | struct wlr_scene_output *scene_output = cfg->output->scene_output; | ||
695 | struct wlr_output_state *state = &backend_state->base; | ||
696 | if (!wlr_scene_output_build_state(scene_output, state, &opts)) { | ||
697 | sway_log(SWAY_ERROR, "Building output state for '%s' failed", | ||
698 | backend_state->output->name); | ||
699 | goto out; | ||
639 | } | 700 | } |
640 | } | 701 | } |
641 | 702 | ||
703 | ok = wlr_backend_commit(server.backend, states, configs_len); | ||
704 | if (!ok) { | ||
705 | sway_log(SWAY_ERROR, "Backend commit failed"); | ||
706 | goto out; | ||
707 | } | ||
708 | |||
709 | sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len); | ||
710 | |||
711 | wlr_output_swapchain_manager_apply(&swapchain_mgr); | ||
712 | |||
713 | for (size_t idx = 0; idx < configs_len; idx++) { | ||
714 | struct matched_output_config *cfg = &configs[idx]; | ||
715 | sway_log(SWAY_DEBUG, "Finalizing config for %s", | ||
716 | cfg->output->wlr_output->name); | ||
717 | finalize_output_config(cfg->config, cfg->output); | ||
718 | } | ||
719 | |||
720 | out: | ||
721 | wlr_output_swapchain_manager_finish(&swapchain_mgr); | ||
722 | for (size_t idx = 0; idx < configs_len; idx++) { | ||
723 | struct wlr_backend_output_state *backend_state = &states[idx]; | ||
724 | wlr_output_state_finish(&backend_state->base); | ||
725 | } | ||
726 | free(states); | ||
727 | |||
728 | // Reconfigure all devices, since input config may have been applied before | ||
729 | // this output came online, and some config items (like map_to_output) are | ||
730 | // dependent on an output being present. | ||
731 | input_manager_configure_all_input_mappings(); | ||
732 | // Reconfigure the cursor images, since the scale may have changed. | ||
733 | input_manager_configure_xcursor(); | ||
734 | |||
642 | struct sway_seat *seat; | 735 | struct sway_seat *seat; |
643 | wl_list_for_each(seat, &server.input->seats, link) { | 736 | wl_list_for_each(seat, &server.input->seats, link) { |
644 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 737 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
645 | cursor_rebase(seat->cursor); | 738 | cursor_rebase(seat->cursor); |
646 | } | 739 | } |
740 | |||
741 | return ok; | ||
647 | } | 742 | } |
648 | 743 | ||
649 | void reset_outputs(void) { | 744 | void apply_all_output_configs(void) { |
650 | struct output_config *oc = NULL; | 745 | size_t configs_len = wl_list_length(&root->all_outputs); |
651 | int i = list_seq_find(config->output_configs, output_name_cmp, "*"); | 746 | struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); |
652 | if (i >= 0) { | 747 | if (!configs) { |
653 | oc = config->output_configs->items[i]; | 748 | return; |
654 | } else { | ||
655 | oc = store_output_config(new_output_config("*")); | ||
656 | } | 749 | } |
657 | apply_output_config_to_outputs(oc); | 750 | |
751 | int config_idx = 0; | ||
752 | struct sway_output *sway_output; | ||
753 | wl_list_for_each(sway_output, &root->all_outputs, link) { | ||
754 | if (sway_output == root->fallback_output) { | ||
755 | configs_len--; | ||
756 | continue; | ||
757 | } | ||
758 | |||
759 | struct matched_output_config *config = &configs[config_idx++]; | ||
760 | config->output = sway_output; | ||
761 | config->config = find_output_config(sway_output); | ||
762 | } | ||
763 | |||
764 | apply_output_configs(configs, configs_len, false); | ||
765 | for (size_t idx = 0; idx < configs_len; idx++) { | ||
766 | struct matched_output_config *cfg = &configs[idx]; | ||
767 | free_output_config(cfg->config); | ||
768 | } | ||
769 | free(configs); | ||
658 | } | 770 | } |
659 | 771 | ||
660 | void free_output_config(struct output_config *oc) { | 772 | void free_output_config(struct output_config *oc) { |
@@ -704,6 +816,8 @@ static bool _spawn_swaybg(char **command) { | |||
704 | sway_log_errno(SWAY_ERROR, "fork failed"); | 816 | sway_log_errno(SWAY_ERROR, "fork failed"); |
705 | return false; | 817 | return false; |
706 | } else if (pid == 0) { | 818 | } else if (pid == 0) { |
819 | restore_nofile_limit(); | ||
820 | |||
707 | pid = fork(); | 821 | pid = fork(); |
708 | if (pid < 0) { | 822 | if (pid < 0) { |
709 | sway_log_errno(SWAY_ERROR, "fork failed"); | 823 | sway_log_errno(SWAY_ERROR, "fork failed"); |
@@ -719,7 +833,9 @@ static bool _spawn_swaybg(char **command) { | |||
719 | setenv("WAYLAND_SOCKET", wayland_socket_str, true); | 833 | setenv("WAYLAND_SOCKET", wayland_socket_str, true); |
720 | 834 | ||
721 | execvp(command[0], command); | 835 | execvp(command[0], command); |
722 | sway_log_errno(SWAY_ERROR, "execvp failed"); | 836 | sway_log_errno(SWAY_ERROR, "failed to execute '%s' " |
837 | "(background configuration probably not applied)", | ||
838 | command[0]); | ||
723 | _exit(EXIT_FAILURE); | 839 | _exit(EXIT_FAILURE); |
724 | } | 840 | } |
725 | _exit(EXIT_SUCCESS); | 841 | _exit(EXIT_SUCCESS); |
@@ -729,12 +845,13 @@ static bool _spawn_swaybg(char **command) { | |||
729 | sway_log_errno(SWAY_ERROR, "close failed"); | 845 | sway_log_errno(SWAY_ERROR, "close failed"); |
730 | return false; | 846 | return false; |
731 | } | 847 | } |
732 | if (waitpid(pid, NULL, 0) < 0) { | 848 | int fork_status = 0; |
849 | if (waitpid(pid, &fork_status, 0) < 0) { | ||
733 | sway_log_errno(SWAY_ERROR, "waitpid failed"); | 850 | sway_log_errno(SWAY_ERROR, "waitpid failed"); |
734 | return false; | 851 | return false; |
735 | } | 852 | } |
736 | 853 | ||
737 | return true; | 854 | return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS; |
738 | } | 855 | } |
739 | 856 | ||
740 | bool spawn_swaybg(void) { | 857 | bool spawn_swaybg(void) { |
diff --git a/sway/config/seat.c b/sway/config/seat.c index 84260aa3..f2326189 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -99,7 +98,6 @@ static void seat_attachment_config_free( | |||
99 | struct seat_attachment_config *attachment) { | 98 | struct seat_attachment_config *attachment) { |
100 | free(attachment->identifier); | 99 | free(attachment->identifier); |
101 | free(attachment); | 100 | free(attachment); |
102 | return; | ||
103 | } | 101 | } |
104 | 102 | ||
105 | static struct seat_attachment_config *seat_attachment_config_copy( | 103 | static struct seat_attachment_config *seat_attachment_config_copy( |
diff --git a/sway/criteria.c b/sway/criteria.c index d2a5566f..e16b4fa8 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -1,9 +1,9 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
5 | #include <strings.h> | 4 | #include <strings.h> |
6 | #include <pcre.h> | 5 | #define PCRE2_CODE_UNIT_WIDTH 8 |
6 | #include <pcre2.h> | ||
7 | #include "sway/criteria.h" | 7 | #include "sway/criteria.h" |
8 | #include "sway/tree/container.h" | 8 | #include "sway/tree/container.h" |
9 | #include "sway/config.h" | 9 | #include "sway/config.h" |
@@ -18,6 +18,7 @@ | |||
18 | bool criteria_is_empty(struct criteria *criteria) { | 18 | bool criteria_is_empty(struct criteria *criteria) { |
19 | return !criteria->title | 19 | return !criteria->title |
20 | && !criteria->shell | 20 | && !criteria->shell |
21 | && !criteria->all | ||
21 | && !criteria->app_id | 22 | && !criteria->app_id |
22 | && !criteria->con_mark | 23 | && !criteria->con_mark |
23 | && !criteria->con_id | 24 | && !criteria->con_id |
@@ -40,17 +41,19 @@ bool criteria_is_empty(struct criteria *criteria) { | |||
40 | char *error = NULL; | 41 | char *error = NULL; |
41 | 42 | ||
42 | // Returns error string on failure or NULL otherwise. | 43 | // Returns error string on failure or NULL otherwise. |
43 | static bool generate_regex(pcre **regex, char *value) { | 44 | static bool generate_regex(pcre2_code **regex, char *value) { |
44 | const char *reg_err; | 45 | int errorcode; |
45 | int offset; | 46 | PCRE2_SIZE offset; |
46 | |||
47 | *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, ®_err, &offset, NULL); | ||
48 | 47 | ||
48 | *regex = pcre2_compile((PCRE2_SPTR)value, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_UCP, &errorcode, &offset, NULL); | ||
49 | if (!*regex) { | 49 | if (!*regex) { |
50 | PCRE2_UCHAR buffer[256]; | ||
51 | pcre2_get_error_message(errorcode, buffer, sizeof(buffer)); | ||
52 | |||
50 | const char *fmt = "Regex compilation for '%s' failed: %s"; | 53 | const char *fmt = "Regex compilation for '%s' failed: %s"; |
51 | int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; | 54 | int len = strlen(fmt) + strlen(value) + strlen((char*) buffer) - 3; |
52 | error = malloc(len); | 55 | error = malloc(len); |
53 | snprintf(error, len, fmt, value, reg_err); | 56 | snprintf(error, len, fmt, value, buffer); |
54 | return false; | 57 | return false; |
55 | } | 58 | } |
56 | 59 | ||
@@ -66,7 +69,7 @@ static bool pattern_create(struct pattern **pattern, char *value) { | |||
66 | if (strcmp(value, "__focused__") == 0) { | 69 | if (strcmp(value, "__focused__") == 0) { |
67 | (*pattern)->match_type = PATTERN_FOCUSED; | 70 | (*pattern)->match_type = PATTERN_FOCUSED; |
68 | } else { | 71 | } else { |
69 | (*pattern)->match_type = PATTERN_PCRE; | 72 | (*pattern)->match_type = PATTERN_PCRE2; |
70 | if (!generate_regex(&(*pattern)->regex, value)) { | 73 | if (!generate_regex(&(*pattern)->regex, value)) { |
71 | return false; | 74 | return false; |
72 | }; | 75 | }; |
@@ -77,7 +80,7 @@ static bool pattern_create(struct pattern **pattern, char *value) { | |||
77 | static void pattern_destroy(struct pattern *pattern) { | 80 | static void pattern_destroy(struct pattern *pattern) { |
78 | if (pattern) { | 81 | if (pattern) { |
79 | if (pattern->regex) { | 82 | if (pattern->regex) { |
80 | pcre_free(pattern->regex); | 83 | pcre2_code_free(pattern->regex); |
81 | } | 84 | } |
82 | free(pattern); | 85 | free(pattern); |
83 | } | 86 | } |
@@ -93,14 +96,18 @@ void criteria_destroy(struct criteria *criteria) { | |||
93 | pattern_destroy(criteria->window_role); | 96 | pattern_destroy(criteria->window_role); |
94 | #endif | 97 | #endif |
95 | pattern_destroy(criteria->con_mark); | 98 | pattern_destroy(criteria->con_mark); |
96 | free(criteria->workspace); | 99 | pattern_destroy(criteria->workspace); |
100 | free(criteria->target); | ||
97 | free(criteria->cmdlist); | 101 | free(criteria->cmdlist); |
98 | free(criteria->raw); | 102 | free(criteria->raw); |
99 | free(criteria); | 103 | free(criteria); |
100 | } | 104 | } |
101 | 105 | ||
102 | static int regex_cmp(const char *item, const pcre *regex) { | 106 | static int regex_cmp(const char *item, const pcre2_code *regex) { |
103 | return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); | 107 | pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL); |
108 | int result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL); | ||
109 | pcre2_match_data_free(match_data); | ||
110 | return result; | ||
104 | } | 111 | } |
105 | 112 | ||
106 | #if HAVE_XWAYLAND | 113 | #if HAVE_XWAYLAND |
@@ -155,7 +162,7 @@ static bool criteria_matches_container(struct criteria *criteria, | |||
155 | bool exists = false; | 162 | bool exists = false; |
156 | struct sway_container *con = container; | 163 | struct sway_container *con = container; |
157 | for (int i = 0; i < con->marks->length; ++i) { | 164 | for (int i = 0; i < con->marks->length; ++i) { |
158 | if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) == 0) { | 165 | if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) { |
159 | exists = true; | 166 | exists = true; |
160 | break; | 167 | break; |
161 | } | 168 | } |
@@ -183,7 +190,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
183 | if (criteria->title) { | 190 | if (criteria->title) { |
184 | const char *title = view_get_title(view); | 191 | const char *title = view_get_title(view); |
185 | if (!title) { | 192 | if (!title) { |
186 | return false; | 193 | title = ""; |
187 | } | 194 | } |
188 | 195 | ||
189 | switch (criteria->title->match_type) { | 196 | switch (criteria->title->match_type) { |
@@ -192,8 +199,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
192 | return false; | 199 | return false; |
193 | } | 200 | } |
194 | break; | 201 | break; |
195 | case PATTERN_PCRE: | 202 | case PATTERN_PCRE2: |
196 | if (regex_cmp(title, criteria->title->regex) != 0) { | 203 | if (regex_cmp(title, criteria->title->regex) < 0) { |
197 | return false; | 204 | return false; |
198 | } | 205 | } |
199 | break; | 206 | break; |
@@ -203,7 +210,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
203 | if (criteria->shell) { | 210 | if (criteria->shell) { |
204 | const char *shell = view_get_shell(view); | 211 | const char *shell = view_get_shell(view); |
205 | if (!shell) { | 212 | if (!shell) { |
206 | return false; | 213 | shell = ""; |
207 | } | 214 | } |
208 | 215 | ||
209 | switch (criteria->shell->match_type) { | 216 | switch (criteria->shell->match_type) { |
@@ -212,8 +219,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
212 | return false; | 219 | return false; |
213 | } | 220 | } |
214 | break; | 221 | break; |
215 | case PATTERN_PCRE: | 222 | case PATTERN_PCRE2: |
216 | if (regex_cmp(shell, criteria->shell->regex) != 0) { | 223 | if (regex_cmp(shell, criteria->shell->regex) < 0) { |
217 | return false; | 224 | return false; |
218 | } | 225 | } |
219 | break; | 226 | break; |
@@ -223,7 +230,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
223 | if (criteria->app_id) { | 230 | if (criteria->app_id) { |
224 | const char *app_id = view_get_app_id(view); | 231 | const char *app_id = view_get_app_id(view); |
225 | if (!app_id) { | 232 | if (!app_id) { |
226 | return false; | 233 | app_id = ""; |
227 | } | 234 | } |
228 | 235 | ||
229 | switch (criteria->app_id->match_type) { | 236 | switch (criteria->app_id->match_type) { |
@@ -232,8 +239,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
232 | return false; | 239 | return false; |
233 | } | 240 | } |
234 | break; | 241 | break; |
235 | case PATTERN_PCRE: | 242 | case PATTERN_PCRE2: |
236 | if (regex_cmp(app_id, criteria->app_id->regex) != 0) { | 243 | if (regex_cmp(app_id, criteria->app_id->regex) < 0) { |
237 | return false; | 244 | return false; |
238 | } | 245 | } |
239 | break; | 246 | break; |
@@ -255,7 +262,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
255 | if (criteria->class) { | 262 | if (criteria->class) { |
256 | const char *class = view_get_class(view); | 263 | const char *class = view_get_class(view); |
257 | if (!class) { | 264 | if (!class) { |
258 | return false; | 265 | class = ""; |
259 | } | 266 | } |
260 | 267 | ||
261 | switch (criteria->class->match_type) { | 268 | switch (criteria->class->match_type) { |
@@ -264,8 +271,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
264 | return false; | 271 | return false; |
265 | } | 272 | } |
266 | break; | 273 | break; |
267 | case PATTERN_PCRE: | 274 | case PATTERN_PCRE2: |
268 | if (regex_cmp(class, criteria->class->regex) != 0) { | 275 | if (regex_cmp(class, criteria->class->regex) < 0) { |
269 | return false; | 276 | return false; |
270 | } | 277 | } |
271 | break; | 278 | break; |
@@ -275,17 +282,17 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
275 | if (criteria->instance) { | 282 | if (criteria->instance) { |
276 | const char *instance = view_get_instance(view); | 283 | const char *instance = view_get_instance(view); |
277 | if (!instance) { | 284 | if (!instance) { |
278 | return false; | 285 | instance = ""; |
279 | } | 286 | } |
280 | 287 | ||
281 | switch (criteria->instance->match_type) { | 288 | switch (criteria->instance->match_type) { |
282 | case PATTERN_FOCUSED: | 289 | case PATTERN_FOCUSED: |
283 | if (focused && strcmp(instance, view_get_instance(focused))) { | 290 | if (focused && lenient_strcmp(instance, view_get_instance(focused))) { |
284 | return false; | 291 | return false; |
285 | } | 292 | } |
286 | break; | 293 | break; |
287 | case PATTERN_PCRE: | 294 | case PATTERN_PCRE2: |
288 | if (regex_cmp(instance, criteria->instance->regex) != 0) { | 295 | if (regex_cmp(instance, criteria->instance->regex) < 0) { |
289 | return false; | 296 | return false; |
290 | } | 297 | } |
291 | break; | 298 | break; |
@@ -295,17 +302,17 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
295 | if (criteria->window_role) { | 302 | if (criteria->window_role) { |
296 | const char *window_role = view_get_window_role(view); | 303 | const char *window_role = view_get_window_role(view); |
297 | if (!window_role) { | 304 | if (!window_role) { |
298 | return false; | 305 | window_role = ""; |
299 | } | 306 | } |
300 | 307 | ||
301 | switch (criteria->window_role->match_type) { | 308 | switch (criteria->window_role->match_type) { |
302 | case PATTERN_FOCUSED: | 309 | case PATTERN_FOCUSED: |
303 | if (focused && strcmp(window_role, view_get_window_role(focused))) { | 310 | if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) { |
304 | return false; | 311 | return false; |
305 | } | 312 | } |
306 | break; | 313 | break; |
307 | case PATTERN_PCRE: | 314 | case PATTERN_PCRE2: |
308 | if (regex_cmp(window_role, criteria->window_role->regex) != 0) { | 315 | if (regex_cmp(window_role, criteria->window_role->regex) < 0) { |
309 | return false; | 316 | return false; |
310 | } | 317 | } |
311 | break; | 318 | break; |
@@ -363,8 +370,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
363 | return false; | 370 | return false; |
364 | } | 371 | } |
365 | break; | 372 | break; |
366 | case PATTERN_PCRE: | 373 | case PATTERN_PCRE2: |
367 | if (regex_cmp(ws->name, criteria->workspace->regex) != 0) { | 374 | if (regex_cmp(ws->name, criteria->workspace->regex) < 0) { |
368 | return false; | 375 | return false; |
369 | } | 376 | } |
370 | break; | 377 | break; |
@@ -449,6 +456,7 @@ static enum atom_name parse_window_type(const char *type) { | |||
449 | #endif | 456 | #endif |
450 | 457 | ||
451 | enum criteria_token { | 458 | enum criteria_token { |
459 | T_ALL, | ||
452 | T_APP_ID, | 460 | T_APP_ID, |
453 | T_CON_ID, | 461 | T_CON_ID, |
454 | T_CON_MARK, | 462 | T_CON_MARK, |
@@ -471,7 +479,9 @@ enum criteria_token { | |||
471 | }; | 479 | }; |
472 | 480 | ||
473 | static enum criteria_token token_from_name(char *name) { | 481 | static enum criteria_token token_from_name(char *name) { |
474 | if (strcmp(name, "app_id") == 0) { | 482 | if (strcmp(name, "all") == 0) { |
483 | return T_ALL; | ||
484 | } else if (strcmp(name, "app_id") == 0) { | ||
475 | return T_APP_ID; | 485 | return T_APP_ID; |
476 | } else if (strcmp(name, "con_id") == 0) { | 486 | } else if (strcmp(name, "con_id") == 0) { |
477 | return T_CON_ID; | 487 | return T_CON_ID; |
@@ -517,8 +527,8 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
517 | return false; | 527 | return false; |
518 | } | 528 | } |
519 | 529 | ||
520 | // Require value, unless token is floating or tiled | 530 | // Require value, unless token is all, floating or tiled |
521 | if (!value && token != T_FLOATING && token != T_TILING) { | 531 | if (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) { |
522 | const char *fmt = "Token '%s' requires a value"; | 532 | const char *fmt = "Token '%s' requires a value"; |
523 | int len = strlen(fmt) + strlen(name) - 1; | 533 | int len = strlen(fmt) + strlen(name) - 1; |
524 | error = malloc(len); | 534 | error = malloc(len); |
@@ -528,6 +538,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
528 | 538 | ||
529 | char *endptr = NULL; | 539 | char *endptr = NULL; |
530 | switch (token) { | 540 | switch (token) { |
541 | case T_ALL: | ||
542 | criteria->all = true; | ||
543 | break; | ||
531 | case T_TITLE: | 544 | case T_TITLE: |
532 | pattern_create(&criteria->title, value); | 545 | pattern_create(&criteria->title, value); |
533 | break; | 546 | break; |
@@ -676,7 +689,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
676 | } | 689 | } |
677 | name = calloc(head - namestart + 1, 1); | 690 | name = calloc(head - namestart + 1, 1); |
678 | if (head != namestart) { | 691 | if (head != namestart) { |
679 | strncpy(name, namestart, head - namestart); | 692 | memcpy(name, namestart, head - namestart); |
680 | } | 693 | } |
681 | // Parse token value | 694 | // Parse token value |
682 | skip_spaces(&head); | 695 | skip_spaces(&head); |
@@ -703,7 +716,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
703 | } | 716 | } |
704 | } | 717 | } |
705 | value = calloc(head - valuestart + 1, 1); | 718 | value = calloc(head - valuestart + 1, 1); |
706 | strncpy(value, valuestart, head - valuestart); | 719 | memcpy(value, valuestart, head - valuestart); |
707 | if (in_quotes) { | 720 | if (in_quotes) { |
708 | ++head; | 721 | ++head; |
709 | in_quotes = false; | 722 | in_quotes = false; |
@@ -734,7 +747,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
734 | ++head; | 747 | ++head; |
735 | int len = head - raw; | 748 | int len = head - raw; |
736 | criteria->raw = calloc(len + 1, 1); | 749 | criteria->raw = calloc(len + 1, 1); |
737 | strncpy(criteria->raw, raw, len); | 750 | memcpy(criteria->raw, raw, len); |
738 | return criteria; | 751 | return criteria; |
739 | 752 | ||
740 | cleanup: | 753 | cleanup: |
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c deleted file mode 100644 index ec45d80a..00000000 --- a/sway/desktop/desktop.c +++ /dev/null | |||
@@ -1,39 +0,0 @@ | |||
1 | #include "sway/tree/container.h" | ||
2 | #include "sway/desktop.h" | ||
3 | #include "sway/output.h" | ||
4 | |||
5 | void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, | ||
6 | bool whole) { | ||
7 | for (int i = 0; i < root->outputs->length; ++i) { | ||
8 | struct sway_output *output = root->outputs->items[i]; | ||
9 | struct wlr_box *output_box = wlr_output_layout_get_box( | ||
10 | root->output_layout, output->wlr_output); | ||
11 | output_damage_surface(output, lx - output_box->x, | ||
12 | ly - output_box->y, surface, whole); | ||
13 | } | ||
14 | } | ||
15 | |||
16 | void desktop_damage_whole_container(struct sway_container *con) { | ||
17 | for (int i = 0; i < root->outputs->length; ++i) { | ||
18 | struct sway_output *output = root->outputs->items[i]; | ||
19 | output_damage_whole_container(output, con); | ||
20 | } | ||
21 | } | ||
22 | |||
23 | void desktop_damage_box(struct wlr_box *box) { | ||
24 | for (int i = 0; i < root->outputs->length; ++i) { | ||
25 | struct sway_output *output = root->outputs->items[i]; | ||
26 | output_damage_box(output, box); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | void desktop_damage_view(struct sway_view *view) { | ||
31 | desktop_damage_whole_container(view->container); | ||
32 | struct wlr_box box = { | ||
33 | .x = view->container->current.content_x - view->geometry.x, | ||
34 | .y = view->container->current.content_y - view->geometry.y, | ||
35 | .width = view->surface->current.width, | ||
36 | .height = view->surface->current.height, | ||
37 | }; | ||
38 | desktop_damage_box(&box); | ||
39 | } | ||
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index a6ad7166..f3af7aa1 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c | |||
@@ -1,5 +1,5 @@ | |||
1 | #include <stdlib.h> | 1 | #include <stdlib.h> |
2 | #include <wlr/types/wlr_idle.h> | 2 | #include <wlr/types/wlr_idle_notify_v1.h> |
3 | #include "log.h" | 3 | #include "log.h" |
4 | #include "sway/desktop/idle_inhibit_v1.h" | 4 | #include "sway/desktop/idle_inhibit_v1.h" |
5 | #include "sway/input/seat.h" | 5 | #include "sway/input/seat.h" |
@@ -11,7 +11,7 @@ | |||
11 | static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { | 11 | static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { |
12 | wl_list_remove(&inhibitor->link); | 12 | wl_list_remove(&inhibitor->link); |
13 | wl_list_remove(&inhibitor->destroy.link); | 13 | wl_list_remove(&inhibitor->destroy.link); |
14 | sway_idle_inhibit_v1_check_active(inhibitor->manager); | 14 | sway_idle_inhibit_v1_check_active(); |
15 | free(inhibitor); | 15 | free(inhibitor); |
16 | } | 16 | } |
17 | 17 | ||
@@ -34,7 +34,6 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { | |||
34 | return; | 34 | return; |
35 | } | 35 | } |
36 | 36 | ||
37 | inhibitor->manager = manager; | ||
38 | inhibitor->mode = INHIBIT_IDLE_APPLICATION; | 37 | inhibitor->mode = INHIBIT_IDLE_APPLICATION; |
39 | inhibitor->wlr_inhibitor = wlr_inhibitor; | 38 | inhibitor->wlr_inhibitor = wlr_inhibitor; |
40 | wl_list_insert(&manager->inhibitors, &inhibitor->link); | 39 | wl_list_insert(&manager->inhibitors, &inhibitor->link); |
@@ -42,35 +41,36 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { | |||
42 | inhibitor->destroy.notify = handle_destroy; | 41 | inhibitor->destroy.notify = handle_destroy; |
43 | wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); | 42 | wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); |
44 | 43 | ||
45 | sway_idle_inhibit_v1_check_active(manager); | 44 | sway_idle_inhibit_v1_check_active(); |
46 | } | 45 | } |
47 | 46 | ||
48 | void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, | 47 | void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, |
49 | enum sway_idle_inhibit_mode mode) { | 48 | enum sway_idle_inhibit_mode mode) { |
49 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
50 | |||
50 | struct sway_idle_inhibitor_v1 *inhibitor = | 51 | struct sway_idle_inhibitor_v1 *inhibitor = |
51 | calloc(1, sizeof(struct sway_idle_inhibitor_v1)); | 52 | calloc(1, sizeof(struct sway_idle_inhibitor_v1)); |
52 | if (!inhibitor) { | 53 | if (!inhibitor) { |
53 | return; | 54 | return; |
54 | } | 55 | } |
55 | 56 | ||
56 | inhibitor->manager = server.idle_inhibit_manager_v1; | ||
57 | inhibitor->mode = mode; | 57 | inhibitor->mode = mode; |
58 | inhibitor->view = view; | 58 | inhibitor->view = view; |
59 | wl_list_insert(&inhibitor->manager->inhibitors, &inhibitor->link); | 59 | wl_list_insert(&manager->inhibitors, &inhibitor->link); |
60 | 60 | ||
61 | inhibitor->destroy.notify = handle_destroy; | 61 | inhibitor->destroy.notify = handle_destroy; |
62 | wl_signal_add(&view->events.unmap, &inhibitor->destroy); | 62 | wl_signal_add(&view->events.unmap, &inhibitor->destroy); |
63 | 63 | ||
64 | sway_idle_inhibit_v1_check_active(inhibitor->manager); | 64 | sway_idle_inhibit_v1_check_active(); |
65 | } | 65 | } |
66 | 66 | ||
67 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( | 67 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( |
68 | struct sway_view *view) { | 68 | struct sway_view *view) { |
69 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
69 | struct sway_idle_inhibitor_v1 *inhibitor; | 70 | struct sway_idle_inhibitor_v1 *inhibitor; |
70 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, | 71 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { |
71 | link) { | 72 | if (inhibitor->mode != INHIBIT_IDLE_APPLICATION && |
72 | if (inhibitor->view == view && | 73 | inhibitor->view == view) { |
73 | inhibitor->mode != INHIBIT_IDLE_APPLICATION) { | ||
74 | return inhibitor; | 74 | return inhibitor; |
75 | } | 75 | } |
76 | } | 76 | } |
@@ -79,11 +79,11 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( | |||
79 | 79 | ||
80 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( | 80 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( |
81 | struct sway_view *view) { | 81 | struct sway_view *view) { |
82 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
82 | struct sway_idle_inhibitor_v1 *inhibitor; | 83 | struct sway_idle_inhibitor_v1 *inhibitor; |
83 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, | 84 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { |
84 | link) { | 85 | if (inhibitor->mode == INHIBIT_IDLE_APPLICATION && |
85 | if (inhibitor->view == view && | 86 | view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { |
86 | inhibitor->mode == INHIBIT_IDLE_APPLICATION) { | ||
87 | return inhibitor; | 87 | return inhibitor; |
88 | } | 88 | } |
89 | } | 89 | } |
@@ -130,8 +130,8 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { | |||
130 | return false; | 130 | return false; |
131 | } | 131 | } |
132 | 132 | ||
133 | void sway_idle_inhibit_v1_check_active( | 133 | void sway_idle_inhibit_v1_check_active(void) { |
134 | struct sway_idle_inhibit_manager_v1 *manager) { | 134 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; |
135 | struct sway_idle_inhibitor_v1 *inhibitor; | 135 | struct sway_idle_inhibitor_v1 *inhibitor; |
136 | bool inhibited = false; | 136 | bool inhibited = false; |
137 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { | 137 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { |
@@ -139,27 +139,21 @@ void sway_idle_inhibit_v1_check_active( | |||
139 | break; | 139 | break; |
140 | } | 140 | } |
141 | } | 141 | } |
142 | wlr_idle_set_enabled(manager->idle, NULL, !inhibited); | 142 | wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited); |
143 | } | 143 | } |
144 | 144 | ||
145 | struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( | 145 | bool sway_idle_inhibit_manager_v1_init(void) { |
146 | struct wl_display *wl_display, struct wlr_idle *idle) { | 146 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; |
147 | struct sway_idle_inhibit_manager_v1 *manager = | ||
148 | calloc(1, sizeof(struct sway_idle_inhibit_manager_v1)); | ||
149 | if (!manager) { | ||
150 | return NULL; | ||
151 | } | ||
152 | 147 | ||
153 | manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); | 148 | manager->wlr_manager = wlr_idle_inhibit_v1_create(server.wl_display); |
154 | if (!manager->wlr_manager) { | 149 | if (!manager->wlr_manager) { |
155 | free(manager); | 150 | return false; |
156 | return NULL; | ||
157 | } | 151 | } |
158 | manager->idle = idle; | 152 | |
159 | wl_signal_add(&manager->wlr_manager->events.new_inhibitor, | 153 | wl_signal_add(&manager->wlr_manager->events.new_inhibitor, |
160 | &manager->new_idle_inhibitor_v1); | 154 | &manager->new_idle_inhibitor_v1); |
161 | manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; | 155 | manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; |
162 | wl_list_init(&manager->inhibitors); | 156 | wl_list_init(&manager->inhibitors); |
163 | 157 | ||
164 | return manager; | 158 | return true; |
165 | } | 159 | } |
diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c new file mode 100644 index 00000000..28043d19 --- /dev/null +++ b/sway/desktop/launcher.c | |||
@@ -0,0 +1,267 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <string.h> | ||
3 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
4 | #include "sway/input/seat.h" | ||
5 | #include "sway/output.h" | ||
6 | #include "sway/desktop/launcher.h" | ||
7 | #include "sway/tree/node.h" | ||
8 | #include "sway/tree/container.h" | ||
9 | #include "sway/tree/workspace.h" | ||
10 | #include "sway/tree/root.h" | ||
11 | #include "log.h" | ||
12 | |||
13 | /** | ||
14 | * Get the pid of a parent process given the pid of a child process. | ||
15 | * | ||
16 | * Returns the parent pid or NULL if the parent pid cannot be determined. | ||
17 | */ | ||
18 | static pid_t get_parent_pid(pid_t child) { | ||
19 | pid_t parent = -1; | ||
20 | char file_name[100]; | ||
21 | char *buffer = NULL; | ||
22 | const char *sep = " "; | ||
23 | FILE *stat = NULL; | ||
24 | size_t buf_size = 0; | ||
25 | |||
26 | snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child); | ||
27 | |||
28 | if ((stat = fopen(file_name, "r"))) { | ||
29 | if (getline(&buffer, &buf_size, stat) != -1) { | ||
30 | strtok(buffer, sep); // pid | ||
31 | strtok(NULL, sep); // executable name | ||
32 | strtok(NULL, sep); // state | ||
33 | char *token = strtok(NULL, sep); // parent pid | ||
34 | parent = strtol(token, NULL, 10); | ||
35 | } | ||
36 | free(buffer); | ||
37 | fclose(stat); | ||
38 | } | ||
39 | |||
40 | if (parent) { | ||
41 | return (parent == child) ? -1 : parent; | ||
42 | } | ||
43 | |||
44 | return -1; | ||
45 | } | ||
46 | |||
47 | void launcher_ctx_consume(struct launcher_ctx *ctx) { | ||
48 | // The view is now responsible for destroying this ctx | ||
49 | wl_list_remove(&ctx->token_destroy.link); | ||
50 | wl_list_init(&ctx->token_destroy.link); | ||
51 | |||
52 | if (!ctx->activated) { | ||
53 | // An unactivated token hasn't been destroyed yet | ||
54 | wlr_xdg_activation_token_v1_destroy(ctx->token); | ||
55 | } | ||
56 | ctx->token = NULL; | ||
57 | |||
58 | // Prevent additional matches | ||
59 | wl_list_remove(&ctx->link); | ||
60 | wl_list_init(&ctx->link); | ||
61 | } | ||
62 | |||
63 | void launcher_ctx_destroy(struct launcher_ctx *ctx) { | ||
64 | if (ctx == NULL) { | ||
65 | return; | ||
66 | } | ||
67 | wl_list_remove(&ctx->node_destroy.link); | ||
68 | wl_list_remove(&ctx->token_destroy.link); | ||
69 | if (ctx->seat) { | ||
70 | wl_list_remove(&ctx->seat_destroy.link); | ||
71 | } | ||
72 | wl_list_remove(&ctx->link); | ||
73 | wlr_xdg_activation_token_v1_destroy(ctx->token); | ||
74 | free(ctx->fallback_name); | ||
75 | free(ctx); | ||
76 | } | ||
77 | |||
78 | struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) { | ||
79 | if (wl_list_empty(&server.pending_launcher_ctxs)) { | ||
80 | return NULL; | ||
81 | } | ||
82 | |||
83 | struct launcher_ctx *ctx = NULL; | ||
84 | sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); | ||
85 | |||
86 | do { | ||
87 | struct launcher_ctx *_ctx = NULL; | ||
88 | wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) { | ||
89 | if (pid == _ctx->pid) { | ||
90 | ctx = _ctx; | ||
91 | sway_log(SWAY_DEBUG, | ||
92 | "found %s match for pid %d: %s", | ||
93 | node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node)); | ||
94 | break; | ||
95 | } | ||
96 | } | ||
97 | pid = get_parent_pid(pid); | ||
98 | } while (pid > 1); | ||
99 | |||
100 | return ctx; | ||
101 | } | ||
102 | |||
103 | struct sway_workspace *launcher_ctx_get_workspace( | ||
104 | struct launcher_ctx *ctx) { | ||
105 | struct sway_workspace *ws = NULL; | ||
106 | struct sway_output *output = NULL; | ||
107 | |||
108 | switch (ctx->node->type) { | ||
109 | case N_CONTAINER: | ||
110 | // Unimplemented | ||
111 | // TODO: add container matching? | ||
112 | ws = ctx->node->sway_container->pending.workspace; | ||
113 | break; | ||
114 | case N_WORKSPACE: | ||
115 | ws = ctx->node->sway_workspace; | ||
116 | break; | ||
117 | case N_OUTPUT: | ||
118 | output = ctx->node->sway_output; | ||
119 | ws = workspace_by_name(ctx->fallback_name); | ||
120 | if (!ws) { | ||
121 | sway_log(SWAY_DEBUG, | ||
122 | "Creating workspace %s for pid %d because it disappeared", | ||
123 | ctx->fallback_name, ctx->pid); | ||
124 | if (!output->enabled) { | ||
125 | sway_log(SWAY_DEBUG, | ||
126 | "Workspace output %s is disabled, trying another one", | ||
127 | output->wlr_output->name); | ||
128 | output = NULL; | ||
129 | } | ||
130 | ws = workspace_create(output, ctx->fallback_name); | ||
131 | } | ||
132 | break; | ||
133 | case N_ROOT: | ||
134 | ws = workspace_create(NULL, ctx->fallback_name); | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | return ws; | ||
139 | } | ||
140 | |||
141 | static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) { | ||
142 | struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy); | ||
143 | switch (ctx->node->type) { | ||
144 | case N_CONTAINER: | ||
145 | // Unimplemented | ||
146 | break; | ||
147 | case N_WORKSPACE:; | ||
148 | struct sway_workspace *ws = ctx->node->sway_workspace; | ||
149 | wl_list_remove(&ctx->node_destroy.link); | ||
150 | wl_list_init(&ctx->node_destroy.link); | ||
151 | // We want to save this ws name to recreate later, hopefully on the | ||
152 | // same output | ||
153 | free(ctx->fallback_name); | ||
154 | ctx->fallback_name = strdup(ws->name); | ||
155 | if (!ws->output || ws->output->node.destroying) { | ||
156 | // If the output is being destroyed it would be pointless to track | ||
157 | // If the output is being disabled, we'll find out if it's still | ||
158 | // disabled when we try to match it. | ||
159 | ctx->node = &root->node; | ||
160 | break; | ||
161 | } | ||
162 | ctx->node = &ws->output->node; | ||
163 | wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); | ||
164 | break; | ||
165 | case N_OUTPUT: | ||
166 | wl_list_remove(&ctx->node_destroy.link); | ||
167 | wl_list_init(&ctx->node_destroy.link); | ||
168 | // We'll make the ws ctx->name somewhere else | ||
169 | ctx->node = &root->node; | ||
170 | break; | ||
171 | case N_ROOT: | ||
172 | // Unreachable | ||
173 | break; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | static void token_handle_destroy(struct wl_listener *listener, void *data) { | ||
178 | struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy); | ||
179 | ctx->token = NULL; | ||
180 | launcher_ctx_destroy(ctx); | ||
181 | } | ||
182 | |||
183 | struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *token, | ||
184 | struct sway_node *node) { | ||
185 | struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx)); | ||
186 | |||
187 | const char *fallback_name = NULL; | ||
188 | struct sway_workspace *ws = NULL; | ||
189 | switch (node->type) { | ||
190 | case N_CONTAINER: | ||
191 | // Unimplemented | ||
192 | free(ctx); | ||
193 | return NULL; | ||
194 | case N_WORKSPACE: | ||
195 | ws = node->sway_workspace; | ||
196 | fallback_name = ws->name; | ||
197 | break; | ||
198 | case N_OUTPUT:; | ||
199 | struct sway_output *output = node->sway_output; | ||
200 | ws = output_get_active_workspace(output); | ||
201 | fallback_name = ws ? ws->name : NULL; | ||
202 | break; | ||
203 | case N_ROOT: | ||
204 | // Unimplemented | ||
205 | free(ctx); | ||
206 | return NULL; | ||
207 | } | ||
208 | |||
209 | if (!fallback_name) { | ||
210 | // TODO: implement a better fallback. | ||
211 | free(ctx); | ||
212 | return NULL; | ||
213 | } | ||
214 | |||
215 | ctx->fallback_name = strdup(fallback_name); | ||
216 | ctx->token = token; | ||
217 | ctx->node = node; | ||
218 | // Having surface set means that the focus check in wlroots has passed | ||
219 | ctx->had_focused_surface = token->surface != NULL; | ||
220 | |||
221 | ctx->node_destroy.notify = ctx_handle_node_destroy; | ||
222 | wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); | ||
223 | |||
224 | ctx->token_destroy.notify = token_handle_destroy; | ||
225 | wl_signal_add(&token->events.destroy, &ctx->token_destroy); | ||
226 | |||
227 | wl_list_init(&ctx->link); | ||
228 | wl_list_insert(&server.pending_launcher_ctxs, &ctx->link); | ||
229 | |||
230 | token->data = ctx; | ||
231 | return ctx; | ||
232 | } | ||
233 | |||
234 | static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) { | ||
235 | struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy); | ||
236 | ctx->seat = NULL; | ||
237 | wl_list_remove(&ctx->seat_destroy.link); | ||
238 | } | ||
239 | |||
240 | // Creates a context with a new token for the internal launcher | ||
241 | struct launcher_ctx *launcher_ctx_create_internal(void) { | ||
242 | struct sway_seat *seat = input_manager_current_seat(); | ||
243 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | ||
244 | if (!ws) { | ||
245 | sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace."); | ||
246 | return NULL; | ||
247 | } | ||
248 | |||
249 | struct wlr_xdg_activation_token_v1 *token = | ||
250 | wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); | ||
251 | |||
252 | struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node); | ||
253 | if (!ctx) { | ||
254 | wlr_xdg_activation_token_v1_destroy(token); | ||
255 | return NULL; | ||
256 | } | ||
257 | ctx->seat = seat; | ||
258 | ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy; | ||
259 | wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy); | ||
260 | |||
261 | return ctx; | ||
262 | } | ||
263 | |||
264 | const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) { | ||
265 | const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token); | ||
266 | return token; | ||
267 | } | ||
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index cadc702a..4b2584b6 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c | |||
@@ -3,9 +3,12 @@ | |||
3 | #include <string.h> | 3 | #include <string.h> |
4 | #include <wayland-server-core.h> | 4 | #include <wayland-server-core.h> |
5 | #include <wlr/types/wlr_layer_shell_v1.h> | 5 | #include <wlr/types/wlr_layer_shell_v1.h> |
6 | #include <wlr/types/wlr_output_damage.h> | ||
7 | #include <wlr/types/wlr_output.h> | 6 | #include <wlr/types/wlr_output.h> |
7 | #include <wlr/types/wlr_scene.h> | ||
8 | #include <wlr/types/wlr_subcompositor.h> | ||
9 | #include <wlr/types/wlr_xdg_shell.h> | ||
8 | #include "log.h" | 10 | #include "log.h" |
11 | #include "sway/scene_descriptor.h" | ||
9 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
10 | #include "sway/input/cursor.h" | 13 | #include "sway/input/cursor.h" |
11 | #include "sway/input/input-manager.h" | 14 | #include "sway/input/input-manager.h" |
@@ -16,163 +19,55 @@ | |||
16 | #include "sway/tree/arrange.h" | 19 | #include "sway/tree/arrange.h" |
17 | #include "sway/tree/workspace.h" | 20 | #include "sway/tree/workspace.h" |
18 | 21 | ||
19 | static void apply_exclusive(struct wlr_box *usable_area, | 22 | struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( |
20 | uint32_t anchor, int32_t exclusive, | 23 | struct wlr_surface *surface) { |
21 | int32_t margin_top, int32_t margin_right, | 24 | struct wlr_layer_surface_v1 *layer; |
22 | int32_t margin_bottom, int32_t margin_left) { | 25 | do { |
23 | if (exclusive <= 0) { | 26 | if (!surface) { |
24 | return; | 27 | return NULL; |
25 | } | 28 | } |
26 | struct { | 29 | // Topmost layer surface |
27 | uint32_t singular_anchor; | 30 | if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) { |
28 | uint32_t anchor_triplet; | 31 | return layer; |
29 | int *positive_axis; | 32 | } |
30 | int *negative_axis; | 33 | // Layer subsurface |
31 | int margin; | 34 | if (wlr_subsurface_try_from_wlr_surface(surface)) { |
32 | } edges[] = { | 35 | surface = wlr_surface_get_root_surface(surface); |
33 | // Top | 36 | continue; |
34 | { | 37 | } |
35 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, | 38 | |
36 | .anchor_triplet = | 39 | // Layer surface popup |
37 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | 40 | struct wlr_xdg_surface *xdg_surface = NULL; |
38 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | 41 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) && |
39 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, | 42 | xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) { |
40 | .positive_axis = &usable_area->y, | 43 | if (!xdg_surface->popup->parent) { |
41 | .negative_axis = &usable_area->height, | 44 | return NULL; |
42 | .margin = margin_top, | ||
43 | }, | ||
44 | // Bottom | ||
45 | { | ||
46 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
47 | .anchor_triplet = | ||
48 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | ||
49 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
50 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
51 | .positive_axis = NULL, | ||
52 | .negative_axis = &usable_area->height, | ||
53 | .margin = margin_bottom, | ||
54 | }, | ||
55 | // Left | ||
56 | { | ||
57 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, | ||
58 | .anchor_triplet = | ||
59 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | ||
60 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
61 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
62 | .positive_axis = &usable_area->x, | ||
63 | .negative_axis = &usable_area->width, | ||
64 | .margin = margin_left, | ||
65 | }, | ||
66 | // Right | ||
67 | { | ||
68 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, | ||
69 | .anchor_triplet = | ||
70 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
71 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
72 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
73 | .positive_axis = NULL, | ||
74 | .negative_axis = &usable_area->width, | ||
75 | .margin = margin_right, | ||
76 | }, | ||
77 | }; | ||
78 | for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { | ||
79 | if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) | ||
80 | && exclusive + edges[i].margin > 0) { | ||
81 | if (edges[i].positive_axis) { | ||
82 | *edges[i].positive_axis += exclusive + edges[i].margin; | ||
83 | } | ||
84 | if (edges[i].negative_axis) { | ||
85 | *edges[i].negative_axis -= exclusive + edges[i].margin; | ||
86 | } | 45 | } |
87 | break; | 46 | surface = wlr_surface_get_root_surface(xdg_surface->popup->parent); |
47 | continue; | ||
88 | } | 48 | } |
89 | } | 49 | |
50 | // Return early if the surface is not a layer/xdg_popup/sub surface | ||
51 | return NULL; | ||
52 | } while (true); | ||
90 | } | 53 | } |
91 | 54 | ||
92 | static void arrange_layer(struct sway_output *output, struct wl_list *list, | 55 | static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area, |
93 | struct wlr_box *usable_area, bool exclusive) { | 56 | struct wlr_box *usable_area, struct wlr_scene_tree *tree) { |
94 | struct sway_layer_surface *sway_layer; | 57 | struct wlr_scene_node *node; |
95 | struct wlr_box full_area = { 0 }; | 58 | wl_list_for_each(node, &tree->children, link) { |
96 | wlr_output_effective_resolution(output->wlr_output, | 59 | struct sway_layer_surface *surface = scene_descriptor_try_get(node, |
97 | &full_area.width, &full_area.height); | 60 | SWAY_SCENE_DESC_LAYER_SHELL); |
98 | wl_list_for_each(sway_layer, list, link) { | 61 | // surface could be null during destruction |
99 | struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; | 62 | if (!surface) { |
100 | struct wlr_layer_surface_v1_state *state = &layer->current; | ||
101 | if (exclusive != (state->exclusive_zone > 0)) { | ||
102 | continue; | 63 | continue; |
103 | } | 64 | } |
104 | struct wlr_box bounds; | 65 | |
105 | if (state->exclusive_zone == -1) { | 66 | if (!surface->scene->layer_surface->initialized) { |
106 | bounds = full_area; | ||
107 | } else { | ||
108 | bounds = *usable_area; | ||
109 | } | ||
110 | struct wlr_box box = { | ||
111 | .width = state->desired_width, | ||
112 | .height = state->desired_height | ||
113 | }; | ||
114 | // Horizontal axis | ||
115 | const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ||
116 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; | ||
117 | if (box.width == 0) { | ||
118 | box.x = bounds.x; | ||
119 | } else if ((state->anchor & both_horiz) == both_horiz) { | ||
120 | box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); | ||
121 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
122 | box.x = bounds.x; | ||
123 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
124 | box.x = bounds.x + (bounds.width - box.width); | ||
125 | } else { | ||
126 | box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); | ||
127 | } | ||
128 | // Vertical axis | ||
129 | const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ||
130 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; | ||
131 | if (box.height == 0) { | ||
132 | box.y = bounds.y; | ||
133 | } else if ((state->anchor & both_vert) == both_vert) { | ||
134 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | ||
135 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
136 | box.y = bounds.y; | ||
137 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
138 | box.y = bounds.y + (bounds.height - box.height); | ||
139 | } else { | ||
140 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | ||
141 | } | ||
142 | // Margin | ||
143 | if (box.width == 0) { | ||
144 | box.x += state->margin.left; | ||
145 | box.width = bounds.width - | ||
146 | (state->margin.left + state->margin.right); | ||
147 | } else if ((state->anchor & both_horiz) == both_horiz) { | ||
148 | // don't apply margins | ||
149 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
150 | box.x += state->margin.left; | ||
151 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
152 | box.x -= state->margin.right; | ||
153 | } | ||
154 | if (box.height == 0) { | ||
155 | box.y += state->margin.top; | ||
156 | box.height = bounds.height - | ||
157 | (state->margin.top + state->margin.bottom); | ||
158 | } else if ((state->anchor & both_vert) == both_vert) { | ||
159 | // don't apply margins | ||
160 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
161 | box.y += state->margin.top; | ||
162 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
163 | box.y -= state->margin.bottom; | ||
164 | } | ||
165 | if (box.width < 0 || box.height < 0) { | ||
166 | // TODO: Bubble up a protocol error? | ||
167 | wlr_layer_surface_v1_close(layer); | ||
168 | continue; | 67 | continue; |
169 | } | 68 | } |
170 | // Apply | 69 | |
171 | sway_layer->geo = box; | 70 | wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); |
172 | apply_exclusive(usable_area, state->anchor, state->exclusive_zone, | ||
173 | state->margin.top, state->margin.right, | ||
174 | state->margin.bottom, state->margin.left); | ||
175 | wlr_layer_surface_v1_configure(layer, box.width, box.height); | ||
176 | } | 71 | } |
177 | } | 72 | } |
178 | 73 | ||
@@ -180,81 +75,94 @@ void arrange_layers(struct sway_output *output) { | |||
180 | struct wlr_box usable_area = { 0 }; | 75 | struct wlr_box usable_area = { 0 }; |
181 | wlr_output_effective_resolution(output->wlr_output, | 76 | wlr_output_effective_resolution(output->wlr_output, |
182 | &usable_area.width, &usable_area.height); | 77 | &usable_area.width, &usable_area.height); |
78 | const struct wlr_box full_area = usable_area; | ||
79 | |||
80 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_background); | ||
81 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom); | ||
82 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_top); | ||
83 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay); | ||
183 | 84 | ||
184 | // Arrange exclusive surfaces from top->bottom | 85 | if (!wlr_box_equal(&usable_area, &output->usable_area)) { |
185 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
186 | &usable_area, true); | ||
187 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
188 | &usable_area, true); | ||
189 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
190 | &usable_area, true); | ||
191 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
192 | &usable_area, true); | ||
193 | |||
194 | if (memcmp(&usable_area, &output->usable_area, | ||
195 | sizeof(struct wlr_box)) != 0) { | ||
196 | sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); | 86 | sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); |
197 | memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); | 87 | output->usable_area = usable_area; |
198 | arrange_output(output); | 88 | arrange_output(output); |
89 | } else { | ||
90 | arrange_popups(root->layers.popup); | ||
199 | } | 91 | } |
92 | } | ||
200 | 93 | ||
201 | // Arrange non-exclusive surfaces from top->bottom | 94 | static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, |
202 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 95 | enum zwlr_layer_shell_v1_layer type) { |
203 | &usable_area, false); | 96 | switch (type) { |
204 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | 97 | case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: |
205 | &usable_area, false); | 98 | return output->layers.shell_background; |
206 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | 99 | case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: |
207 | &usable_area, false); | 100 | return output->layers.shell_bottom; |
208 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | 101 | case ZWLR_LAYER_SHELL_V1_LAYER_TOP: |
209 | &usable_area, false); | 102 | return output->layers.shell_top; |
210 | 103 | case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: | |
211 | // Find topmost keyboard interactive layer, if such a layer exists | 104 | return output->layers.shell_overlay; |
212 | uint32_t layers_above_shell[] = { | ||
213 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, | ||
214 | ZWLR_LAYER_SHELL_V1_LAYER_TOP, | ||
215 | }; | ||
216 | size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); | ||
217 | struct sway_layer_surface *layer, *topmost = NULL; | ||
218 | for (size_t i = 0; i < nlayers; ++i) { | ||
219 | wl_list_for_each_reverse(layer, | ||
220 | &output->layers[layers_above_shell[i]], link) { | ||
221 | if (layer->layer_surface->current.keyboard_interactive && | ||
222 | layer->layer_surface->mapped) { | ||
223 | topmost = layer; | ||
224 | break; | ||
225 | } | ||
226 | } | ||
227 | if (topmost != NULL) { | ||
228 | break; | ||
229 | } | ||
230 | } | 105 | } |
231 | 106 | ||
232 | struct sway_seat *seat; | 107 | sway_assert(false, "unreachable"); |
233 | wl_list_for_each(seat, &server.input->seats, link) { | 108 | return NULL; |
234 | if (topmost != NULL) { | 109 | } |
235 | seat_set_focus_layer(seat, topmost->layer_surface); | 110 | |
236 | } else if (seat->focused_layer && | 111 | static struct sway_layer_surface *sway_layer_surface_create( |
237 | !seat->focused_layer->current.keyboard_interactive) { | 112 | struct wlr_scene_layer_surface_v1 *scene) { |
238 | seat_set_focus_layer(seat, NULL); | 113 | struct sway_layer_surface *surface = calloc(1, sizeof(*surface)); |
239 | } | 114 | if (!surface) { |
115 | sway_log(SWAY_ERROR, "Could not allocate a scene_layer surface"); | ||
116 | return NULL; | ||
240 | } | 117 | } |
118 | |||
119 | struct wlr_scene_tree *popups = wlr_scene_tree_create(root->layers.popup); | ||
120 | if (!popups) { | ||
121 | sway_log(SWAY_ERROR, "Could not allocate a scene_layer popup node"); | ||
122 | free(surface); | ||
123 | return NULL; | ||
124 | } | ||
125 | |||
126 | surface->desc.relative = &scene->tree->node; | ||
127 | |||
128 | if (!scene_descriptor_assign(&popups->node, | ||
129 | SWAY_SCENE_DESC_POPUP, &surface->desc)) { | ||
130 | sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); | ||
131 | wlr_scene_node_destroy(&popups->node); | ||
132 | free(surface); | ||
133 | return NULL; | ||
134 | } | ||
135 | |||
136 | surface->tree = scene->tree; | ||
137 | surface->scene = scene; | ||
138 | surface->layer_surface = scene->layer_surface; | ||
139 | surface->popups = popups; | ||
140 | surface->layer_surface->data = surface; | ||
141 | |||
142 | return surface; | ||
241 | } | 143 | } |
242 | 144 | ||
243 | static struct sway_layer_surface *find_mapped_layer_by_client( | 145 | static struct sway_layer_surface *find_mapped_layer_by_client( |
244 | struct wl_client *client, struct wlr_output *ignore_output) { | 146 | struct wl_client *client, struct sway_output *ignore_output) { |
245 | for (int i = 0; i < root->outputs->length; ++i) { | 147 | for (int i = 0; i < root->outputs->length; ++i) { |
246 | struct sway_output *output = root->outputs->items[i]; | 148 | struct sway_output *output = root->outputs->items[i]; |
247 | if (output->wlr_output == ignore_output) { | 149 | if (output == ignore_output) { |
248 | continue; | 150 | continue; |
249 | } | 151 | } |
250 | // For now we'll only check the overlay layer | 152 | // For now we'll only check the overlay layer |
251 | struct sway_layer_surface *lsurface; | 153 | struct wlr_scene_node *node; |
252 | wl_list_for_each(lsurface, | 154 | wl_list_for_each (node, &output->layers.shell_overlay->children, link) { |
253 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | 155 | struct sway_layer_surface *surface = scene_descriptor_try_get(node, |
254 | struct wl_resource *resource = lsurface->layer_surface->resource; | 156 | SWAY_SCENE_DESC_LAYER_SHELL); |
157 | if (!surface) { | ||
158 | continue; | ||
159 | } | ||
160 | |||
161 | struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; | ||
162 | struct wl_resource *resource = layer_surface->resource; | ||
255 | if (wl_resource_get_client(resource) == client | 163 | if (wl_resource_get_client(resource) == client |
256 | && lsurface->layer_surface->mapped) { | 164 | && layer_surface->surface->mapped) { |
257 | return lsurface; | 165 | return surface; |
258 | } | 166 | } |
259 | } | 167 | } |
260 | } | 168 | } |
@@ -262,280 +170,142 @@ static struct sway_layer_surface *find_mapped_layer_by_client( | |||
262 | } | 170 | } |
263 | 171 | ||
264 | static void handle_output_destroy(struct wl_listener *listener, void *data) { | 172 | static void handle_output_destroy(struct wl_listener *listener, void *data) { |
265 | struct sway_layer_surface *sway_layer = | 173 | struct sway_layer_surface *layer = |
266 | wl_container_of(listener, sway_layer, output_destroy); | 174 | wl_container_of(listener, layer, output_destroy); |
267 | // Determine if this layer is being used by an exclusive client. If it is, | ||
268 | // try and find another layer owned by this client to pass focus to. | ||
269 | struct sway_seat *seat = input_manager_get_default_seat(); | ||
270 | struct wl_client *client = | ||
271 | wl_resource_get_client(sway_layer->layer_surface->resource); | ||
272 | bool set_focus = seat->exclusive_client == client; | ||
273 | |||
274 | wl_list_remove(&sway_layer->output_destroy.link); | ||
275 | wl_list_remove(&sway_layer->link); | ||
276 | wl_list_init(&sway_layer->link); | ||
277 | |||
278 | if (set_focus) { | ||
279 | struct sway_layer_surface *layer = | ||
280 | find_mapped_layer_by_client(client, sway_layer->layer_surface->output); | ||
281 | if (layer) { | ||
282 | seat_set_focus_layer(seat, layer->layer_surface); | ||
283 | } | ||
284 | } | ||
285 | 175 | ||
286 | sway_layer->layer_surface->output = NULL; | 176 | layer->output = NULL; |
287 | wlr_layer_surface_v1_close(sway_layer->layer_surface); | 177 | wlr_scene_node_destroy(&layer->scene->tree->node); |
288 | } | 178 | } |
289 | 179 | ||
290 | static void handle_surface_commit(struct wl_listener *listener, void *data) { | 180 | static void handle_node_destroy(struct wl_listener *listener, void *data) { |
291 | struct sway_layer_surface *layer = | 181 | struct sway_layer_surface *layer = |
292 | wl_container_of(listener, layer, surface_commit); | 182 | wl_container_of(listener, layer, node_destroy); |
293 | struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; | ||
294 | struct wlr_output *wlr_output = layer_surface->output; | ||
295 | if (wlr_output == NULL) { | ||
296 | return; | ||
297 | } | ||
298 | 183 | ||
299 | struct sway_output *output = wlr_output->data; | 184 | // destroy the scene descriptor straight away if it exists, otherwise |
300 | struct wlr_box old_geo = layer->geo; | 185 | // we will try to reflow still considering the destroyed node. |
301 | arrange_layers(output); | 186 | scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL); |
302 | |||
303 | bool geo_changed = | ||
304 | memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0; | ||
305 | bool layer_changed = layer->layer != layer_surface->current.layer; | ||
306 | if (layer_changed) { | ||
307 | wl_list_remove(&layer->link); | ||
308 | wl_list_insert(&output->layers[layer_surface->current.layer], | ||
309 | &layer->link); | ||
310 | layer->layer = layer_surface->current.layer; | ||
311 | } | ||
312 | if (geo_changed || layer_changed) { | ||
313 | output_damage_surface(output, old_geo.x, old_geo.y, | ||
314 | layer_surface->surface, true); | ||
315 | output_damage_surface(output, layer->geo.x, layer->geo.y, | ||
316 | layer_surface->surface, true); | ||
317 | } else { | ||
318 | output_damage_surface(output, layer->geo.x, layer->geo.y, | ||
319 | layer_surface->surface, false); | ||
320 | } | ||
321 | |||
322 | transaction_commit_dirty(); | ||
323 | } | ||
324 | 187 | ||
325 | static void unmap(struct sway_layer_surface *sway_layer) { | 188 | // Determine if this layer is being used by an exclusive client. If it is, |
326 | struct sway_seat *seat; | 189 | // try and find another layer owned by this client to pass focus to. |
327 | wl_list_for_each(seat, &server.input->seats, link) { | 190 | struct sway_seat *seat = input_manager_get_default_seat(); |
328 | if (seat->focused_layer == sway_layer->layer_surface) { | 191 | struct wl_client *client = |
329 | seat_set_focus_layer(seat, NULL); | 192 | wl_resource_get_client(layer->layer_surface->resource); |
193 | if (!server.session_lock.lock) { | ||
194 | struct sway_layer_surface *consider_layer = | ||
195 | find_mapped_layer_by_client(client, layer->output); | ||
196 | if (consider_layer) { | ||
197 | seat_set_focus_layer(seat, consider_layer->layer_surface); | ||
330 | } | 198 | } |
331 | } | 199 | } |
332 | 200 | ||
333 | cursor_rebase_all(); | 201 | if (layer->output) { |
334 | 202 | arrange_layers(layer->output); | |
335 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | 203 | transaction_commit_dirty(); |
336 | if (wlr_output == NULL) { | ||
337 | return; | ||
338 | } | ||
339 | struct sway_output *output = wlr_output->data; | ||
340 | if (output == NULL) { | ||
341 | return; | ||
342 | } | ||
343 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | ||
344 | sway_layer->layer_surface->surface, true); | ||
345 | } | ||
346 | |||
347 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
348 | struct sway_layer_surface *sway_layer = | ||
349 | wl_container_of(listener, sway_layer, destroy); | ||
350 | sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", | ||
351 | sway_layer->layer_surface->namespace); | ||
352 | if (sway_layer->layer_surface->mapped) { | ||
353 | unmap(sway_layer); | ||
354 | } | 204 | } |
355 | wl_list_remove(&sway_layer->link); | ||
356 | wl_list_remove(&sway_layer->destroy.link); | ||
357 | wl_list_remove(&sway_layer->map.link); | ||
358 | wl_list_remove(&sway_layer->unmap.link); | ||
359 | wl_list_remove(&sway_layer->surface_commit.link); | ||
360 | wl_list_remove(&sway_layer->new_popup.link); | ||
361 | wl_list_remove(&sway_layer->new_subsurface.link); | ||
362 | if (sway_layer->layer_surface->output != NULL) { | ||
363 | struct sway_output *output = sway_layer->layer_surface->output->data; | ||
364 | if (output != NULL) { | ||
365 | arrange_layers(output); | ||
366 | transaction_commit_dirty(); | ||
367 | } | ||
368 | wl_list_remove(&sway_layer->output_destroy.link); | ||
369 | sway_layer->layer_surface->output = NULL; | ||
370 | } | ||
371 | free(sway_layer); | ||
372 | } | ||
373 | 205 | ||
374 | static void handle_map(struct wl_listener *listener, void *data) { | 206 | wlr_scene_node_destroy(&layer->popups->node); |
375 | struct sway_layer_surface *sway_layer = wl_container_of(listener, | ||
376 | sway_layer, map); | ||
377 | struct sway_output *output = sway_layer->layer_surface->output->data; | ||
378 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | ||
379 | sway_layer->layer_surface->surface, true); | ||
380 | wlr_surface_send_enter(sway_layer->layer_surface->surface, | ||
381 | sway_layer->layer_surface->output); | ||
382 | cursor_rebase_all(); | ||
383 | } | ||
384 | 207 | ||
385 | static void handle_unmap(struct wl_listener *listener, void *data) { | 208 | wl_list_remove(&layer->map.link); |
386 | struct sway_layer_surface *sway_layer = wl_container_of( | 209 | wl_list_remove(&layer->unmap.link); |
387 | listener, sway_layer, unmap); | 210 | wl_list_remove(&layer->surface_commit.link); |
388 | unmap(sway_layer); | 211 | wl_list_remove(&layer->node_destroy.link); |
389 | } | 212 | wl_list_remove(&layer->output_destroy.link); |
390 | 213 | ||
391 | static void subsurface_damage(struct sway_layer_subsurface *subsurface, | 214 | layer->layer_surface->data = NULL; |
392 | bool whole) { | ||
393 | struct sway_layer_surface *layer = subsurface->layer_surface; | ||
394 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
395 | if (!wlr_output) { | ||
396 | return; | ||
397 | } | ||
398 | struct sway_output *output = wlr_output->data; | ||
399 | int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; | ||
400 | int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; | ||
401 | output_damage_surface( | ||
402 | output, ox, oy, subsurface->wlr_subsurface->surface, whole); | ||
403 | } | ||
404 | |||
405 | static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { | ||
406 | struct sway_layer_subsurface *subsurface = | ||
407 | wl_container_of(listener, subsurface, unmap); | ||
408 | subsurface_damage(subsurface, true); | ||
409 | } | ||
410 | 215 | ||
411 | static void subsurface_handle_map(struct wl_listener *listener, void *data) { | 216 | free(layer); |
412 | struct sway_layer_subsurface *subsurface = | ||
413 | wl_container_of(listener, subsurface, map); | ||
414 | subsurface_damage(subsurface, true); | ||
415 | } | 217 | } |
416 | 218 | ||
417 | static void subsurface_handle_commit(struct wl_listener *listener, void *data) { | 219 | static void handle_surface_commit(struct wl_listener *listener, void *data) { |
418 | struct sway_layer_subsurface *subsurface = | 220 | struct sway_layer_surface *surface = |
419 | wl_container_of(listener, subsurface, commit); | 221 | wl_container_of(listener, surface, surface_commit); |
420 | subsurface_damage(subsurface, false); | ||
421 | } | ||
422 | |||
423 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
424 | void *data) { | ||
425 | struct sway_layer_subsurface *subsurface = | ||
426 | wl_container_of(listener, subsurface, destroy); | ||
427 | |||
428 | wl_list_remove(&subsurface->map.link); | ||
429 | wl_list_remove(&subsurface->unmap.link); | ||
430 | wl_list_remove(&subsurface->destroy.link); | ||
431 | wl_list_remove(&subsurface->commit.link); | ||
432 | free(subsurface); | ||
433 | } | ||
434 | 222 | ||
435 | static struct sway_layer_subsurface *create_subsurface( | 223 | struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; |
436 | struct wlr_subsurface *wlr_subsurface, | 224 | if (!layer_surface->initialized) { |
437 | struct sway_layer_surface *layer_surface) { | 225 | return; |
438 | struct sway_layer_subsurface *subsurface = | ||
439 | calloc(1, sizeof(struct sway_layer_subsurface)); | ||
440 | if (subsurface == NULL) { | ||
441 | return NULL; | ||
442 | } | 226 | } |
443 | 227 | ||
444 | subsurface->wlr_subsurface = wlr_subsurface; | 228 | uint32_t committed = layer_surface->current.committed; |
445 | subsurface->layer_surface = layer_surface; | 229 | if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { |
446 | 230 | enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; | |
447 | subsurface->map.notify = subsurface_handle_map; | 231 | struct wlr_scene_tree *output_layer = sway_layer_get_scene( |
448 | wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); | 232 | surface->output, layer_type); |
449 | subsurface->unmap.notify = subsurface_handle_unmap; | 233 | wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); |
450 | wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); | 234 | } |
451 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
452 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
453 | subsurface->commit.notify = subsurface_handle_commit; | ||
454 | wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit); | ||
455 | |||
456 | return subsurface; | ||
457 | } | ||
458 | |||
459 | static void handle_new_subsurface(struct wl_listener *listener, void *data) { | ||
460 | struct sway_layer_surface *sway_layer_surface = | ||
461 | wl_container_of(listener, sway_layer_surface, new_subsurface); | ||
462 | struct wlr_subsurface *wlr_subsurface = data; | ||
463 | create_subsurface(wlr_subsurface, sway_layer_surface); | ||
464 | } | ||
465 | |||
466 | 235 | ||
467 | static struct sway_layer_surface *popup_get_layer( | 236 | if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) { |
468 | struct sway_layer_popup *popup) { | 237 | surface->mapped = layer_surface->surface->mapped; |
469 | while (popup->parent_type == LAYER_PARENT_POPUP) { | 238 | arrange_layers(surface->output); |
470 | popup = popup->parent_popup; | 239 | transaction_commit_dirty(); |
471 | } | 240 | } |
472 | return popup->parent_layer; | ||
473 | } | 241 | } |
474 | 242 | ||
475 | static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { | 243 | static void handle_map(struct wl_listener *listener, void *data) { |
476 | struct wlr_xdg_popup *popup = layer_popup->wlr_popup; | 244 | struct sway_layer_surface *surface = wl_container_of(listener, |
477 | struct wlr_surface *surface = popup->base->surface; | 245 | surface, map); |
478 | int popup_sx = popup->geometry.x - popup->base->geometry.x; | 246 | |
479 | int popup_sy = popup->geometry.y - popup->base->geometry.y; | 247 | struct wlr_layer_surface_v1 *layer_surface = |
480 | int ox = popup_sx, oy = popup_sy; | 248 | surface->scene->layer_surface; |
481 | struct sway_layer_surface *layer; | 249 | |
482 | while (true) { | 250 | // focus on new surface |
483 | if (layer_popup->parent_type == LAYER_PARENT_POPUP) { | 251 | if (layer_surface->current.keyboard_interactive && |
484 | layer_popup = layer_popup->parent_popup; | 252 | (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || |
485 | ox += layer_popup->wlr_popup->geometry.x; | 253 | layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { |
486 | oy += layer_popup->wlr_popup->geometry.y; | 254 | struct sway_seat *seat; |
487 | } else { | 255 | wl_list_for_each(seat, &server.input->seats, link) { |
488 | layer = layer_popup->parent_layer; | 256 | // but only if the currently focused layer has a lower precedence |
489 | ox += layer->geo.x; | 257 | if (!seat->focused_layer || |
490 | oy += layer->geo.y; | 258 | seat->focused_layer->current.layer >= layer_surface->current.layer) { |
491 | break; | 259 | seat_set_focus_layer(seat, layer_surface); |
260 | } | ||
492 | } | 261 | } |
262 | arrange_layers(surface->output); | ||
493 | } | 263 | } |
494 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
495 | struct sway_output *output = wlr_output->data; | ||
496 | output_damage_surface(output, ox, oy, surface, whole); | ||
497 | } | ||
498 | 264 | ||
499 | static void popup_handle_map(struct wl_listener *listener, void *data) { | 265 | cursor_rebase_all(); |
500 | struct sway_layer_popup *popup = wl_container_of(listener, popup, map); | ||
501 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
502 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
503 | wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); | ||
504 | popup_damage(popup, true); | ||
505 | } | 266 | } |
506 | 267 | ||
507 | static void popup_handle_unmap(struct wl_listener *listener, void *data) { | 268 | static void handle_unmap(struct wl_listener *listener, void *data) { |
508 | struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); | 269 | struct sway_layer_surface *surface = wl_container_of( |
509 | popup_damage(popup, true); | 270 | listener, surface, unmap); |
510 | } | 271 | struct sway_seat *seat; |
272 | wl_list_for_each(seat, &server.input->seats, link) { | ||
273 | if (seat->focused_layer == surface->layer_surface) { | ||
274 | seat_set_focus_layer(seat, NULL); | ||
275 | } | ||
276 | } | ||
511 | 277 | ||
512 | static void popup_handle_commit(struct wl_listener *listener, void *data) { | 278 | cursor_rebase_all(); |
513 | struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); | ||
514 | popup_damage(popup, false); | ||
515 | } | 279 | } |
516 | 280 | ||
517 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 281 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
518 | struct sway_layer_popup *popup = | 282 | struct sway_layer_popup *popup = |
519 | wl_container_of(listener, popup, destroy); | 283 | wl_container_of(listener, popup, destroy); |
520 | 284 | ||
521 | wl_list_remove(&popup->map.link); | ||
522 | wl_list_remove(&popup->unmap.link); | ||
523 | wl_list_remove(&popup->destroy.link); | 285 | wl_list_remove(&popup->destroy.link); |
286 | wl_list_remove(&popup->new_popup.link); | ||
524 | wl_list_remove(&popup->commit.link); | 287 | wl_list_remove(&popup->commit.link); |
525 | free(popup); | 288 | free(popup); |
526 | } | 289 | } |
527 | 290 | ||
528 | static void popup_unconstrain(struct sway_layer_popup *popup) { | 291 | static void popup_unconstrain(struct sway_layer_popup *popup) { |
529 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
530 | struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; | 292 | struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; |
293 | struct sway_output *output = popup->toplevel->output; | ||
531 | 294 | ||
532 | struct sway_output *output = layer->layer_surface->output->data; | 295 | // if a client tries to create a popup while we are in the process of destroying |
296 | // its output, don't crash. | ||
297 | if (!output) { | ||
298 | return; | ||
299 | } | ||
300 | |||
301 | int lx, ly; | ||
302 | wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly); | ||
533 | 303 | ||
534 | // the output box expressed in the coordinate system of the toplevel parent | 304 | // the output box expressed in the coordinate system of the toplevel parent |
535 | // of the popup | 305 | // of the popup |
536 | struct wlr_box output_toplevel_sx_box = { | 306 | struct wlr_box output_toplevel_sx_box = { |
537 | .x = -layer->geo.x, | 307 | .x = output->lx - lx, |
538 | .y = -layer->geo.y, | 308 | .y = output->ly - ly, |
539 | .width = output->width, | 309 | .width = output->width, |
540 | .height = output->height, | 310 | .height = output->height, |
541 | }; | 311 | }; |
@@ -543,32 +313,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { | |||
543 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | 313 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); |
544 | } | 314 | } |
545 | 315 | ||
316 | static void popup_handle_commit(struct wl_listener *listener, void *data) { | ||
317 | struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); | ||
318 | if (popup->wlr_popup->base->initial_commit) { | ||
319 | popup_unconstrain(popup); | ||
320 | } | ||
321 | } | ||
322 | |||
546 | static void popup_handle_new_popup(struct wl_listener *listener, void *data); | 323 | static void popup_handle_new_popup(struct wl_listener *listener, void *data); |
547 | 324 | ||
548 | static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, | 325 | static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, |
549 | enum layer_parent parent_type, void *parent) { | 326 | struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) { |
550 | struct sway_layer_popup *popup = | 327 | struct sway_layer_popup *popup = calloc(1, sizeof(*popup)); |
551 | calloc(1, sizeof(struct sway_layer_popup)); | ||
552 | if (popup == NULL) { | 328 | if (popup == NULL) { |
553 | return NULL; | 329 | return NULL; |
554 | } | 330 | } |
555 | 331 | ||
332 | popup->toplevel = toplevel; | ||
556 | popup->wlr_popup = wlr_popup; | 333 | popup->wlr_popup = wlr_popup; |
557 | popup->parent_type = parent_type; | 334 | popup->scene = wlr_scene_xdg_surface_create(parent, |
558 | popup->parent_layer = parent; | 335 | wlr_popup->base); |
336 | |||
337 | if (!popup->scene) { | ||
338 | free(popup); | ||
339 | return NULL; | ||
340 | } | ||
559 | 341 | ||
560 | popup->map.notify = popup_handle_map; | ||
561 | wl_signal_add(&wlr_popup->base->events.map, &popup->map); | ||
562 | popup->unmap.notify = popup_handle_unmap; | ||
563 | wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); | ||
564 | popup->destroy.notify = popup_handle_destroy; | 342 | popup->destroy.notify = popup_handle_destroy; |
565 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); | 343 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); |
566 | popup->commit.notify = popup_handle_commit; | ||
567 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); | ||
568 | popup->new_popup.notify = popup_handle_new_popup; | 344 | popup->new_popup.notify = popup_handle_new_popup; |
569 | wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); | 345 | wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); |
570 | 346 | popup->commit.notify = popup_handle_commit; | |
571 | popup_unconstrain(popup); | 347 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); |
572 | 348 | ||
573 | return popup; | 349 | return popup; |
574 | } | 350 | } |
@@ -577,19 +353,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | |||
577 | struct sway_layer_popup *sway_layer_popup = | 353 | struct sway_layer_popup *sway_layer_popup = |
578 | wl_container_of(listener, sway_layer_popup, new_popup); | 354 | wl_container_of(listener, sway_layer_popup, new_popup); |
579 | struct wlr_xdg_popup *wlr_popup = data; | 355 | struct wlr_xdg_popup *wlr_popup = data; |
580 | create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); | 356 | create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene); |
581 | } | 357 | } |
582 | 358 | ||
583 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 359 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
584 | struct sway_layer_surface *sway_layer_surface = | 360 | struct sway_layer_surface *sway_layer_surface = |
585 | wl_container_of(listener, sway_layer_surface, new_popup); | 361 | wl_container_of(listener, sway_layer_surface, new_popup); |
586 | struct wlr_xdg_popup *wlr_popup = data; | 362 | struct wlr_xdg_popup *wlr_popup = data; |
587 | create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); | 363 | create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups); |
588 | } | ||
589 | |||
590 | struct sway_layer_surface *layer_from_wlr_layer_surface_v1( | ||
591 | struct wlr_layer_surface_v1 *layer_surface) { | ||
592 | return layer_surface->data; | ||
593 | } | 364 | } |
594 | 365 | ||
595 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | 366 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { |
@@ -597,14 +368,14 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
597 | sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 | 368 | sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 |
598 | " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", | 369 | " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", |
599 | layer_surface->namespace, | 370 | layer_surface->namespace, |
600 | layer_surface->client_pending.layer, | 371 | layer_surface->pending.layer, |
601 | layer_surface->client_pending.anchor, | 372 | layer_surface->pending.anchor, |
602 | layer_surface->client_pending.desired_width, | 373 | layer_surface->pending.desired_width, |
603 | layer_surface->client_pending.desired_height, | 374 | layer_surface->pending.desired_height, |
604 | layer_surface->client_pending.margin.top, | 375 | layer_surface->pending.margin.top, |
605 | layer_surface->client_pending.margin.right, | 376 | layer_surface->pending.margin.right, |
606 | layer_surface->client_pending.margin.bottom, | 377 | layer_surface->pending.margin.bottom, |
607 | layer_surface->client_pending.margin.left); | 378 | layer_surface->pending.margin.left); |
608 | 379 | ||
609 | if (!layer_surface->output) { | 380 | if (!layer_surface->output) { |
610 | // Assign last active output | 381 | // Assign last active output |
@@ -616,12 +387,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
616 | output = ws->output; | 387 | output = ws->output; |
617 | } | 388 | } |
618 | } | 389 | } |
619 | if (!output || output == root->noop_output) { | 390 | if (!output || output == root->fallback_output) { |
620 | if (!root->outputs->length) { | 391 | if (!root->outputs->length) { |
621 | sway_log(SWAY_ERROR, | 392 | sway_log(SWAY_ERROR, |
622 | "no output to auto-assign layer surface '%s' to", | 393 | "no output to auto-assign layer surface '%s' to", |
623 | layer_surface->namespace); | 394 | layer_surface->namespace); |
624 | wlr_layer_surface_v1_close(layer_surface); | 395 | wlr_layer_surface_v1_destroy(layer_surface); |
625 | return; | 396 | return; |
626 | } | 397 | } |
627 | output = root->outputs->items[0]; | 398 | output = root->outputs->items[0]; |
@@ -629,42 +400,51 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
629 | layer_surface->output = output->wlr_output; | 400 | layer_surface->output = output->wlr_output; |
630 | } | 401 | } |
631 | 402 | ||
632 | struct sway_layer_surface *sway_layer = | 403 | struct sway_output *output = layer_surface->output->data; |
633 | calloc(1, sizeof(struct sway_layer_surface)); | 404 | |
634 | if (!sway_layer) { | 405 | enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer; |
406 | struct wlr_scene_tree *output_layer = sway_layer_get_scene( | ||
407 | output, layer_type); | ||
408 | struct wlr_scene_layer_surface_v1 *scene_surface = | ||
409 | wlr_scene_layer_surface_v1_create(output_layer, layer_surface); | ||
410 | if (!scene_surface) { | ||
411 | sway_log(SWAY_ERROR, "Could not allocate a layer_surface_v1"); | ||
635 | return; | 412 | return; |
636 | } | 413 | } |
637 | 414 | ||
638 | sway_layer->surface_commit.notify = handle_surface_commit; | 415 | struct sway_layer_surface *surface = |
639 | wl_signal_add(&layer_surface->surface->events.commit, | 416 | sway_layer_surface_create(scene_surface); |
640 | &sway_layer->surface_commit); | 417 | if (!surface) { |
641 | 418 | wlr_layer_surface_v1_destroy(layer_surface); | |
642 | sway_layer->destroy.notify = handle_destroy; | ||
643 | wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); | ||
644 | sway_layer->map.notify = handle_map; | ||
645 | wl_signal_add(&layer_surface->events.map, &sway_layer->map); | ||
646 | sway_layer->unmap.notify = handle_unmap; | ||
647 | wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap); | ||
648 | sway_layer->new_popup.notify = handle_new_popup; | ||
649 | wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup); | ||
650 | sway_layer->new_subsurface.notify = handle_new_subsurface; | ||
651 | wl_signal_add(&layer_surface->surface->events.new_subsurface, | ||
652 | &sway_layer->new_subsurface); | ||
653 | |||
654 | sway_layer->layer_surface = layer_surface; | ||
655 | layer_surface->data = sway_layer; | ||
656 | 419 | ||
657 | struct sway_output *output = layer_surface->output->data; | 420 | sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface"); |
658 | sway_layer->output_destroy.notify = handle_output_destroy; | 421 | return; |
659 | wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); | 422 | } |
660 | 423 | ||
661 | wl_list_insert(&output->layers[layer_surface->client_pending.layer], | 424 | if (!scene_descriptor_assign(&scene_surface->tree->node, |
662 | &sway_layer->link); | 425 | SWAY_SCENE_DESC_LAYER_SHELL, surface)) { |
663 | 426 | sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor"); | |
664 | // Temporarily set the layer's current state to client_pending | 427 | // destroying the layer_surface will also destroy its corresponding |
665 | // So that we can easily arrange it | 428 | // scene node |
666 | struct wlr_layer_surface_v1_state old_state = layer_surface->current; | 429 | wlr_layer_surface_v1_destroy(layer_surface); |
667 | layer_surface->current = layer_surface->client_pending; | 430 | return; |
668 | arrange_layers(output); | 431 | } |
669 | layer_surface->current = old_state; | 432 | |
433 | surface->output = output; | ||
434 | |||
435 | surface->surface_commit.notify = handle_surface_commit; | ||
436 | wl_signal_add(&layer_surface->surface->events.commit, | ||
437 | &surface->surface_commit); | ||
438 | surface->map.notify = handle_map; | ||
439 | wl_signal_add(&layer_surface->surface->events.map, &surface->map); | ||
440 | surface->unmap.notify = handle_unmap; | ||
441 | wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap); | ||
442 | surface->new_popup.notify = handle_new_popup; | ||
443 | wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); | ||
444 | |||
445 | surface->output_destroy.notify = handle_output_destroy; | ||
446 | wl_signal_add(&output->events.disable, &surface->output_destroy); | ||
447 | |||
448 | surface->node_destroy.notify = handle_node_destroy; | ||
449 | wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy); | ||
670 | } | 450 | } |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 2f2ab4bc..70987feb 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -1,41 +1,61 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
5 | #include <time.h> | 4 | #include <time.h> |
6 | #include <wayland-server-core.h> | 5 | #include <wayland-server-core.h> |
6 | #include <wlr/config.h> | ||
7 | #include <wlr/backend/headless.h> | ||
8 | #include <wlr/render/swapchain.h> | ||
7 | #include <wlr/render/wlr_renderer.h> | 9 | #include <wlr/render/wlr_renderer.h> |
8 | #include <wlr/types/wlr_buffer.h> | 10 | #include <wlr/types/wlr_buffer.h> |
11 | #include <wlr/types/wlr_gamma_control_v1.h> | ||
9 | #include <wlr/types/wlr_matrix.h> | 12 | #include <wlr/types/wlr_matrix.h> |
10 | #include <wlr/types/wlr_output_damage.h> | ||
11 | #include <wlr/types/wlr_output_layout.h> | 13 | #include <wlr/types/wlr_output_layout.h> |
14 | #include <wlr/types/wlr_output_management_v1.h> | ||
15 | #include <wlr/types/wlr_output_power_management_v1.h> | ||
12 | #include <wlr/types/wlr_output.h> | 16 | #include <wlr/types/wlr_output.h> |
13 | #include <wlr/types/wlr_presentation_time.h> | 17 | #include <wlr/types/wlr_presentation_time.h> |
14 | #include <wlr/types/wlr_surface.h> | 18 | #include <wlr/types/wlr_compositor.h> |
15 | #include <wlr/util/region.h> | 19 | #include <wlr/util/region.h> |
20 | #include <wlr/util/transform.h> | ||
16 | #include "config.h" | 21 | #include "config.h" |
17 | #include "log.h" | 22 | #include "log.h" |
18 | #include "sway/config.h" | 23 | #include "sway/config.h" |
19 | #include "sway/desktop/transaction.h" | 24 | #include "sway/desktop/transaction.h" |
20 | #include "sway/input/input-manager.h" | 25 | #include "sway/input/input-manager.h" |
21 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/ipc-server.h" | ||
22 | #include "sway/layers.h" | 28 | #include "sway/layers.h" |
23 | #include "sway/output.h" | 29 | #include "sway/output.h" |
30 | #include "sway/scene_descriptor.h" | ||
24 | #include "sway/server.h" | 31 | #include "sway/server.h" |
25 | #include "sway/surface.h" | ||
26 | #include "sway/tree/arrange.h" | 32 | #include "sway/tree/arrange.h" |
27 | #include "sway/tree/container.h" | 33 | #include "sway/tree/container.h" |
28 | #include "sway/tree/root.h" | 34 | #include "sway/tree/root.h" |
29 | #include "sway/tree/view.h" | 35 | #include "sway/tree/view.h" |
30 | #include "sway/tree/workspace.h" | 36 | #include "sway/tree/workspace.h" |
31 | 37 | ||
38 | #if WLR_HAS_DRM_BACKEND | ||
39 | #include <wlr/backend/drm.h> | ||
40 | #include <wlr/types/wlr_drm_lease_v1.h> | ||
41 | #endif | ||
42 | |||
43 | bool output_match_name_or_id(struct sway_output *output, | ||
44 | const char *name_or_id) { | ||
45 | if (strcmp(name_or_id, "*") == 0) { | ||
46 | return true; | ||
47 | } | ||
48 | |||
49 | char identifier[128]; | ||
50 | output_get_identifier(identifier, sizeof(identifier), output); | ||
51 | return strcasecmp(identifier, name_or_id) == 0 | ||
52 | || strcasecmp(output->wlr_output->name, name_or_id) == 0; | ||
53 | } | ||
54 | |||
32 | struct sway_output *output_by_name_or_id(const char *name_or_id) { | 55 | struct sway_output *output_by_name_or_id(const char *name_or_id) { |
33 | for (int i = 0; i < root->outputs->length; ++i) { | 56 | for (int i = 0; i < root->outputs->length; ++i) { |
34 | struct sway_output *output = root->outputs->items[i]; | 57 | struct sway_output *output = root->outputs->items[i]; |
35 | char identifier[128]; | 58 | if (output_match_name_or_id(output, name_or_id)) { |
36 | output_get_identifier(identifier, sizeof(identifier), output); | ||
37 | if (strcasecmp(identifier, name_or_id) == 0 | ||
38 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
39 | return output; | 59 | return output; |
40 | } | 60 | } |
41 | } | 61 | } |
@@ -45,513 +65,217 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) { | |||
45 | struct sway_output *all_output_by_name_or_id(const char *name_or_id) { | 65 | struct sway_output *all_output_by_name_or_id(const char *name_or_id) { |
46 | struct sway_output *output; | 66 | struct sway_output *output; |
47 | wl_list_for_each(output, &root->all_outputs, link) { | 67 | wl_list_for_each(output, &root->all_outputs, link) { |
48 | char identifier[128]; | 68 | if (output_match_name_or_id(output, name_or_id)) { |
49 | output_get_identifier(identifier, sizeof(identifier), output); | ||
50 | if (strcasecmp(identifier, name_or_id) == 0 | ||
51 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
52 | return output; | 69 | return output; |
53 | } | 70 | } |
54 | } | 71 | } |
55 | return NULL; | 72 | return NULL; |
56 | } | 73 | } |
57 | 74 | ||
58 | struct surface_iterator_data { | ||
59 | sway_surface_iterator_func_t user_iterator; | ||
60 | void *user_data; | ||
61 | |||
62 | struct sway_output *output; | ||
63 | struct sway_view *view; | ||
64 | double ox, oy; | ||
65 | int width, height; | ||
66 | }; | ||
67 | |||
68 | static bool get_surface_box(struct surface_iterator_data *data, | ||
69 | struct wlr_surface *surface, int sx, int sy, | ||
70 | struct wlr_box *surface_box) { | ||
71 | struct sway_output *output = data->output; | ||
72 | |||
73 | if (!wlr_surface_has_buffer(surface)) { | ||
74 | return false; | ||
75 | } | ||
76 | |||
77 | int sw = surface->current.width; | ||
78 | int sh = surface->current.height; | ||
79 | 75 | ||
80 | struct wlr_box box = { | 76 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { |
81 | .x = floor(data->ox + sx), | 77 | struct sway_seat *seat = input_manager_current_seat(); |
82 | .y = floor(data->oy + sy), | 78 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); |
83 | .width = sw, | 79 | if (!focus) { |
84 | .height = sh, | 80 | if (!output->workspaces->length) { |
85 | }; | 81 | return NULL; |
86 | if (surface_box != NULL) { | 82 | } |
87 | memcpy(surface_box, &box, sizeof(struct wlr_box)); | 83 | return output->workspaces->items[0]; |
88 | } | 84 | } |
89 | 85 | return focus->sway_workspace; | |
90 | struct wlr_box output_box = { | ||
91 | .width = output->width, | ||
92 | .height = output->height, | ||
93 | }; | ||
94 | |||
95 | struct wlr_box intersection; | ||
96 | return wlr_box_intersection(&intersection, &output_box, &box); | ||
97 | } | 86 | } |
98 | 87 | ||
99 | static void output_for_each_surface_iterator(struct wlr_surface *surface, | 88 | struct send_frame_done_data { |
100 | int sx, int sy, void *_data) { | 89 | struct timespec when; |
101 | struct surface_iterator_data *data = _data; | 90 | int msec_until_refresh; |
102 | 91 | struct sway_output *output; | |
103 | struct wlr_box box; | 92 | }; |
104 | bool intersects = get_surface_box(data, surface, sx, sy, &box); | ||
105 | if (!intersects) { | ||
106 | return; | ||
107 | } | ||
108 | 93 | ||
109 | data->user_iterator(data->output, data->view, surface, &box, | 94 | struct buffer_timer { |
110 | data->user_data); | 95 | struct wl_listener destroy; |
111 | } | 96 | struct wl_event_source *frame_done_timer; |
97 | }; | ||
112 | 98 | ||
113 | void output_surface_for_each_surface(struct sway_output *output, | 99 | static int handle_buffer_timer(void *data) { |
114 | struct wlr_surface *surface, double ox, double oy, | 100 | struct wlr_scene_buffer *buffer = data; |
115 | sway_surface_iterator_func_t iterator, void *user_data) { | ||
116 | struct surface_iterator_data data = { | ||
117 | .user_iterator = iterator, | ||
118 | .user_data = user_data, | ||
119 | .output = output, | ||
120 | .view = NULL, | ||
121 | .ox = ox, | ||
122 | .oy = oy, | ||
123 | .width = surface->current.width, | ||
124 | .height = surface->current.height, | ||
125 | }; | ||
126 | |||
127 | wlr_surface_for_each_surface(surface, | ||
128 | output_for_each_surface_iterator, &data); | ||
129 | } | ||
130 | 101 | ||
131 | void output_view_for_each_surface(struct sway_output *output, | 102 | struct timespec now; |
132 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 103 | clock_gettime(CLOCK_MONOTONIC, &now); |
133 | void *user_data) { | 104 | wlr_scene_buffer_send_frame_done(buffer, &now); |
134 | struct surface_iterator_data data = { | 105 | return 0; |
135 | .user_iterator = iterator, | ||
136 | .user_data = user_data, | ||
137 | .output = output, | ||
138 | .view = view, | ||
139 | .ox = view->container->surface_x - output->lx | ||
140 | - view->geometry.x, | ||
141 | .oy = view->container->surface_y - output->ly | ||
142 | - view->geometry.y, | ||
143 | .width = view->container->current.content_width, | ||
144 | .height = view->container->current.content_height, | ||
145 | }; | ||
146 | |||
147 | view_for_each_surface(view, output_for_each_surface_iterator, &data); | ||
148 | } | 106 | } |
149 | 107 | ||
150 | void output_view_for_each_popup_surface(struct sway_output *output, | 108 | static void handle_buffer_timer_destroy(struct wl_listener *listener, |
151 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 109 | void *data) { |
152 | void *user_data) { | 110 | struct buffer_timer *timer = wl_container_of(listener, timer, destroy); |
153 | struct surface_iterator_data data = { | ||
154 | .user_iterator = iterator, | ||
155 | .user_data = user_data, | ||
156 | .output = output, | ||
157 | .view = view, | ||
158 | .ox = view->container->surface_x - output->lx | ||
159 | - view->geometry.x, | ||
160 | .oy = view->container->surface_y - output->ly | ||
161 | - view->geometry.y, | ||
162 | .width = view->container->current.content_width, | ||
163 | .height = view->container->current.content_height, | ||
164 | }; | ||
165 | |||
166 | view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); | ||
167 | } | ||
168 | 111 | ||
169 | void output_layer_for_each_surface(struct sway_output *output, | 112 | wl_list_remove(&timer->destroy.link); |
170 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 113 | wl_event_source_remove(timer->frame_done_timer); |
171 | void *user_data) { | 114 | free(timer); |
172 | struct sway_layer_surface *layer_surface; | ||
173 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
174 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
175 | layer_surface->layer_surface; | ||
176 | struct wlr_surface *surface = wlr_layer_surface_v1->surface; | ||
177 | struct surface_iterator_data data = { | ||
178 | .user_iterator = iterator, | ||
179 | .user_data = user_data, | ||
180 | .output = output, | ||
181 | .view = NULL, | ||
182 | .ox = layer_surface->geo.x, | ||
183 | .oy = layer_surface->geo.y, | ||
184 | .width = surface->current.width, | ||
185 | .height = surface->current.height, | ||
186 | }; | ||
187 | wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1, | ||
188 | output_for_each_surface_iterator, &data); | ||
189 | } | ||
190 | } | 115 | } |
191 | 116 | ||
192 | void output_layer_for_each_toplevel_surface(struct sway_output *output, | 117 | static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) { |
193 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 118 | struct buffer_timer *timer = |
194 | void *user_data) { | 119 | scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); |
195 | struct sway_layer_surface *layer_surface; | 120 | if (timer) { |
196 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 121 | return timer; |
197 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
198 | layer_surface->layer_surface; | ||
199 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | ||
200 | layer_surface->geo.x, layer_surface->geo.y, iterator, | ||
201 | user_data); | ||
202 | } | 122 | } |
203 | } | ||
204 | 123 | ||
205 | 124 | timer = calloc(1, sizeof(struct buffer_timer)); | |
206 | void output_layer_for_each_popup_surface(struct sway_output *output, | 125 | if (!timer) { |
207 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 126 | return NULL; |
208 | void *user_data) { | ||
209 | struct sway_layer_surface *layer_surface; | ||
210 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
211 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
212 | layer_surface->layer_surface; | ||
213 | struct wlr_surface *surface = wlr_layer_surface_v1->surface; | ||
214 | struct surface_iterator_data data = { | ||
215 | .user_iterator = iterator, | ||
216 | .user_data = user_data, | ||
217 | .output = output, | ||
218 | .view = NULL, | ||
219 | .ox = layer_surface->geo.x, | ||
220 | .oy = layer_surface->geo.y, | ||
221 | .width = surface->current.width, | ||
222 | .height = surface->current.height, | ||
223 | }; | ||
224 | wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1, | ||
225 | output_for_each_surface_iterator, &data); | ||
226 | } | 127 | } |
227 | } | ||
228 | 128 | ||
229 | #if HAVE_XWAYLAND | 129 | timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, |
230 | void output_unmanaged_for_each_surface(struct sway_output *output, | 130 | handle_buffer_timer, buffer); |
231 | struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, | 131 | if (!timer->frame_done_timer) { |
232 | void *user_data) { | 132 | free(timer); |
233 | struct sway_xwayland_unmanaged *unmanaged_surface; | 133 | return NULL; |
234 | wl_list_for_each(unmanaged_surface, unmanaged, link) { | ||
235 | struct wlr_xwayland_surface *xsurface = | ||
236 | unmanaged_surface->wlr_xwayland_surface; | ||
237 | double ox = unmanaged_surface->lx - output->lx; | ||
238 | double oy = unmanaged_surface->ly - output->ly; | ||
239 | |||
240 | output_surface_for_each_surface(output, xsurface->surface, ox, oy, | ||
241 | iterator, user_data); | ||
242 | } | 134 | } |
243 | } | ||
244 | #endif | ||
245 | 135 | ||
246 | void output_drag_icons_for_each_surface(struct sway_output *output, | 136 | scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer); |
247 | struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, | ||
248 | void *user_data) { | ||
249 | struct sway_drag_icon *drag_icon; | ||
250 | wl_list_for_each(drag_icon, drag_icons, link) { | ||
251 | double ox = drag_icon->x - output->lx; | ||
252 | double oy = drag_icon->y - output->ly; | ||
253 | |||
254 | if (drag_icon->wlr_drag_icon->mapped) { | ||
255 | output_surface_for_each_surface(output, | ||
256 | drag_icon->wlr_drag_icon->surface, ox, oy, | ||
257 | iterator, user_data); | ||
258 | } | ||
259 | } | ||
260 | } | ||
261 | 137 | ||
262 | static void for_each_surface_container_iterator(struct sway_container *con, | 138 | timer->destroy.notify = handle_buffer_timer_destroy; |
263 | void *_data) { | 139 | wl_signal_add(&buffer->node.events.destroy, &timer->destroy); |
264 | if (!con->view || !view_is_visible(con->view)) { | ||
265 | return; | ||
266 | } | ||
267 | 140 | ||
268 | struct surface_iterator_data *data = _data; | 141 | return timer; |
269 | output_view_for_each_surface(data->output, con->view, | ||
270 | data->user_iterator, data->user_data); | ||
271 | } | 142 | } |
272 | 143 | ||
273 | static void output_for_each_surface(struct sway_output *output, | 144 | static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, |
274 | sway_surface_iterator_func_t iterator, void *user_data) { | 145 | int x, int y, void *user_data) { |
275 | if (output_has_opaque_overlay_layer_surface(output)) { | 146 | struct send_frame_done_data *data = user_data; |
276 | goto overlay; | 147 | struct sway_output *output = data->output; |
277 | } | 148 | int view_max_render_time = 0; |
278 | |||
279 | struct surface_iterator_data data = { | ||
280 | .user_iterator = iterator, | ||
281 | .user_data = user_data, | ||
282 | .output = output, | ||
283 | .view = NULL, | ||
284 | }; | ||
285 | 149 | ||
286 | struct sway_workspace *workspace = output_get_active_workspace(output); | 150 | if (buffer->primary_output != data->output->scene_output) { |
287 | struct sway_container *fullscreen_con = root->fullscreen_global; | 151 | return; |
288 | if (!fullscreen_con) { | ||
289 | if (!workspace) { | ||
290 | return; | ||
291 | } | ||
292 | fullscreen_con = workspace->current.fullscreen; | ||
293 | } | ||
294 | if (fullscreen_con) { | ||
295 | for_each_surface_container_iterator(fullscreen_con, &data); | ||
296 | container_for_each_child(fullscreen_con, | ||
297 | for_each_surface_container_iterator, &data); | ||
298 | |||
299 | // TODO: Show transient containers for fullscreen global | ||
300 | if (fullscreen_con == workspace->current.fullscreen) { | ||
301 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
302 | struct sway_container *floater = | ||
303 | workspace->current.floating->items[i]; | ||
304 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
305 | for_each_surface_container_iterator(floater, &data); | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | #if HAVE_XWAYLAND | ||
310 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
311 | iterator, user_data); | ||
312 | #endif | ||
313 | } else { | ||
314 | output_layer_for_each_surface(output, | ||
315 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
316 | iterator, user_data); | ||
317 | output_layer_for_each_surface(output, | ||
318 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
319 | iterator, user_data); | ||
320 | |||
321 | workspace_for_each_container(workspace, | ||
322 | for_each_surface_container_iterator, &data); | ||
323 | |||
324 | #if HAVE_XWAYLAND | ||
325 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
326 | iterator, user_data); | ||
327 | #endif | ||
328 | output_layer_for_each_surface(output, | ||
329 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
330 | iterator, user_data); | ||
331 | } | 152 | } |
332 | 153 | ||
333 | overlay: | 154 | struct wlr_scene_node *current = &buffer->node; |
334 | output_layer_for_each_surface(output, | 155 | while (true) { |
335 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 156 | struct sway_view *view = scene_descriptor_try_get(current, |
336 | iterator, user_data); | 157 | SWAY_SCENE_DESC_VIEW); |
337 | output_drag_icons_for_each_surface(output, &root->drag_icons, | 158 | if (view) { |
338 | iterator, user_data); | 159 | view_max_render_time = view->max_render_time; |
339 | } | 160 | break; |
340 | |||
341 | static int scale_length(int length, int offset, float scale) { | ||
342 | return round((offset + length) * scale) - round(offset * scale); | ||
343 | } | ||
344 | |||
345 | void scale_box(struct wlr_box *box, float scale) { | ||
346 | box->width = scale_length(box->width, box->x, scale); | ||
347 | box->height = scale_length(box->height, box->y, scale); | ||
348 | box->x = round(box->x * scale); | ||
349 | box->y = round(box->y * scale); | ||
350 | } | ||
351 | |||
352 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { | ||
353 | struct sway_seat *seat = input_manager_current_seat(); | ||
354 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); | ||
355 | if (!focus) { | ||
356 | if (!output->workspaces->length) { | ||
357 | return NULL; | ||
358 | } | 161 | } |
359 | return output->workspaces->items[0]; | ||
360 | } | ||
361 | return focus->sway_workspace; | ||
362 | } | ||
363 | 162 | ||
364 | bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { | 163 | if (!current->parent) { |
365 | struct sway_layer_surface *sway_layer_surface; | 164 | break; |
366 | wl_list_for_each(sway_layer_surface, | ||
367 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | ||
368 | struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface; | ||
369 | pixman_box32_t output_box = { | ||
370 | .x2 = output->width, | ||
371 | .y2 = output->height, | ||
372 | }; | ||
373 | pixman_region32_t surface_opaque_box; | ||
374 | pixman_region32_init(&surface_opaque_box); | ||
375 | pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); | ||
376 | pixman_region32_translate(&surface_opaque_box, | ||
377 | sway_layer_surface->geo.x, sway_layer_surface->geo.y); | ||
378 | pixman_region_overlap_t contains = | ||
379 | pixman_region32_contains_rectangle(&surface_opaque_box, &output_box); | ||
380 | pixman_region32_fini(&surface_opaque_box); | ||
381 | |||
382 | if (contains == PIXMAN_REGION_IN) { | ||
383 | return true; | ||
384 | } | 165 | } |
385 | } | ||
386 | return false; | ||
387 | } | ||
388 | 166 | ||
389 | struct send_frame_done_data { | 167 | current = ¤t->parent->node; |
390 | struct timespec when; | ||
391 | int msec_until_refresh; | ||
392 | }; | ||
393 | |||
394 | static void send_frame_done_iterator(struct sway_output *output, | ||
395 | struct sway_view *view, struct wlr_surface *surface, | ||
396 | struct wlr_box *box, void *user_data) { | ||
397 | int view_max_render_time = 0; | ||
398 | if (view != NULL) { | ||
399 | view_max_render_time = view->max_render_time; | ||
400 | } | 168 | } |
401 | 169 | ||
402 | struct send_frame_done_data *data = user_data; | ||
403 | |||
404 | int delay = data->msec_until_refresh - output->max_render_time | 170 | int delay = data->msec_until_refresh - output->max_render_time |
405 | - view_max_render_time; | 171 | - view_max_render_time; |
406 | 172 | ||
407 | if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { | 173 | struct buffer_timer *timer = NULL; |
408 | wlr_surface_send_frame_done(surface, &data->when); | ||
409 | } else { | ||
410 | struct sway_surface *sway_surface = surface->data; | ||
411 | wl_event_source_timer_update(sway_surface->frame_done_timer, delay); | ||
412 | } | ||
413 | } | ||
414 | |||
415 | static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { | ||
416 | output_for_each_surface(output, send_frame_done_iterator, data); | ||
417 | } | ||
418 | |||
419 | static void count_surface_iterator(struct sway_output *output, | ||
420 | struct sway_view *view, struct wlr_surface *surface, | ||
421 | struct wlr_box *box, void *data) { | ||
422 | size_t *n = data; | ||
423 | (*n)++; | ||
424 | } | ||
425 | 174 | ||
426 | static bool scan_out_fullscreen_view(struct sway_output *output, | 175 | if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { |
427 | struct sway_view *view) { | 176 | timer = buffer_timer_get_or_create(buffer); |
428 | struct wlr_output *wlr_output = output->wlr_output; | ||
429 | struct sway_workspace *workspace = output->current.active_workspace; | ||
430 | if (!sway_assert(workspace, "Expected an active workspace")) { | ||
431 | return false; | ||
432 | } | 177 | } |
433 | 178 | ||
434 | if (!wl_list_empty(&view->saved_buffers)) { | 179 | if (timer) { |
435 | return false; | 180 | wl_event_source_timer_update(timer->frame_done_timer, delay); |
181 | } else { | ||
182 | wlr_scene_buffer_send_frame_done(buffer, &data->when); | ||
436 | } | 183 | } |
184 | } | ||
437 | 185 | ||
438 | for (int i = 0; i < workspace->current.floating->length; ++i) { | 186 | static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, |
439 | struct sway_container *floater = | 187 | struct wlr_scene_buffer *buffer) { |
440 | workspace->current.floating->items[i]; | 188 | // if we are scaling down, we should always choose linear |
441 | if (container_is_transient_for(floater, view->container)) { | 189 | if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( |
442 | return false; | 190 | buffer->dst_width < buffer->buffer_width || |
443 | } | 191 | buffer->dst_height < buffer->buffer_height)) { |
192 | return WLR_SCALE_FILTER_BILINEAR; | ||
444 | } | 193 | } |
445 | 194 | ||
446 | #if HAVE_XWAYLAND | 195 | switch (output->scale_filter) { |
447 | if (!wl_list_empty(&root->xwayland_unmanaged)) { | 196 | case SCALE_FILTER_LINEAR: |
448 | return false; | 197 | return WLR_SCALE_FILTER_BILINEAR; |
198 | case SCALE_FILTER_NEAREST: | ||
199 | return WLR_SCALE_FILTER_NEAREST; | ||
200 | default: | ||
201 | abort(); // unreachable | ||
449 | } | 202 | } |
450 | #endif | 203 | } |
451 | 204 | ||
452 | if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { | 205 | static void output_configure_scene(struct sway_output *output, |
453 | return false; | 206 | struct wlr_scene_node *node, float opacity) { |
454 | } | 207 | if (!node->enabled) { |
455 | if (!wl_list_empty(&root->drag_icons)) { | 208 | return; |
456 | return false; | ||
457 | } | 209 | } |
458 | 210 | ||
459 | struct wlr_surface *surface = view->surface; | 211 | struct sway_container *con = |
460 | if (surface == NULL) { | 212 | scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER); |
461 | return false; | 213 | if (con) { |
462 | } | 214 | opacity = con->alpha; |
463 | size_t n_surfaces = 0; | ||
464 | output_view_for_each_surface(output, view, | ||
465 | count_surface_iterator, &n_surfaces); | ||
466 | if (n_surfaces != 1) { | ||
467 | return false; | ||
468 | } | 215 | } |
469 | 216 | ||
470 | if (surface->buffer == NULL) { | 217 | if (node->type == WLR_SCENE_NODE_BUFFER) { |
471 | return false; | 218 | struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); |
472 | } | ||
473 | 219 | ||
474 | if ((float)surface->current.scale != wlr_output->scale || | 220 | // hack: don't call the scene setter because that will damage all outputs |
475 | surface->current.transform != wlr_output->transform) { | 221 | // We don't want to damage outputs that aren't our current output that |
476 | return false; | 222 | // we're configuring |
477 | } | 223 | buffer->filter_mode = get_scale_filter(output, buffer); |
478 | 224 | ||
479 | wlr_output_attach_buffer(wlr_output, &surface->buffer->base); | 225 | wlr_scene_buffer_set_opacity(buffer, opacity); |
480 | if (!wlr_output_test(wlr_output)) { | 226 | } else if (node->type == WLR_SCENE_NODE_TREE) { |
481 | return false; | 227 | struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node); |
228 | struct wlr_scene_node *node; | ||
229 | wl_list_for_each(node, &tree->children, link) { | ||
230 | output_configure_scene(output, node, opacity); | ||
231 | } | ||
482 | } | 232 | } |
483 | |||
484 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
485 | wlr_output); | ||
486 | |||
487 | return wlr_output_commit(wlr_output); | ||
488 | } | 233 | } |
489 | 234 | ||
490 | static int output_repaint_timer_handler(void *data) { | 235 | static int output_repaint_timer_handler(void *data) { |
491 | struct sway_output *output = data; | 236 | struct sway_output *output = data; |
492 | if (output->wlr_output == NULL) { | ||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | output->wlr_output->frame_pending = false; | ||
497 | 237 | ||
498 | struct sway_workspace *workspace = output->current.active_workspace; | 238 | if (!output->enabled) { |
499 | if (workspace == NULL) { | ||
500 | return 0; | 239 | return 0; |
501 | } | 240 | } |
502 | 241 | ||
503 | struct sway_container *fullscreen_con = root->fullscreen_global; | 242 | output->wlr_output->frame_pending = false; |
504 | if (!fullscreen_con) { | ||
505 | fullscreen_con = workspace->current.fullscreen; | ||
506 | } | ||
507 | 243 | ||
508 | if (fullscreen_con && fullscreen_con->view) { | 244 | output_configure_scene(output, &root->root_scene->tree.node, 1.0f); |
509 | // Try to scan-out the fullscreen view | ||
510 | static bool last_scanned_out = false; | ||
511 | bool scanned_out = | ||
512 | scan_out_fullscreen_view(output, fullscreen_con->view); | ||
513 | 245 | ||
514 | if (scanned_out && !last_scanned_out) { | 246 | if (output->gamma_lut_changed) { |
515 | sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", | 247 | struct wlr_output_state pending; |
516 | output->wlr_output->name); | 248 | wlr_output_state_init(&pending); |
249 | if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) { | ||
250 | return 0; | ||
517 | } | 251 | } |
518 | if (last_scanned_out && !scanned_out) { | 252 | |
519 | sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", | 253 | output->gamma_lut_changed = false; |
520 | output->wlr_output->name); | 254 | struct wlr_gamma_control_v1 *gamma_control = |
521 | output_damage_whole(output); | 255 | wlr_gamma_control_manager_v1_get_control( |
256 | server.gamma_control_manager_v1, output->wlr_output); | ||
257 | if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { | ||
258 | wlr_output_state_finish(&pending); | ||
259 | return 0; | ||
522 | } | 260 | } |
523 | last_scanned_out = scanned_out; | ||
524 | 261 | ||
525 | if (scanned_out) { | 262 | if (!wlr_output_commit_state(output->wlr_output, &pending)) { |
263 | wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); | ||
264 | wlr_output_state_finish(&pending); | ||
526 | return 0; | 265 | return 0; |
527 | } | 266 | } |
528 | } | ||
529 | 267 | ||
530 | bool needs_frame; | 268 | wlr_output_state_finish(&pending); |
531 | pixman_region32_t damage; | ||
532 | pixman_region32_init(&damage); | ||
533 | if (!wlr_output_damage_attach_render(output->damage, | ||
534 | &needs_frame, &damage)) { | ||
535 | return 0; | 269 | return 0; |
536 | } | 270 | } |
537 | 271 | ||
538 | if (needs_frame) { | 272 | wlr_scene_output_commit(output->scene_output, NULL); |
539 | struct timespec now; | ||
540 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
541 | |||
542 | output_render(output, &now, &damage); | ||
543 | } else { | ||
544 | wlr_output_rollback(output->wlr_output); | ||
545 | } | ||
546 | |||
547 | pixman_region32_fini(&damage); | ||
548 | |||
549 | return 0; | 273 | return 0; |
550 | } | 274 | } |
551 | 275 | ||
552 | static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | 276 | static void handle_frame(struct wl_listener *listener, void *user_data) { |
553 | struct sway_output *output = | 277 | struct sway_output *output = |
554 | wl_container_of(listener, output, damage_frame); | 278 | wl_container_of(listener, output, frame); |
555 | if (!output->enabled || !output->wlr_output->enabled) { | 279 | if (!output->enabled || !output->wlr_output->enabled) { |
556 | return; | 280 | return; |
557 | } | 281 | } |
@@ -562,9 +286,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
562 | 286 | ||
563 | if (output->max_render_time != 0) { | 287 | if (output->max_render_time != 0) { |
564 | struct timespec now; | 288 | struct timespec now; |
565 | clockid_t presentation_clock | 289 | clock_gettime(CLOCK_MONOTONIC, &now); |
566 | = wlr_backend_get_presentation_clock(server.backend); | ||
567 | clock_gettime(presentation_clock, &now); | ||
568 | 290 | ||
569 | const long NSEC_IN_SECONDS = 1000000000; | 291 | const long NSEC_IN_SECONDS = 1000000000; |
570 | struct timespec predicted_refresh = output->last_presentation; | 292 | struct timespec predicted_refresh = output->last_presentation; |
@@ -611,118 +333,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
611 | struct send_frame_done_data data = {0}; | 333 | struct send_frame_done_data data = {0}; |
612 | clock_gettime(CLOCK_MONOTONIC, &data.when); | 334 | clock_gettime(CLOCK_MONOTONIC, &data.when); |
613 | data.msec_until_refresh = msec_until_refresh; | 335 | data.msec_until_refresh = msec_until_refresh; |
614 | send_frame_done(output, &data); | 336 | data.output = output; |
615 | } | 337 | wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); |
616 | |||
617 | void output_damage_whole(struct sway_output *output) { | ||
618 | // The output can exist with no wlr_output if it's just been disconnected | ||
619 | // and the transaction to evacuate it has't completed yet. | ||
620 | if (output && output->wlr_output && output->damage) { | ||
621 | wlr_output_damage_add_whole(output->damage); | ||
622 | } | ||
623 | } | ||
624 | |||
625 | static void damage_surface_iterator(struct sway_output *output, | ||
626 | struct sway_view *view, struct wlr_surface *surface, | ||
627 | struct wlr_box *_box, void *_data) { | ||
628 | bool *data = _data; | ||
629 | bool whole = *data; | ||
630 | |||
631 | struct wlr_box box = *_box; | ||
632 | scale_box(&box, output->wlr_output->scale); | ||
633 | |||
634 | if (pixman_region32_not_empty(&surface->buffer_damage)) { | ||
635 | pixman_region32_t damage; | ||
636 | pixman_region32_init(&damage); | ||
637 | wlr_surface_get_effective_damage(surface, &damage); | ||
638 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); | ||
639 | if (ceil(output->wlr_output->scale) > surface->current.scale) { | ||
640 | // When scaling up a surface, it'll become blurry so we need to | ||
641 | // expand the damage region | ||
642 | wlr_region_expand(&damage, &damage, | ||
643 | ceil(output->wlr_output->scale) - surface->current.scale); | ||
644 | } | ||
645 | pixman_region32_translate(&damage, box.x, box.y); | ||
646 | wlr_output_damage_add(output->damage, &damage); | ||
647 | pixman_region32_fini(&damage); | ||
648 | } | ||
649 | |||
650 | if (whole) { | ||
651 | wlr_output_damage_add_box(output->damage, &box); | ||
652 | } | ||
653 | |||
654 | if (!wl_list_empty(&surface->current.frame_callback_list)) { | ||
655 | wlr_output_schedule_frame(output->wlr_output); | ||
656 | } | ||
657 | } | ||
658 | |||
659 | void output_damage_surface(struct sway_output *output, double ox, double oy, | ||
660 | struct wlr_surface *surface, bool whole) { | ||
661 | output_surface_for_each_surface(output, surface, ox, oy, | ||
662 | damage_surface_iterator, &whole); | ||
663 | } | ||
664 | |||
665 | void output_damage_from_view(struct sway_output *output, | ||
666 | struct sway_view *view) { | ||
667 | if (!view_is_visible(view)) { | ||
668 | return; | ||
669 | } | ||
670 | bool whole = false; | ||
671 | output_view_for_each_surface(output, view, damage_surface_iterator, &whole); | ||
672 | } | ||
673 | |||
674 | // Expecting an unscaled box in layout coordinates | ||
675 | void output_damage_box(struct sway_output *output, struct wlr_box *_box) { | ||
676 | struct wlr_box box; | ||
677 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
678 | box.x -= output->lx; | ||
679 | box.y -= output->ly; | ||
680 | scale_box(&box, output->wlr_output->scale); | ||
681 | wlr_output_damage_add_box(output->damage, &box); | ||
682 | } | ||
683 | |||
684 | static void damage_child_views_iterator(struct sway_container *con, | ||
685 | void *data) { | ||
686 | if (!con->view || !view_is_visible(con->view)) { | ||
687 | return; | ||
688 | } | ||
689 | struct sway_output *output = data; | ||
690 | bool whole = true; | ||
691 | output_view_for_each_surface(output, con->view, damage_surface_iterator, | ||
692 | &whole); | ||
693 | } | ||
694 | |||
695 | void output_damage_whole_container(struct sway_output *output, | ||
696 | struct sway_container *con) { | ||
697 | // Pad the box by 1px, because the width is a double and might be a fraction | ||
698 | struct wlr_box box = { | ||
699 | .x = con->current.x - output->lx - 1, | ||
700 | .y = con->current.y - output->ly - 1, | ||
701 | .width = con->current.width + 2, | ||
702 | .height = con->current.height + 2, | ||
703 | }; | ||
704 | scale_box(&box, output->wlr_output->scale); | ||
705 | wlr_output_damage_add_box(output->damage, &box); | ||
706 | // Damage subsurfaces as well, which may extend outside the box | ||
707 | if (con->view) { | ||
708 | damage_child_views_iterator(con, output); | ||
709 | } else { | ||
710 | container_for_each_child(con, damage_child_views_iterator, output); | ||
711 | } | ||
712 | } | ||
713 | |||
714 | static void damage_handle_destroy(struct wl_listener *listener, void *data) { | ||
715 | struct sway_output *output = | ||
716 | wl_container_of(listener, output, damage_destroy); | ||
717 | if (!output->enabled) { | ||
718 | return; | ||
719 | } | ||
720 | output_disable(output); | ||
721 | |||
722 | wl_list_remove(&output->damage_destroy.link); | ||
723 | wl_list_remove(&output->damage_frame.link); | ||
724 | |||
725 | transaction_commit_dirty(); | ||
726 | } | 338 | } |
727 | 339 | ||
728 | static void update_output_manager_config(struct sway_server *server) { | 340 | static void update_output_manager_config(struct sway_server *server) { |
@@ -731,73 +343,61 @@ static void update_output_manager_config(struct sway_server *server) { | |||
731 | 343 | ||
732 | struct sway_output *output; | 344 | struct sway_output *output; |
733 | wl_list_for_each(output, &root->all_outputs, link) { | 345 | wl_list_for_each(output, &root->all_outputs, link) { |
734 | if (output == root->noop_output) { | 346 | if (output == root->fallback_output) { |
735 | continue; | 347 | continue; |
736 | } | 348 | } |
737 | struct wlr_output_configuration_head_v1 *config_head = | 349 | struct wlr_output_configuration_head_v1 *config_head = |
738 | wlr_output_configuration_head_v1_create(config, output->wlr_output); | 350 | wlr_output_configuration_head_v1_create(config, output->wlr_output); |
739 | struct wlr_box *output_box = wlr_output_layout_get_box( | 351 | struct wlr_box output_box; |
740 | root->output_layout, output->wlr_output); | 352 | wlr_output_layout_get_box(root->output_layout, |
741 | // We mark the output enabled even if it is switched off by DPMS | 353 | output->wlr_output, &output_box); |
742 | config_head->state.enabled = output->current_mode != NULL && output->enabled; | 354 | // We mark the output enabled when it's switched off but not disabled |
743 | config_head->state.mode = output->current_mode; | 355 | config_head->state.enabled = !wlr_box_empty(&output_box); |
744 | if (output_box) { | 356 | config_head->state.x = output_box.x; |
745 | config_head->state.x = output_box->x; | 357 | config_head->state.y = output_box.y; |
746 | config_head->state.y = output_box->y; | ||
747 | } | ||
748 | } | 358 | } |
749 | 359 | ||
750 | wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); | 360 | wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); |
361 | |||
362 | ipc_event_output(); | ||
751 | } | 363 | } |
752 | 364 | ||
753 | static void handle_destroy(struct wl_listener *listener, void *data) { | 365 | static void begin_destroy(struct sway_output *output) { |
754 | struct sway_output *output = wl_container_of(listener, output, destroy); | ||
755 | struct sway_server *server = output->server; | 366 | struct sway_server *server = output->server; |
756 | wl_signal_emit(&output->events.destroy, output); | ||
757 | 367 | ||
758 | if (output->enabled) { | 368 | if (output->enabled) { |
759 | output_disable(output); | 369 | output_disable(output); |
760 | } | 370 | } |
371 | |||
761 | output_begin_destroy(output); | 372 | output_begin_destroy(output); |
762 | 373 | ||
374 | wl_list_remove(&output->link); | ||
375 | |||
376 | wl_list_remove(&output->layout_destroy.link); | ||
763 | wl_list_remove(&output->destroy.link); | 377 | wl_list_remove(&output->destroy.link); |
764 | wl_list_remove(&output->commit.link); | 378 | wl_list_remove(&output->commit.link); |
765 | wl_list_remove(&output->mode.link); | ||
766 | wl_list_remove(&output->present.link); | 379 | wl_list_remove(&output->present.link); |
380 | wl_list_remove(&output->frame.link); | ||
381 | wl_list_remove(&output->request_state.link); | ||
382 | |||
383 | wlr_scene_output_destroy(output->scene_output); | ||
384 | output->scene_output = NULL; | ||
385 | output->wlr_output->data = NULL; | ||
386 | output->wlr_output = NULL; | ||
767 | 387 | ||
768 | transaction_commit_dirty(); | 388 | transaction_commit_dirty(); |
769 | 389 | ||
770 | update_output_manager_config(server); | 390 | update_output_manager_config(server); |
771 | } | 391 | } |
772 | 392 | ||
773 | static void handle_mode(struct wl_listener *listener, void *data) { | 393 | static void handle_destroy(struct wl_listener *listener, void *data) { |
774 | struct sway_output *output = wl_container_of(listener, output, mode); | 394 | struct sway_output *output = wl_container_of(listener, output, destroy); |
775 | if (!output->enabled && !output->enabling) { | 395 | begin_destroy(output); |
776 | struct output_config *oc = find_output_config(output); | ||
777 | if (output->wlr_output->current_mode != NULL && | ||
778 | (!oc || oc->enabled)) { | ||
779 | // We want to enable this output, but it didn't work last time, | ||
780 | // possibly because we hadn't enough CRTCs. Try again now that the | ||
781 | // output has a mode. | ||
782 | sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, " | ||
783 | "trying to enable it", output->wlr_output->name); | ||
784 | apply_output_config(oc, output); | ||
785 | } | ||
786 | return; | ||
787 | } | ||
788 | if (!output->enabled) { | ||
789 | return; | ||
790 | } | ||
791 | arrange_layers(output); | ||
792 | arrange_output(output); | ||
793 | transaction_commit_dirty(); | ||
794 | |||
795 | update_output_manager_config(output->server); | ||
796 | } | 396 | } |
797 | 397 | ||
798 | static void update_textures(struct sway_container *con, void *data) { | 398 | static void handle_layout_destroy(struct wl_listener *listener, void *data) { |
799 | container_update_title_textures(con); | 399 | struct sway_output *output = wl_container_of(listener, output, layout_destroy); |
800 | container_update_marks_textures(con); | 400 | begin_destroy(output); |
801 | } | 401 | } |
802 | 402 | ||
803 | static void handle_commit(struct wl_listener *listener, void *data) { | 403 | static void handle_commit(struct wl_listener *listener, void *data) { |
@@ -808,24 +408,28 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
808 | return; | 408 | return; |
809 | } | 409 | } |
810 | 410 | ||
811 | if (event->committed & WLR_OUTPUT_STATE_SCALE) { | 411 | if (event->state->committed & ( |
812 | output_for_each_container(output, update_textures, NULL); | 412 | WLR_OUTPUT_STATE_MODE | |
813 | } | 413 | WLR_OUTPUT_STATE_TRANSFORM | |
814 | 414 | WLR_OUTPUT_STATE_SCALE)) { | |
815 | if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { | ||
816 | arrange_layers(output); | 415 | arrange_layers(output); |
817 | arrange_output(output); | 416 | arrange_output(output); |
818 | transaction_commit_dirty(); | 417 | transaction_commit_dirty(); |
819 | 418 | ||
820 | update_output_manager_config(output->server); | 419 | update_output_manager_config(output->server); |
821 | } | 420 | } |
421 | |||
422 | // Next time the output is enabled, try to re-apply the gamma LUT | ||
423 | if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) { | ||
424 | output->gamma_lut_changed = true; | ||
425 | } | ||
822 | } | 426 | } |
823 | 427 | ||
824 | static void handle_present(struct wl_listener *listener, void *data) { | 428 | static void handle_present(struct wl_listener *listener, void *data) { |
825 | struct sway_output *output = wl_container_of(listener, output, present); | 429 | struct sway_output *output = wl_container_of(listener, output, present); |
826 | struct wlr_output_event_present *output_event = data; | 430 | struct wlr_output_event_present *output_event = data; |
827 | 431 | ||
828 | if (!output->enabled) { | 432 | if (!output->enabled || !output_event->presented) { |
829 | return; | 433 | return; |
830 | } | 434 | } |
831 | 435 | ||
@@ -833,37 +437,91 @@ static void handle_present(struct wl_listener *listener, void *data) { | |||
833 | output->refresh_nsec = output_event->refresh; | 437 | output->refresh_nsec = output_event->refresh; |
834 | } | 438 | } |
835 | 439 | ||
440 | static void handle_request_state(struct wl_listener *listener, void *data) { | ||
441 | struct sway_output *output = | ||
442 | wl_container_of(listener, output, request_state); | ||
443 | const struct wlr_output_event_request_state *event = data; | ||
444 | wlr_output_commit_state(output->wlr_output, event->state); | ||
445 | } | ||
446 | |||
447 | static unsigned int last_headless_num = 0; | ||
448 | |||
836 | void handle_new_output(struct wl_listener *listener, void *data) { | 449 | void handle_new_output(struct wl_listener *listener, void *data) { |
837 | struct sway_server *server = wl_container_of(listener, server, new_output); | 450 | struct sway_server *server = wl_container_of(listener, server, new_output); |
838 | struct wlr_output *wlr_output = data; | 451 | struct wlr_output *wlr_output = data; |
839 | sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); | 452 | |
453 | if (wlr_output == root->fallback_output->wlr_output) { | ||
454 | return; | ||
455 | } | ||
456 | |||
457 | if (wlr_output_is_headless(wlr_output)) { | ||
458 | char name[64]; | ||
459 | snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num); | ||
460 | wlr_output_set_name(wlr_output, name); | ||
461 | } | ||
462 | |||
463 | sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)", | ||
464 | wlr_output, wlr_output->name, wlr_output->non_desktop); | ||
465 | |||
466 | if (wlr_output->non_desktop) { | ||
467 | sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); | ||
468 | struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output); | ||
469 | #if WLR_HAS_DRM_BACKEND | ||
470 | if (server->drm_lease_manager) { | ||
471 | wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, | ||
472 | wlr_output); | ||
473 | } | ||
474 | #endif | ||
475 | list_add(root->non_desktop_outputs, non_desktop); | ||
476 | return; | ||
477 | } | ||
478 | |||
479 | if (!wlr_output_init_render(wlr_output, server->allocator, | ||
480 | server->renderer)) { | ||
481 | sway_log(SWAY_ERROR, "Failed to init output render"); | ||
482 | return; | ||
483 | } | ||
484 | |||
485 | // Create the scene output here so we're not accidentally creating one for | ||
486 | // the fallback output | ||
487 | struct wlr_scene_output *scene_output = | ||
488 | wlr_scene_output_create(root->root_scene, wlr_output); | ||
489 | if (!scene_output) { | ||
490 | sway_log(SWAY_ERROR, "Failed to create a scene output"); | ||
491 | return; | ||
492 | } | ||
840 | 493 | ||
841 | struct sway_output *output = output_create(wlr_output); | 494 | struct sway_output *output = output_create(wlr_output); |
842 | if (!output) { | 495 | if (!output) { |
496 | sway_log(SWAY_ERROR, "Failed to create a sway output"); | ||
497 | wlr_scene_output_destroy(scene_output); | ||
843 | return; | 498 | return; |
844 | } | 499 | } |
500 | |||
845 | output->server = server; | 501 | output->server = server; |
846 | output->damage = wlr_output_damage_create(wlr_output); | 502 | output->scene_output = scene_output; |
847 | 503 | ||
504 | wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); | ||
505 | output->layout_destroy.notify = handle_layout_destroy; | ||
848 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | 506 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); |
849 | output->destroy.notify = handle_destroy; | 507 | output->destroy.notify = handle_destroy; |
850 | wl_signal_add(&wlr_output->events.commit, &output->commit); | 508 | wl_signal_add(&wlr_output->events.commit, &output->commit); |
851 | output->commit.notify = handle_commit; | 509 | output->commit.notify = handle_commit; |
852 | wl_signal_add(&wlr_output->events.mode, &output->mode); | ||
853 | output->mode.notify = handle_mode; | ||
854 | wl_signal_add(&wlr_output->events.present, &output->present); | 510 | wl_signal_add(&wlr_output->events.present, &output->present); |
855 | output->present.notify = handle_present; | 511 | output->present.notify = handle_present; |
856 | wl_signal_add(&output->damage->events.frame, &output->damage_frame); | 512 | wl_signal_add(&wlr_output->events.frame, &output->frame); |
857 | output->damage_frame.notify = damage_handle_frame; | 513 | output->frame.notify = handle_frame; |
858 | wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); | 514 | wl_signal_add(&wlr_output->events.request_state, &output->request_state); |
859 | output->damage_destroy.notify = damage_handle_destroy; | 515 | output->request_state.notify = handle_request_state; |
860 | 516 | ||
861 | output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, | 517 | output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, |
862 | output_repaint_timer_handler, output); | 518 | output_repaint_timer_handler, output); |
863 | 519 | ||
864 | struct output_config *oc = find_output_config(output); | 520 | if (server->session_lock.lock) { |
865 | apply_output_config(oc, output); | 521 | sway_session_lock_add_output(server->session_lock.lock, output); |
866 | free_output_config(oc); | 522 | } |
523 | |||
524 | apply_all_output_configs(); | ||
867 | 525 | ||
868 | transaction_commit_dirty(); | 526 | transaction_commit_dirty(); |
869 | 527 | ||
@@ -877,62 +535,103 @@ void handle_output_layout_change(struct wl_listener *listener, | |||
877 | update_output_manager_config(server); | 535 | update_output_manager_config(server); |
878 | } | 536 | } |
879 | 537 | ||
538 | void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { | ||
539 | struct sway_server *server = | ||
540 | wl_container_of(listener, server, gamma_control_set_gamma); | ||
541 | const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; | ||
542 | |||
543 | struct sway_output *output = event->output->data; | ||
544 | |||
545 | if(!output) { | ||
546 | return; | ||
547 | } | ||
548 | |||
549 | output->gamma_lut_changed = true; | ||
550 | wlr_output_schedule_frame(output->wlr_output); | ||
551 | } | ||
552 | |||
553 | static struct output_config *output_config_for_config_head( | ||
554 | struct wlr_output_configuration_head_v1 *config_head, | ||
555 | struct sway_output *output) { | ||
556 | struct output_config *oc = new_output_config(output->wlr_output->name); | ||
557 | oc->enabled = config_head->state.enabled; | ||
558 | if (!oc->enabled) { | ||
559 | return oc; | ||
560 | } | ||
561 | |||
562 | if (config_head->state.mode != NULL) { | ||
563 | struct wlr_output_mode *mode = config_head->state.mode; | ||
564 | oc->width = mode->width; | ||
565 | oc->height = mode->height; | ||
566 | oc->refresh_rate = mode->refresh / 1000.f; | ||
567 | } else { | ||
568 | oc->width = config_head->state.custom_mode.width; | ||
569 | oc->height = config_head->state.custom_mode.height; | ||
570 | oc->refresh_rate = | ||
571 | config_head->state.custom_mode.refresh / 1000.f; | ||
572 | } | ||
573 | oc->x = config_head->state.x; | ||
574 | oc->y = config_head->state.y; | ||
575 | oc->transform = config_head->state.transform; | ||
576 | oc->scale = config_head->state.scale; | ||
577 | oc->adaptive_sync = config_head->state.adaptive_sync_enabled; | ||
578 | return oc; | ||
579 | } | ||
580 | |||
880 | static void output_manager_apply(struct sway_server *server, | 581 | static void output_manager_apply(struct sway_server *server, |
881 | struct wlr_output_configuration_v1 *config, bool test_only) { | 582 | struct wlr_output_configuration_v1 *config, bool test_only) { |
882 | // TODO: perform atomic tests on the whole backend atomically | 583 | size_t configs_len = wl_list_length(&root->all_outputs); |
883 | 584 | struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); | |
884 | struct wlr_output_configuration_head_v1 *config_head; | 585 | if (!configs) { |
885 | // First disable outputs we need to disable | 586 | return; |
886 | bool ok = true; | 587 | } |
887 | wl_list_for_each(config_head, &config->heads, link) { | 588 | |
888 | struct wlr_output *wlr_output = config_head->state.output; | 589 | int config_idx = 0; |
889 | struct sway_output *output = wlr_output->data; | 590 | struct sway_output *sway_output; |
890 | if (!output->enabled || config_head->state.enabled) { | 591 | wl_list_for_each(sway_output, &root->all_outputs, link) { |
592 | if (sway_output == root->fallback_output) { | ||
593 | configs_len--; | ||
891 | continue; | 594 | continue; |
892 | } | 595 | } |
893 | struct output_config *oc = new_output_config(output->wlr_output->name); | ||
894 | oc->enabled = false; | ||
895 | 596 | ||
896 | if (test_only) { | 597 | struct matched_output_config *cfg = &configs[config_idx++]; |
897 | ok &= test_output_config(oc, output); | 598 | cfg->output = sway_output; |
898 | } else { | ||
899 | oc = store_output_config(oc); | ||
900 | ok &= apply_output_config(oc, output); | ||
901 | } | ||
902 | } | ||
903 | 599 | ||
904 | // Then enable outputs that need to | 600 | struct wlr_output_configuration_head_v1 *config_head; |
905 | wl_list_for_each(config_head, &config->heads, link) { | 601 | wl_list_for_each(config_head, &config->heads, link) { |
906 | struct wlr_output *wlr_output = config_head->state.output; | 602 | if (config_head->state.output == sway_output->wlr_output) { |
907 | struct sway_output *output = wlr_output->data; | 603 | cfg->config = output_config_for_config_head(config_head, sway_output); |
908 | if (!config_head->state.enabled) { | 604 | break; |
909 | continue; | 605 | } |
910 | } | 606 | } |
911 | struct output_config *oc = new_output_config(output->wlr_output->name); | 607 | if (!cfg->config) { |
912 | oc->enabled = true; | 608 | cfg->config = find_output_config(sway_output); |
913 | if (config_head->state.mode != NULL) { | ||
914 | struct wlr_output_mode *mode = config_head->state.mode; | ||
915 | oc->width = mode->width; | ||
916 | oc->height = mode->height; | ||
917 | oc->refresh_rate = mode->refresh / 1000.f; | ||
918 | } else { | ||
919 | oc->width = config_head->state.custom_mode.width; | ||
920 | oc->height = config_head->state.custom_mode.height; | ||
921 | oc->refresh_rate = | ||
922 | config_head->state.custom_mode.refresh / 1000.f; | ||
923 | } | 609 | } |
924 | oc->x = config_head->state.x; | 610 | } |
925 | oc->y = config_head->state.y; | 611 | |
926 | oc->transform = config_head->state.transform; | 612 | bool ok = apply_output_configs(configs, configs_len, test_only); |
927 | oc->scale = config_head->state.scale; | 613 | for (size_t idx = 0; idx < configs_len; idx++) { |
614 | struct matched_output_config *cfg = &configs[idx]; | ||
928 | 615 | ||
929 | if (test_only) { | 616 | // Only store new configs for successful non-test commits. Old configs, |
930 | ok &= test_output_config(oc, output); | 617 | // test-only and failed commits just get freed. |
618 | bool store_config = false; | ||
619 | if (!test_only && ok) { | ||
620 | struct wlr_output_configuration_head_v1 *config_head; | ||
621 | wl_list_for_each(config_head, &config->heads, link) { | ||
622 | if (config_head->state.output == cfg->output->wlr_output) { | ||
623 | store_config = true; | ||
624 | break; | ||
625 | } | ||
626 | } | ||
627 | } | ||
628 | if (store_config) { | ||
629 | store_output_config(cfg->config); | ||
931 | } else { | 630 | } else { |
932 | oc = store_output_config(oc); | 631 | free_output_config(cfg->config); |
933 | ok &= apply_output_config(oc, output); | ||
934 | } | 632 | } |
935 | } | 633 | } |
634 | free(configs); | ||
936 | 635 | ||
937 | if (ok) { | 636 | if (ok) { |
938 | wlr_output_configuration_v1_send_succeeded(config); | 637 | wlr_output_configuration_v1_send_succeeded(config); |
@@ -970,12 +669,12 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, | |||
970 | struct output_config *oc = new_output_config(output->wlr_output->name); | 669 | struct output_config *oc = new_output_config(output->wlr_output->name); |
971 | switch (event->mode) { | 670 | switch (event->mode) { |
972 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: | 671 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: |
973 | oc->dpms_state = DPMS_OFF; | 672 | oc->power = 0; |
974 | break; | 673 | break; |
975 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: | 674 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: |
976 | oc->dpms_state = DPMS_ON; | 675 | oc->power = 1; |
977 | break; | 676 | break; |
978 | } | 677 | } |
979 | oc = store_output_config(oc); | 678 | store_output_config(oc); |
980 | apply_output_config(oc, output); | 679 | apply_all_output_configs(); |
981 | } | 680 | } |
diff --git a/sway/desktop/render.c b/sway/desktop/render.c deleted file mode 100644 index a5bd8a5f..00000000 --- a/sway/desktop/render.c +++ /dev/null | |||
@@ -1,1160 +0,0 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | ||
3 | #include <GLES2/gl2.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <strings.h> | ||
6 | #include <time.h> | ||
7 | #include <wayland-server-core.h> | ||
8 | #include <wlr/render/gles2.h> | ||
9 | #include <wlr/render/wlr_renderer.h> | ||
10 | #include <wlr/types/wlr_buffer.h> | ||
11 | #include <wlr/types/wlr_matrix.h> | ||
12 | #include <wlr/types/wlr_output_damage.h> | ||
13 | #include <wlr/types/wlr_output_layout.h> | ||
14 | #include <wlr/types/wlr_output.h> | ||
15 | #include <wlr/types/wlr_surface.h> | ||
16 | #include <wlr/util/region.h> | ||
17 | #include "log.h" | ||
18 | #include "config.h" | ||
19 | #include "sway/config.h" | ||
20 | #include "sway/input/input-manager.h" | ||
21 | #include "sway/input/seat.h" | ||
22 | #include "sway/layers.h" | ||
23 | #include "sway/output.h" | ||
24 | #include "sway/server.h" | ||
25 | #include "sway/tree/arrange.h" | ||
26 | #include "sway/tree/container.h" | ||
27 | #include "sway/tree/root.h" | ||
28 | #include "sway/tree/view.h" | ||
29 | #include "sway/tree/workspace.h" | ||
30 | |||
31 | struct render_data { | ||
32 | pixman_region32_t *damage; | ||
33 | float alpha; | ||
34 | struct wlr_box *clip_box; | ||
35 | }; | ||
36 | |||
37 | /** | ||
38 | * Apply scale to a width or height. | ||
39 | * | ||
40 | * One does not simply multiply the width by the scale. We allow fractional | ||
41 | * scaling, which means the resulting scaled width might be a decimal. | ||
42 | * So we round it. | ||
43 | * | ||
44 | * But even this can produce undesirable results depending on the X or Y offset | ||
45 | * of the box. For example, with a scale of 1.5, a box with width=1 should not | ||
46 | * scale to 2px if its X coordinate is 1, because the X coordinate would have | ||
47 | * scaled to 2px. | ||
48 | */ | ||
49 | static int scale_length(int length, int offset, float scale) { | ||
50 | return round((offset + length) * scale) - round(offset * scale); | ||
51 | } | ||
52 | |||
53 | static void scissor_output(struct wlr_output *wlr_output, | ||
54 | pixman_box32_t *rect) { | ||
55 | struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); | ||
56 | assert(renderer); | ||
57 | |||
58 | struct wlr_box box = { | ||
59 | .x = rect->x1, | ||
60 | .y = rect->y1, | ||
61 | .width = rect->x2 - rect->x1, | ||
62 | .height = rect->y2 - rect->y1, | ||
63 | }; | ||
64 | |||
65 | int ow, oh; | ||
66 | wlr_output_transformed_resolution(wlr_output, &ow, &oh); | ||
67 | |||
68 | enum wl_output_transform transform = | ||
69 | wlr_output_transform_invert(wlr_output->transform); | ||
70 | wlr_box_transform(&box, &box, transform, ow, oh); | ||
71 | |||
72 | wlr_renderer_scissor(renderer, &box); | ||
73 | } | ||
74 | |||
75 | static void set_scale_filter(struct wlr_output *wlr_output, | ||
76 | struct wlr_texture *texture, enum scale_filter_mode scale_filter) { | ||
77 | if (!wlr_texture_is_gles2(texture)) { | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | struct wlr_gles2_texture_attribs attribs; | ||
82 | wlr_gles2_texture_get_attribs(texture, &attribs); | ||
83 | |||
84 | glBindTexture(attribs.target, attribs.tex); | ||
85 | |||
86 | switch (scale_filter) { | ||
87 | case SCALE_FILTER_LINEAR: | ||
88 | glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
89 | break; | ||
90 | case SCALE_FILTER_NEAREST: | ||
91 | glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
92 | break; | ||
93 | case SCALE_FILTER_DEFAULT: | ||
94 | case SCALE_FILTER_SMART: | ||
95 | assert(false); // unreachable | ||
96 | } | ||
97 | } | ||
98 | |||
99 | static void render_texture(struct wlr_output *wlr_output, | ||
100 | pixman_region32_t *output_damage, struct wlr_texture *texture, | ||
101 | const struct wlr_fbox *src_box, const struct wlr_box *dst_box, | ||
102 | const float matrix[static 9], float alpha) { | ||
103 | struct wlr_renderer *renderer = | ||
104 | wlr_backend_get_renderer(wlr_output->backend); | ||
105 | struct sway_output *output = wlr_output->data; | ||
106 | |||
107 | pixman_region32_t damage; | ||
108 | pixman_region32_init(&damage); | ||
109 | pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, | ||
110 | dst_box->width, dst_box->height); | ||
111 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
112 | bool damaged = pixman_region32_not_empty(&damage); | ||
113 | if (!damaged) { | ||
114 | goto damage_finish; | ||
115 | } | ||
116 | |||
117 | int nrects; | ||
118 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
119 | for (int i = 0; i < nrects; ++i) { | ||
120 | scissor_output(wlr_output, &rects[i]); | ||
121 | set_scale_filter(wlr_output, texture, output->scale_filter); | ||
122 | if (src_box != NULL) { | ||
123 | wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha); | ||
124 | } else { | ||
125 | wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | damage_finish: | ||
130 | pixman_region32_fini(&damage); | ||
131 | } | ||
132 | |||
133 | static void render_surface_iterator(struct sway_output *output, | ||
134 | struct sway_view *view, struct wlr_surface *surface, | ||
135 | struct wlr_box *_box, void *_data) { | ||
136 | struct render_data *data = _data; | ||
137 | struct wlr_output *wlr_output = output->wlr_output; | ||
138 | pixman_region32_t *output_damage = data->damage; | ||
139 | float alpha = data->alpha; | ||
140 | |||
141 | struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
142 | if (!texture) { | ||
143 | return; | ||
144 | } | ||
145 | |||
146 | struct wlr_fbox src_box; | ||
147 | wlr_surface_get_buffer_source_box(surface, &src_box); | ||
148 | |||
149 | struct wlr_box proj_box = *_box; | ||
150 | scale_box(&proj_box, wlr_output->scale); | ||
151 | |||
152 | float matrix[9]; | ||
153 | enum wl_output_transform transform = | ||
154 | wlr_output_transform_invert(surface->current.transform); | ||
155 | wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, | ||
156 | wlr_output->transform_matrix); | ||
157 | |||
158 | struct wlr_box dst_box = *_box; | ||
159 | struct wlr_box *clip_box = data->clip_box; | ||
160 | if (clip_box != NULL) { | ||
161 | dst_box.width = fmin(dst_box.width, clip_box->width); | ||
162 | dst_box.height = fmin(dst_box.height, clip_box->height); | ||
163 | } | ||
164 | scale_box(&dst_box, wlr_output->scale); | ||
165 | |||
166 | render_texture(wlr_output, output_damage, texture, | ||
167 | &src_box, &dst_box, matrix, alpha); | ||
168 | |||
169 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
170 | wlr_output); | ||
171 | } | ||
172 | |||
173 | static void render_layer_toplevel(struct sway_output *output, | ||
174 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
175 | struct render_data data = { | ||
176 | .damage = damage, | ||
177 | .alpha = 1.0f, | ||
178 | }; | ||
179 | output_layer_for_each_toplevel_surface(output, layer_surfaces, | ||
180 | render_surface_iterator, &data); | ||
181 | } | ||
182 | |||
183 | static void render_layer_popups(struct sway_output *output, | ||
184 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
185 | struct render_data data = { | ||
186 | .damage = damage, | ||
187 | .alpha = 1.0f, | ||
188 | }; | ||
189 | output_layer_for_each_popup_surface(output, layer_surfaces, | ||
190 | render_surface_iterator, &data); | ||
191 | } | ||
192 | |||
193 | #if HAVE_XWAYLAND | ||
194 | static void render_unmanaged(struct sway_output *output, | ||
195 | pixman_region32_t *damage, struct wl_list *unmanaged) { | ||
196 | struct render_data data = { | ||
197 | .damage = damage, | ||
198 | .alpha = 1.0f, | ||
199 | }; | ||
200 | output_unmanaged_for_each_surface(output, unmanaged, | ||
201 | render_surface_iterator, &data); | ||
202 | } | ||
203 | #endif | ||
204 | |||
205 | static void render_drag_icons(struct sway_output *output, | ||
206 | pixman_region32_t *damage, struct wl_list *drag_icons) { | ||
207 | struct render_data data = { | ||
208 | .damage = damage, | ||
209 | .alpha = 1.0f, | ||
210 | }; | ||
211 | output_drag_icons_for_each_surface(output, drag_icons, | ||
212 | render_surface_iterator, &data); | ||
213 | } | ||
214 | |||
215 | // _box.x and .y are expected to be layout-local | ||
216 | // _box.width and .height are expected to be output-buffer-local | ||
217 | void render_rect(struct sway_output *output, | ||
218 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
219 | float color[static 4]) { | ||
220 | struct wlr_output *wlr_output = output->wlr_output; | ||
221 | struct wlr_renderer *renderer = | ||
222 | wlr_backend_get_renderer(wlr_output->backend); | ||
223 | |||
224 | struct wlr_box box; | ||
225 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
226 | box.x -= output->lx * wlr_output->scale; | ||
227 | box.y -= output->ly * wlr_output->scale; | ||
228 | |||
229 | pixman_region32_t damage; | ||
230 | pixman_region32_init(&damage); | ||
231 | pixman_region32_union_rect(&damage, &damage, box.x, box.y, | ||
232 | box.width, box.height); | ||
233 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
234 | bool damaged = pixman_region32_not_empty(&damage); | ||
235 | if (!damaged) { | ||
236 | goto damage_finish; | ||
237 | } | ||
238 | |||
239 | int nrects; | ||
240 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
241 | for (int i = 0; i < nrects; ++i) { | ||
242 | scissor_output(wlr_output, &rects[i]); | ||
243 | wlr_render_rect(renderer, &box, color, | ||
244 | wlr_output->transform_matrix); | ||
245 | } | ||
246 | |||
247 | damage_finish: | ||
248 | pixman_region32_fini(&damage); | ||
249 | } | ||
250 | |||
251 | void premultiply_alpha(float color[4], float opacity) { | ||
252 | color[3] *= opacity; | ||
253 | color[0] *= color[3]; | ||
254 | color[1] *= color[3]; | ||
255 | color[2] *= color[3]; | ||
256 | } | ||
257 | |||
258 | static void render_view_toplevels(struct sway_view *view, | ||
259 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
260 | struct render_data data = { | ||
261 | .damage = damage, | ||
262 | .alpha = alpha, | ||
263 | }; | ||
264 | struct wlr_box clip_box; | ||
265 | if (!container_is_current_floating(view->container)) { | ||
266 | // As we pass the geometry offsets to the surface iterator, we will | ||
267 | // need to account for the offsets in the clip dimensions. | ||
268 | clip_box.width = view->container->current.content_width + view->geometry.x; | ||
269 | clip_box.height = view->container->current.content_height + view->geometry.y; | ||
270 | data.clip_box = &clip_box; | ||
271 | } | ||
272 | // Render all toplevels without descending into popups | ||
273 | double ox = view->container->surface_x - | ||
274 | output->lx - view->geometry.x; | ||
275 | double oy = view->container->surface_y - | ||
276 | output->ly - view->geometry.y; | ||
277 | output_surface_for_each_surface(output, view->surface, ox, oy, | ||
278 | render_surface_iterator, &data); | ||
279 | } | ||
280 | |||
281 | static void render_view_popups(struct sway_view *view, | ||
282 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
283 | struct render_data data = { | ||
284 | .damage = damage, | ||
285 | .alpha = alpha, | ||
286 | }; | ||
287 | output_view_for_each_popup_surface(output, view, | ||
288 | render_surface_iterator, &data); | ||
289 | } | ||
290 | |||
291 | static void render_saved_view(struct sway_view *view, | ||
292 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
293 | struct wlr_output *wlr_output = output->wlr_output; | ||
294 | |||
295 | if (wl_list_empty(&view->saved_buffers)) { | ||
296 | return; | ||
297 | } | ||
298 | |||
299 | bool floating = container_is_current_floating(view->container); | ||
300 | |||
301 | struct sway_saved_buffer *saved_buf; | ||
302 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
303 | if (!saved_buf->buffer->texture) { | ||
304 | continue; | ||
305 | } | ||
306 | |||
307 | struct wlr_box proj_box = { | ||
308 | .x = saved_buf->x - view->saved_geometry.x - output->lx, | ||
309 | .y = saved_buf->y - view->saved_geometry.y - output->ly, | ||
310 | .width = saved_buf->width, | ||
311 | .height = saved_buf->height, | ||
312 | }; | ||
313 | |||
314 | struct wlr_box output_box = { | ||
315 | .width = output->width, | ||
316 | .height = output->height, | ||
317 | }; | ||
318 | |||
319 | struct wlr_box intersection; | ||
320 | bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box); | ||
321 | if (!intersects) { | ||
322 | continue; | ||
323 | } | ||
324 | |||
325 | struct wlr_box dst_box = proj_box; | ||
326 | scale_box(&proj_box, wlr_output->scale); | ||
327 | |||
328 | float matrix[9]; | ||
329 | enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); | ||
330 | wlr_matrix_project_box(matrix, &proj_box, transform, 0, | ||
331 | wlr_output->transform_matrix); | ||
332 | |||
333 | if (!floating) { | ||
334 | dst_box.width = fmin(dst_box.width, | ||
335 | view->container->current.content_width - | ||
336 | (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x); | ||
337 | dst_box.height = fmin(dst_box.height, | ||
338 | view->container->current.content_height - | ||
339 | (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y); | ||
340 | } | ||
341 | scale_box(&dst_box, wlr_output->scale); | ||
342 | |||
343 | render_texture(wlr_output, damage, saved_buf->buffer->texture, | ||
344 | &saved_buf->source_box, &dst_box, matrix, alpha); | ||
345 | } | ||
346 | |||
347 | // FIXME: we should set the surface that this saved buffer originates from | ||
348 | // as sampled here. | ||
349 | // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 | ||
350 | } | ||
351 | |||
352 | /** | ||
353 | * Render a view's surface and left/bottom/right borders. | ||
354 | */ | ||
355 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | ||
356 | struct sway_container *con, struct border_colors *colors) { | ||
357 | struct sway_view *view = con->view; | ||
358 | if (!wl_list_empty(&view->saved_buffers)) { | ||
359 | render_saved_view(view, output, damage, view->container->alpha); | ||
360 | } else if (view->surface) { | ||
361 | render_view_toplevels(view, output, damage, view->container->alpha); | ||
362 | } | ||
363 | |||
364 | if (con->current.border == B_NONE || con->current.border == B_CSD) { | ||
365 | return; | ||
366 | } | ||
367 | |||
368 | struct wlr_box box; | ||
369 | float output_scale = output->wlr_output->scale; | ||
370 | float color[4]; | ||
371 | struct sway_container_state *state = &con->current; | ||
372 | |||
373 | if (state->border_left) { | ||
374 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
375 | premultiply_alpha(color, con->alpha); | ||
376 | box.x = floor(state->x); | ||
377 | box.y = floor(state->content_y); | ||
378 | box.width = state->border_thickness; | ||
379 | box.height = state->content_height; | ||
380 | scale_box(&box, output_scale); | ||
381 | render_rect(output, damage, &box, color); | ||
382 | } | ||
383 | |||
384 | list_t *siblings = container_get_current_siblings(con); | ||
385 | enum sway_container_layout layout = | ||
386 | container_current_parent_layout(con); | ||
387 | |||
388 | if (state->border_right) { | ||
389 | if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) { | ||
390 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
391 | } else { | ||
392 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
393 | } | ||
394 | premultiply_alpha(color, con->alpha); | ||
395 | box.x = floor(state->content_x + state->content_width); | ||
396 | box.y = floor(state->content_y); | ||
397 | box.width = state->border_thickness; | ||
398 | box.height = state->content_height; | ||
399 | scale_box(&box, output_scale); | ||
400 | render_rect(output, damage, &box, color); | ||
401 | } | ||
402 | |||
403 | if (state->border_bottom) { | ||
404 | if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) { | ||
405 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
406 | } else { | ||
407 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
408 | } | ||
409 | premultiply_alpha(color, con->alpha); | ||
410 | box.x = floor(state->x); | ||
411 | box.y = floor(state->content_y + state->content_height); | ||
412 | box.width = state->width; | ||
413 | box.height = state->border_thickness; | ||
414 | scale_box(&box, output_scale); | ||
415 | render_rect(output, damage, &box, color); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | /** | ||
420 | * Render a titlebar. | ||
421 | * | ||
422 | * Care must be taken not to render over the same pixel multiple times, | ||
423 | * otherwise the colors will be incorrect when using opacity. | ||
424 | * | ||
425 | * The height is: 1px border, 3px padding, font height, 3px padding, 1px border | ||
426 | * The left side is: 1px border, 2px padding, title | ||
427 | */ | ||
428 | static void render_titlebar(struct sway_output *output, | ||
429 | pixman_region32_t *output_damage, struct sway_container *con, | ||
430 | int x, int y, int width, | ||
431 | struct border_colors *colors, struct wlr_texture *title_texture, | ||
432 | struct wlr_texture *marks_texture) { | ||
433 | struct wlr_box box; | ||
434 | float color[4]; | ||
435 | float output_scale = output->wlr_output->scale; | ||
436 | double output_x = output->lx; | ||
437 | double output_y = output->ly; | ||
438 | int titlebar_border_thickness = config->titlebar_border_thickness; | ||
439 | int titlebar_h_padding = config->titlebar_h_padding; | ||
440 | int titlebar_v_padding = config->titlebar_v_padding; | ||
441 | enum alignment title_align = config->title_align; | ||
442 | |||
443 | // Single pixel bar above title | ||
444 | memcpy(&color, colors->border, sizeof(float) * 4); | ||
445 | premultiply_alpha(color, con->alpha); | ||
446 | box.x = x; | ||
447 | box.y = y; | ||
448 | box.width = width; | ||
449 | box.height = titlebar_border_thickness; | ||
450 | scale_box(&box, output_scale); | ||
451 | render_rect(output, output_damage, &box, color); | ||
452 | |||
453 | // Single pixel bar below title | ||
454 | box.x = x; | ||
455 | box.y = y + container_titlebar_height() - titlebar_border_thickness; | ||
456 | box.width = width; | ||
457 | box.height = titlebar_border_thickness; | ||
458 | scale_box(&box, output_scale); | ||
459 | render_rect(output, output_damage, &box, color); | ||
460 | |||
461 | // Single pixel left edge | ||
462 | box.x = x; | ||
463 | box.y = y + titlebar_border_thickness; | ||
464 | box.width = titlebar_border_thickness; | ||
465 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
466 | scale_box(&box, output_scale); | ||
467 | render_rect(output, output_damage, &box, color); | ||
468 | |||
469 | // Single pixel right edge | ||
470 | box.x = x + width - titlebar_border_thickness; | ||
471 | box.y = y + titlebar_border_thickness; | ||
472 | box.width = titlebar_border_thickness; | ||
473 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
474 | scale_box(&box, output_scale); | ||
475 | render_rect(output, output_damage, &box, color); | ||
476 | |||
477 | int inner_x = x - output_x + titlebar_h_padding; | ||
478 | int bg_y = y + titlebar_border_thickness; | ||
479 | size_t inner_width = width - titlebar_h_padding * 2; | ||
480 | |||
481 | // output-buffer local | ||
482 | int ob_inner_x = round(inner_x * output_scale); | ||
483 | int ob_inner_width = scale_length(inner_width, inner_x, output_scale); | ||
484 | int ob_bg_height = scale_length( | ||
485 | (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
486 | config->font_height, bg_y, output_scale); | ||
487 | |||
488 | // Marks | ||
489 | int ob_marks_x = 0; // output-buffer-local | ||
490 | int ob_marks_width = 0; // output-buffer-local | ||
491 | if (config->show_marks && marks_texture) { | ||
492 | struct wlr_box texture_box = { | ||
493 | .width = marks_texture->width, | ||
494 | .height = marks_texture->height, | ||
495 | }; | ||
496 | ob_marks_width = texture_box.width; | ||
497 | |||
498 | // The marks texture might be shorter than the config->font_height, in | ||
499 | // which case we need to pad it as evenly as possible above and below. | ||
500 | int ob_padding_total = ob_bg_height - texture_box.height; | ||
501 | int ob_padding_above = floor(ob_padding_total / 2.0); | ||
502 | int ob_padding_below = ceil(ob_padding_total / 2.0); | ||
503 | |||
504 | // Render texture. If the title is on the right, the marks will be on | ||
505 | // the left. Otherwise, they will be on the right. | ||
506 | if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) { | ||
507 | texture_box.x = ob_inner_x; | ||
508 | } else { | ||
509 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
510 | } | ||
511 | ob_marks_x = texture_box.x; | ||
512 | |||
513 | texture_box.y = round((bg_y - output_y) * output_scale) + | ||
514 | ob_padding_above; | ||
515 | |||
516 | float matrix[9]; | ||
517 | wlr_matrix_project_box(matrix, &texture_box, | ||
518 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
519 | 0.0, output->wlr_output->transform_matrix); | ||
520 | |||
521 | if (ob_inner_width < texture_box.width) { | ||
522 | texture_box.width = ob_inner_width; | ||
523 | } | ||
524 | render_texture(output->wlr_output, output_damage, marks_texture, | ||
525 | NULL, &texture_box, matrix, con->alpha); | ||
526 | |||
527 | // Padding above | ||
528 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
529 | premultiply_alpha(color, con->alpha); | ||
530 | box.x = texture_box.x + round(output_x * output_scale); | ||
531 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
532 | box.width = texture_box.width; | ||
533 | box.height = ob_padding_above; | ||
534 | render_rect(output, output_damage, &box, color); | ||
535 | |||
536 | // Padding below | ||
537 | box.y += ob_padding_above + texture_box.height; | ||
538 | box.height = ob_padding_below; | ||
539 | render_rect(output, output_damage, &box, color); | ||
540 | } | ||
541 | |||
542 | // Title text | ||
543 | int ob_title_x = 0; // output-buffer-local | ||
544 | int ob_title_width = 0; // output-buffer-local | ||
545 | if (title_texture) { | ||
546 | struct wlr_box texture_box = { | ||
547 | .width = title_texture->width, | ||
548 | .height = title_texture->height, | ||
549 | }; | ||
550 | |||
551 | // The effective output may be NULL when con is not on any output. | ||
552 | // This can happen because we render all children of containers, | ||
553 | // even those that are out of the bounds of any output. | ||
554 | struct sway_output *effective = container_get_effective_output(con); | ||
555 | float title_scale = effective ? effective->wlr_output->scale : output_scale; | ||
556 | texture_box.width = texture_box.width * output_scale / title_scale; | ||
557 | texture_box.height = texture_box.height * output_scale / title_scale; | ||
558 | ob_title_width = texture_box.width; | ||
559 | |||
560 | // The title texture might be shorter than the config->font_height, | ||
561 | // in which case we need to pad it above and below. | ||
562 | int ob_padding_above = round((config->font_baseline - | ||
563 | con->title_baseline + titlebar_v_padding - | ||
564 | titlebar_border_thickness) * output_scale); | ||
565 | int ob_padding_below = ob_bg_height - ob_padding_above - | ||
566 | texture_box.height; | ||
567 | |||
568 | // Render texture | ||
569 | if (texture_box.width > ob_inner_width - ob_marks_width) { | ||
570 | texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width) | ||
571 | ? ob_marks_x + ob_marks_width : ob_inner_x; | ||
572 | } else if (title_align == ALIGN_LEFT) { | ||
573 | texture_box.x = ob_inner_x; | ||
574 | } else if (title_align == ALIGN_CENTER) { | ||
575 | // If there are marks visible, center between the edge and marks. | ||
576 | // Otherwise, center in the inner area. | ||
577 | if (ob_marks_width) { | ||
578 | texture_box.x = (ob_inner_x + ob_marks_x) / 2 | ||
579 | - texture_box.width / 2; | ||
580 | } else { | ||
581 | texture_box.x = ob_inner_x + ob_inner_width / 2 | ||
582 | - texture_box.width / 2; | ||
583 | } | ||
584 | } else { | ||
585 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
586 | } | ||
587 | ob_title_x = texture_box.x; | ||
588 | |||
589 | texture_box.y = | ||
590 | round((bg_y - output_y) * output_scale) + ob_padding_above; | ||
591 | |||
592 | float matrix[9]; | ||
593 | wlr_matrix_project_box(matrix, &texture_box, | ||
594 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
595 | 0.0, output->wlr_output->transform_matrix); | ||
596 | |||
597 | if (ob_inner_width - ob_marks_width < texture_box.width) { | ||
598 | texture_box.width = ob_inner_width - ob_marks_width; | ||
599 | } | ||
600 | |||
601 | render_texture(output->wlr_output, output_damage, title_texture, | ||
602 | NULL, &texture_box, matrix, con->alpha); | ||
603 | |||
604 | // Padding above | ||
605 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
606 | premultiply_alpha(color, con->alpha); | ||
607 | box.x = texture_box.x + round(output_x * output_scale); | ||
608 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
609 | box.width = texture_box.width; | ||
610 | box.height = ob_padding_above; | ||
611 | render_rect(output, output_damage, &box, color); | ||
612 | |||
613 | // Padding below | ||
614 | box.y += ob_padding_above + texture_box.height; | ||
615 | box.height = ob_padding_below; | ||
616 | render_rect(output, output_damage, &box, color); | ||
617 | } | ||
618 | |||
619 | // Determine the left + right extends of the textures (output-buffer local) | ||
620 | int ob_left_x, ob_left_width, ob_right_x, ob_right_width; | ||
621 | if (ob_title_width == 0 && ob_marks_width == 0) { | ||
622 | ob_left_x = ob_inner_x; | ||
623 | ob_left_width = 0; | ||
624 | ob_right_x = ob_inner_x; | ||
625 | ob_right_width = 0; | ||
626 | } else if (ob_title_x < ob_marks_x) { | ||
627 | ob_left_x = ob_title_x; | ||
628 | ob_left_width = ob_title_width; | ||
629 | ob_right_x = ob_marks_x; | ||
630 | ob_right_width = ob_marks_width; | ||
631 | } else { | ||
632 | ob_left_x = ob_marks_x; | ||
633 | ob_left_width = ob_marks_width; | ||
634 | ob_right_x = ob_title_x; | ||
635 | ob_right_width = ob_title_width; | ||
636 | } | ||
637 | if (ob_left_x < ob_inner_x) { | ||
638 | ob_left_x = ob_inner_x; | ||
639 | } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) { | ||
640 | ob_right_x = ob_left_x; | ||
641 | ob_right_width = ob_left_width; | ||
642 | } | ||
643 | |||
644 | // Filler between title and marks | ||
645 | box.width = ob_right_x - ob_left_x - ob_left_width; | ||
646 | if (box.width > 0) { | ||
647 | box.x = ob_left_x + ob_left_width + round(output_x * output_scale); | ||
648 | box.y = round(bg_y * output_scale); | ||
649 | box.height = ob_bg_height; | ||
650 | render_rect(output, output_damage, &box, color); | ||
651 | } | ||
652 | |||
653 | // Padding on left side | ||
654 | box.x = x + titlebar_border_thickness; | ||
655 | box.y = y + titlebar_border_thickness; | ||
656 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
657 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
658 | config->font_height; | ||
659 | scale_box(&box, output_scale); | ||
660 | int left_x = ob_left_x + round(output_x * output_scale); | ||
661 | if (box.x + box.width < left_x) { | ||
662 | box.width += left_x - box.x - box.width; | ||
663 | } | ||
664 | render_rect(output, output_damage, &box, color); | ||
665 | |||
666 | // Padding on right side | ||
667 | box.x = x + width - titlebar_h_padding; | ||
668 | box.y = y + titlebar_border_thickness; | ||
669 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
670 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
671 | config->font_height; | ||
672 | scale_box(&box, output_scale); | ||
673 | int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale); | ||
674 | if (right_rx < box.x) { | ||
675 | box.width += box.x - right_rx; | ||
676 | box.x = right_rx; | ||
677 | } | ||
678 | render_rect(output, output_damage, &box, color); | ||
679 | } | ||
680 | |||
681 | /** | ||
682 | * Render the top border line for a view using "border pixel". | ||
683 | */ | ||
684 | static void render_top_border(struct sway_output *output, | ||
685 | pixman_region32_t *output_damage, struct sway_container *con, | ||
686 | struct border_colors *colors) { | ||
687 | struct sway_container_state *state = &con->current; | ||
688 | if (!state->border_top) { | ||
689 | return; | ||
690 | } | ||
691 | struct wlr_box box; | ||
692 | float color[4]; | ||
693 | float output_scale = output->wlr_output->scale; | ||
694 | |||
695 | // Child border - top edge | ||
696 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
697 | premultiply_alpha(color, con->alpha); | ||
698 | box.x = floor(state->x); | ||
699 | box.y = floor(state->y); | ||
700 | box.width = state->width; | ||
701 | box.height = state->border_thickness; | ||
702 | scale_box(&box, output_scale); | ||
703 | render_rect(output, output_damage, &box, color); | ||
704 | } | ||
705 | |||
706 | struct parent_data { | ||
707 | enum sway_container_layout layout; | ||
708 | struct wlr_box box; | ||
709 | list_t *children; | ||
710 | bool focused; | ||
711 | struct sway_container *active_child; | ||
712 | }; | ||
713 | |||
714 | static void render_container(struct sway_output *output, | ||
715 | pixman_region32_t *damage, struct sway_container *con, bool parent_focused); | ||
716 | |||
717 | /** | ||
718 | * Render a container's children using a L_HORIZ or L_VERT layout. | ||
719 | * | ||
720 | * Wrap child views in borders and leave child containers borderless because | ||
721 | * they'll apply their own borders to their children. | ||
722 | */ | ||
723 | static void render_containers_linear(struct sway_output *output, | ||
724 | pixman_region32_t *damage, struct parent_data *parent) { | ||
725 | for (int i = 0; i < parent->children->length; ++i) { | ||
726 | struct sway_container *child = parent->children->items[i]; | ||
727 | |||
728 | if (child->view) { | ||
729 | struct sway_view *view = child->view; | ||
730 | struct border_colors *colors; | ||
731 | struct wlr_texture *title_texture; | ||
732 | struct wlr_texture *marks_texture; | ||
733 | struct sway_container_state *state = &child->current; | ||
734 | |||
735 | if (view_is_urgent(view)) { | ||
736 | colors = &config->border_colors.urgent; | ||
737 | title_texture = child->title_urgent; | ||
738 | marks_texture = child->marks_urgent; | ||
739 | } else if (state->focused || parent->focused) { | ||
740 | colors = &config->border_colors.focused; | ||
741 | title_texture = child->title_focused; | ||
742 | marks_texture = child->marks_focused; | ||
743 | } else if (child == parent->active_child) { | ||
744 | colors = &config->border_colors.focused_inactive; | ||
745 | title_texture = child->title_focused_inactive; | ||
746 | marks_texture = child->marks_focused_inactive; | ||
747 | } else { | ||
748 | colors = &config->border_colors.unfocused; | ||
749 | title_texture = child->title_unfocused; | ||
750 | marks_texture = child->marks_unfocused; | ||
751 | } | ||
752 | |||
753 | if (state->border == B_NORMAL) { | ||
754 | render_titlebar(output, damage, child, floor(state->x), | ||
755 | floor(state->y), state->width, colors, | ||
756 | title_texture, marks_texture); | ||
757 | } else if (state->border == B_PIXEL) { | ||
758 | render_top_border(output, damage, child, colors); | ||
759 | } | ||
760 | render_view(output, damage, child, colors); | ||
761 | } else { | ||
762 | render_container(output, damage, child, | ||
763 | parent->focused || child->current.focused); | ||
764 | } | ||
765 | } | ||
766 | } | ||
767 | |||
768 | /** | ||
769 | * Render a container's children using the L_TABBED layout. | ||
770 | */ | ||
771 | static void render_containers_tabbed(struct sway_output *output, | ||
772 | pixman_region32_t *damage, struct parent_data *parent) { | ||
773 | if (!parent->children->length) { | ||
774 | return; | ||
775 | } | ||
776 | struct sway_container *current = parent->active_child; | ||
777 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
778 | int tab_width = parent->box.width / parent->children->length; | ||
779 | |||
780 | // Render tabs | ||
781 | for (int i = 0; i < parent->children->length; ++i) { | ||
782 | struct sway_container *child = parent->children->items[i]; | ||
783 | struct sway_view *view = child->view; | ||
784 | struct sway_container_state *cstate = &child->current; | ||
785 | struct border_colors *colors; | ||
786 | struct wlr_texture *title_texture; | ||
787 | struct wlr_texture *marks_texture; | ||
788 | bool urgent = view ? | ||
789 | view_is_urgent(view) : container_has_urgent_child(child); | ||
790 | |||
791 | if (urgent) { | ||
792 | colors = &config->border_colors.urgent; | ||
793 | title_texture = child->title_urgent; | ||
794 | marks_texture = child->marks_urgent; | ||
795 | } else if (cstate->focused || parent->focused) { | ||
796 | colors = &config->border_colors.focused; | ||
797 | title_texture = child->title_focused; | ||
798 | marks_texture = child->marks_focused; | ||
799 | } else if (child == parent->active_child) { | ||
800 | colors = &config->border_colors.focused_inactive; | ||
801 | title_texture = child->title_focused_inactive; | ||
802 | marks_texture = child->marks_focused_inactive; | ||
803 | } else { | ||
804 | colors = &config->border_colors.unfocused; | ||
805 | title_texture = child->title_unfocused; | ||
806 | marks_texture = child->marks_unfocused; | ||
807 | } | ||
808 | |||
809 | int x = floor(cstate->x + tab_width * i); | ||
810 | |||
811 | // Make last tab use the remaining width of the parent | ||
812 | if (i == parent->children->length - 1) { | ||
813 | tab_width = parent->box.width - tab_width * i; | ||
814 | } | ||
815 | |||
816 | render_titlebar(output, damage, child, x, parent->box.y, tab_width, | ||
817 | colors, title_texture, marks_texture); | ||
818 | |||
819 | if (child == current) { | ||
820 | current_colors = colors; | ||
821 | } | ||
822 | } | ||
823 | |||
824 | // Render surface and left/right/bottom borders | ||
825 | if (current->view) { | ||
826 | render_view(output, damage, current, current_colors); | ||
827 | } else { | ||
828 | render_container(output, damage, current, | ||
829 | parent->focused || current->current.focused); | ||
830 | } | ||
831 | } | ||
832 | |||
833 | /** | ||
834 | * Render a container's children using the L_STACKED layout. | ||
835 | */ | ||
836 | static void render_containers_stacked(struct sway_output *output, | ||
837 | pixman_region32_t *damage, struct parent_data *parent) { | ||
838 | if (!parent->children->length) { | ||
839 | return; | ||
840 | } | ||
841 | struct sway_container *current = parent->active_child; | ||
842 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
843 | size_t titlebar_height = container_titlebar_height(); | ||
844 | |||
845 | // Render titles | ||
846 | for (int i = 0; i < parent->children->length; ++i) { | ||
847 | struct sway_container *child = parent->children->items[i]; | ||
848 | struct sway_view *view = child->view; | ||
849 | struct sway_container_state *cstate = &child->current; | ||
850 | struct border_colors *colors; | ||
851 | struct wlr_texture *title_texture; | ||
852 | struct wlr_texture *marks_texture; | ||
853 | bool urgent = view ? | ||
854 | view_is_urgent(view) : container_has_urgent_child(child); | ||
855 | |||
856 | if (urgent) { | ||
857 | colors = &config->border_colors.urgent; | ||
858 | title_texture = child->title_urgent; | ||
859 | marks_texture = child->marks_urgent; | ||
860 | } else if (cstate->focused || parent->focused) { | ||
861 | colors = &config->border_colors.focused; | ||
862 | title_texture = child->title_focused; | ||
863 | marks_texture = child->marks_focused; | ||
864 | } else if (child == parent->active_child) { | ||
865 | colors = &config->border_colors.focused_inactive; | ||
866 | title_texture = child->title_focused_inactive; | ||
867 | marks_texture = child->marks_focused_inactive; | ||
868 | } else { | ||
869 | colors = &config->border_colors.unfocused; | ||
870 | title_texture = child->title_unfocused; | ||
871 | marks_texture = child->marks_unfocused; | ||
872 | } | ||
873 | |||
874 | int y = parent->box.y + titlebar_height * i; | ||
875 | render_titlebar(output, damage, child, parent->box.x, y, | ||
876 | parent->box.width, colors, title_texture, marks_texture); | ||
877 | |||
878 | if (child == current) { | ||
879 | current_colors = colors; | ||
880 | } | ||
881 | } | ||
882 | |||
883 | // Render surface and left/right/bottom borders | ||
884 | if (current->view) { | ||
885 | render_view(output, damage, current, current_colors); | ||
886 | } else { | ||
887 | render_container(output, damage, current, | ||
888 | parent->focused || current->current.focused); | ||
889 | } | ||
890 | } | ||
891 | |||
892 | static void render_containers(struct sway_output *output, | ||
893 | pixman_region32_t *damage, struct parent_data *parent) { | ||
894 | if (config->hide_lone_tab && parent->children->length == 1) { | ||
895 | struct sway_container *child = parent->children->items[0]; | ||
896 | if (child->view) { | ||
897 | render_containers_linear(output,damage, parent); | ||
898 | return; | ||
899 | } | ||
900 | } | ||
901 | |||
902 | switch (parent->layout) { | ||
903 | case L_NONE: | ||
904 | case L_HORIZ: | ||
905 | case L_VERT: | ||
906 | render_containers_linear(output, damage, parent); | ||
907 | break; | ||
908 | case L_STACKED: | ||
909 | render_containers_stacked(output, damage, parent); | ||
910 | break; | ||
911 | case L_TABBED: | ||
912 | render_containers_tabbed(output, damage, parent); | ||
913 | break; | ||
914 | } | ||
915 | } | ||
916 | |||
917 | static void render_container(struct sway_output *output, | ||
918 | pixman_region32_t *damage, struct sway_container *con, bool focused) { | ||
919 | struct parent_data data = { | ||
920 | .layout = con->current.layout, | ||
921 | .box = { | ||
922 | .x = floor(con->current.x), | ||
923 | .y = floor(con->current.y), | ||
924 | .width = con->current.width, | ||
925 | .height = con->current.height, | ||
926 | }, | ||
927 | .children = con->current.children, | ||
928 | .focused = focused, | ||
929 | .active_child = con->current.focused_inactive_child, | ||
930 | }; | ||
931 | render_containers(output, damage, &data); | ||
932 | } | ||
933 | |||
934 | static void render_workspace(struct sway_output *output, | ||
935 | pixman_region32_t *damage, struct sway_workspace *ws, bool focused) { | ||
936 | struct parent_data data = { | ||
937 | .layout = ws->current.layout, | ||
938 | .box = { | ||
939 | .x = floor(ws->current.x), | ||
940 | .y = floor(ws->current.y), | ||
941 | .width = ws->current.width, | ||
942 | .height = ws->current.height, | ||
943 | }, | ||
944 | .children = ws->current.tiling, | ||
945 | .focused = focused, | ||
946 | .active_child = ws->current.focused_inactive_child, | ||
947 | }; | ||
948 | render_containers(output, damage, &data); | ||
949 | } | ||
950 | |||
951 | static void render_floating_container(struct sway_output *soutput, | ||
952 | pixman_region32_t *damage, struct sway_container *con) { | ||
953 | if (con->view) { | ||
954 | struct sway_view *view = con->view; | ||
955 | struct border_colors *colors; | ||
956 | struct wlr_texture *title_texture; | ||
957 | struct wlr_texture *marks_texture; | ||
958 | |||
959 | if (view_is_urgent(view)) { | ||
960 | colors = &config->border_colors.urgent; | ||
961 | title_texture = con->title_urgent; | ||
962 | marks_texture = con->marks_urgent; | ||
963 | } else if (con->current.focused) { | ||
964 | colors = &config->border_colors.focused; | ||
965 | title_texture = con->title_focused; | ||
966 | marks_texture = con->marks_focused; | ||
967 | } else { | ||
968 | colors = &config->border_colors.unfocused; | ||
969 | title_texture = con->title_unfocused; | ||
970 | marks_texture = con->marks_unfocused; | ||
971 | } | ||
972 | |||
973 | if (con->current.border == B_NORMAL) { | ||
974 | render_titlebar(soutput, damage, con, floor(con->current.x), | ||
975 | floor(con->current.y), con->current.width, colors, | ||
976 | title_texture, marks_texture); | ||
977 | } else if (con->current.border == B_PIXEL) { | ||
978 | render_top_border(soutput, damage, con, colors); | ||
979 | } | ||
980 | render_view(soutput, damage, con, colors); | ||
981 | } else { | ||
982 | render_container(soutput, damage, con, con->current.focused); | ||
983 | } | ||
984 | } | ||
985 | |||
986 | static void render_floating(struct sway_output *soutput, | ||
987 | pixman_region32_t *damage) { | ||
988 | for (int i = 0; i < root->outputs->length; ++i) { | ||
989 | struct sway_output *output = root->outputs->items[i]; | ||
990 | for (int j = 0; j < output->current.workspaces->length; ++j) { | ||
991 | struct sway_workspace *ws = output->current.workspaces->items[j]; | ||
992 | if (!workspace_is_visible(ws)) { | ||
993 | continue; | ||
994 | } | ||
995 | for (int k = 0; k < ws->current.floating->length; ++k) { | ||
996 | struct sway_container *floater = ws->current.floating->items[k]; | ||
997 | if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { | ||
998 | continue; | ||
999 | } | ||
1000 | render_floating_container(soutput, damage, floater); | ||
1001 | } | ||
1002 | } | ||
1003 | } | ||
1004 | } | ||
1005 | |||
1006 | static void render_seatops(struct sway_output *output, | ||
1007 | pixman_region32_t *damage) { | ||
1008 | struct sway_seat *seat; | ||
1009 | wl_list_for_each(seat, &server.input->seats, link) { | ||
1010 | seatop_render(seat, output, damage); | ||
1011 | } | ||
1012 | } | ||
1013 | |||
1014 | void output_render(struct sway_output *output, struct timespec *when, | ||
1015 | pixman_region32_t *damage) { | ||
1016 | struct wlr_output *wlr_output = output->wlr_output; | ||
1017 | |||
1018 | struct wlr_renderer *renderer = | ||
1019 | wlr_backend_get_renderer(wlr_output->backend); | ||
1020 | if (!sway_assert(renderer != NULL, | ||
1021 | "expected the output backend to have a renderer")) { | ||
1022 | return; | ||
1023 | } | ||
1024 | |||
1025 | struct sway_workspace *workspace = output->current.active_workspace; | ||
1026 | if (workspace == NULL) { | ||
1027 | return; | ||
1028 | } | ||
1029 | |||
1030 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
1031 | if (!fullscreen_con) { | ||
1032 | fullscreen_con = workspace->current.fullscreen; | ||
1033 | } | ||
1034 | |||
1035 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
1036 | |||
1037 | if (!pixman_region32_not_empty(damage)) { | ||
1038 | // Output isn't damaged but needs buffer swap | ||
1039 | goto renderer_end; | ||
1040 | } | ||
1041 | |||
1042 | if (debug.damage == DAMAGE_HIGHLIGHT) { | ||
1043 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | ||
1044 | } else if (debug.damage == DAMAGE_RERENDER) { | ||
1045 | int width, height; | ||
1046 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1047 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
1048 | } | ||
1049 | |||
1050 | if (output_has_opaque_overlay_layer_surface(output)) { | ||
1051 | goto render_overlay; | ||
1052 | } | ||
1053 | |||
1054 | if (fullscreen_con) { | ||
1055 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
1056 | |||
1057 | int nrects; | ||
1058 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1059 | for (int i = 0; i < nrects; ++i) { | ||
1060 | scissor_output(wlr_output, &rects[i]); | ||
1061 | wlr_renderer_clear(renderer, clear_color); | ||
1062 | } | ||
1063 | |||
1064 | if (fullscreen_con->view) { | ||
1065 | if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { | ||
1066 | render_saved_view(fullscreen_con->view, output, damage, 1.0f); | ||
1067 | } else if (fullscreen_con->view->surface) { | ||
1068 | render_view_toplevels(fullscreen_con->view, | ||
1069 | output, damage, 1.0f); | ||
1070 | } | ||
1071 | } else { | ||
1072 | render_container(output, damage, fullscreen_con, | ||
1073 | fullscreen_con->current.focused); | ||
1074 | } | ||
1075 | |||
1076 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
1077 | struct sway_container *floater = | ||
1078 | workspace->current.floating->items[i]; | ||
1079 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
1080 | render_floating_container(output, damage, floater); | ||
1081 | } | ||
1082 | } | ||
1083 | #if HAVE_XWAYLAND | ||
1084 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1085 | #endif | ||
1086 | } else { | ||
1087 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | ||
1088 | |||
1089 | int nrects; | ||
1090 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1091 | for (int i = 0; i < nrects; ++i) { | ||
1092 | scissor_output(wlr_output, &rects[i]); | ||
1093 | wlr_renderer_clear(renderer, clear_color); | ||
1094 | } | ||
1095 | |||
1096 | render_layer_toplevel(output, damage, | ||
1097 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1098 | render_layer_toplevel(output, damage, | ||
1099 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1100 | |||
1101 | render_workspace(output, damage, workspace, workspace->current.focused); | ||
1102 | render_floating(output, damage); | ||
1103 | #if HAVE_XWAYLAND | ||
1104 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1105 | #endif | ||
1106 | render_layer_toplevel(output, damage, | ||
1107 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1108 | |||
1109 | render_layer_popups(output, damage, | ||
1110 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1111 | render_layer_popups(output, damage, | ||
1112 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1113 | render_layer_popups(output, damage, | ||
1114 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1115 | } | ||
1116 | |||
1117 | render_seatops(output, damage); | ||
1118 | |||
1119 | struct sway_seat *seat = input_manager_current_seat(); | ||
1120 | struct sway_container *focus = seat_get_focused_container(seat); | ||
1121 | if (focus && focus->view) { | ||
1122 | render_view_popups(focus->view, output, damage, focus->alpha); | ||
1123 | } | ||
1124 | |||
1125 | render_overlay: | ||
1126 | render_layer_toplevel(output, damage, | ||
1127 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1128 | render_layer_popups(output, damage, | ||
1129 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1130 | render_drag_icons(output, damage, &root->drag_icons); | ||
1131 | |||
1132 | renderer_end: | ||
1133 | wlr_renderer_scissor(renderer, NULL); | ||
1134 | wlr_output_render_software_cursors(wlr_output, damage); | ||
1135 | wlr_renderer_end(renderer); | ||
1136 | |||
1137 | int width, height; | ||
1138 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1139 | |||
1140 | pixman_region32_t frame_damage; | ||
1141 | pixman_region32_init(&frame_damage); | ||
1142 | |||
1143 | enum wl_output_transform transform = | ||
1144 | wlr_output_transform_invert(wlr_output->transform); | ||
1145 | wlr_region_transform(&frame_damage, &output->damage->current, | ||
1146 | transform, width, height); | ||
1147 | |||
1148 | if (debug.damage == DAMAGE_HIGHLIGHT) { | ||
1149 | pixman_region32_union_rect(&frame_damage, &frame_damage, | ||
1150 | 0, 0, wlr_output->width, wlr_output->height); | ||
1151 | } | ||
1152 | |||
1153 | wlr_output_set_damage(wlr_output, &frame_damage); | ||
1154 | pixman_region32_fini(&frame_damage); | ||
1155 | |||
1156 | if (!wlr_output_commit(wlr_output)) { | ||
1157 | return; | ||
1158 | } | ||
1159 | output->last_frame = *when; | ||
1160 | } | ||
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c deleted file mode 100644 index 767b2045..00000000 --- a/sway/desktop/surface.c +++ /dev/null | |||
@@ -1,46 +0,0 @@ | |||
1 | #define _POSIX_C_SOURCE 200112L | ||
2 | #include <stdlib.h> | ||
3 | #include <time.h> | ||
4 | #include <wlr/types/wlr_surface.h> | ||
5 | #include "sway/server.h" | ||
6 | #include "sway/surface.h" | ||
7 | |||
8 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
9 | struct sway_surface *surface = wl_container_of(listener, surface, destroy); | ||
10 | |||
11 | surface->wlr_surface->data = NULL; | ||
12 | wl_list_remove(&surface->destroy.link); | ||
13 | |||
14 | if (surface->frame_done_timer) { | ||
15 | wl_event_source_remove(surface->frame_done_timer); | ||
16 | } | ||
17 | |||
18 | free(surface); | ||
19 | } | ||
20 | |||
21 | static int surface_frame_done_timer_handler(void *data) { | ||
22 | struct sway_surface *surface = data; | ||
23 | |||
24 | struct timespec now; | ||
25 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
26 | wlr_surface_send_frame_done(surface->wlr_surface, &now); | ||
27 | |||
28 | return 0; | ||
29 | } | ||
30 | |||
31 | void handle_compositor_new_surface(struct wl_listener *listener, void *data) { | ||
32 | struct wlr_surface *wlr_surface = data; | ||
33 | |||
34 | struct sway_surface *surface = calloc(1, sizeof(struct sway_surface)); | ||
35 | surface->wlr_surface = wlr_surface; | ||
36 | wlr_surface->data = surface; | ||
37 | |||
38 | surface->destroy.notify = handle_destroy; | ||
39 | wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); | ||
40 | |||
41 | surface->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, | ||
42 | surface_frame_done_timer_handler, surface); | ||
43 | if (!surface->frame_done_timer) { | ||
44 | wl_resource_post_no_memory(wlr_surface->resource); | ||
45 | } | ||
46 | } | ||
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index b1f3fb32..042141ab 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -1,11 +1,10 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <time.h> | 4 | #include <time.h> |
6 | #include <wlr/types/wlr_buffer.h> | 5 | #include <wlr/types/wlr_buffer.h> |
7 | #include "sway/config.h" | 6 | #include "sway/config.h" |
8 | #include "sway/desktop.h" | 7 | #include "sway/scene_descriptor.h" |
9 | #include "sway/desktop/idle_inhibit_v1.h" | 8 | #include "sway/desktop/idle_inhibit_v1.h" |
10 | #include "sway/desktop/transaction.h" | 9 | #include "sway/desktop/transaction.h" |
11 | #include "sway/input/cursor.h" | 10 | #include "sway/input/cursor.h" |
@@ -214,39 +213,20 @@ static void transaction_add_node(struct sway_transaction *transaction, | |||
214 | 213 | ||
215 | static void apply_output_state(struct sway_output *output, | 214 | static void apply_output_state(struct sway_output *output, |
216 | struct sway_output_state *state) { | 215 | struct sway_output_state *state) { |
217 | output_damage_whole(output); | ||
218 | list_free(output->current.workspaces); | 216 | list_free(output->current.workspaces); |
219 | memcpy(&output->current, state, sizeof(struct sway_output_state)); | 217 | memcpy(&output->current, state, sizeof(struct sway_output_state)); |
220 | output_damage_whole(output); | ||
221 | } | 218 | } |
222 | 219 | ||
223 | static void apply_workspace_state(struct sway_workspace *ws, | 220 | static void apply_workspace_state(struct sway_workspace *ws, |
224 | struct sway_workspace_state *state) { | 221 | struct sway_workspace_state *state) { |
225 | output_damage_whole(ws->current.output); | ||
226 | list_free(ws->current.floating); | 222 | list_free(ws->current.floating); |
227 | list_free(ws->current.tiling); | 223 | list_free(ws->current.tiling); |
228 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); | 224 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); |
229 | output_damage_whole(ws->current.output); | ||
230 | } | 225 | } |
231 | 226 | ||
232 | static void apply_container_state(struct sway_container *container, | 227 | static void apply_container_state(struct sway_container *container, |
233 | struct sway_container_state *state) { | 228 | struct sway_container_state *state) { |
234 | struct sway_view *view = container->view; | 229 | struct sway_view *view = container->view; |
235 | // Damage the old location | ||
236 | desktop_damage_whole_container(container); | ||
237 | if (view && !wl_list_empty(&view->saved_buffers)) { | ||
238 | struct sway_saved_buffer *saved_buf; | ||
239 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
240 | struct wlr_box box = { | ||
241 | .x = saved_buf->x - view->saved_geometry.x, | ||
242 | .y = saved_buf->y - view->saved_geometry.y, | ||
243 | .width = saved_buf->width, | ||
244 | .height = saved_buf->height, | ||
245 | }; | ||
246 | desktop_damage_box(&box); | ||
247 | } | ||
248 | } | ||
249 | |||
250 | // There are separate children lists for each instruction state, the | 230 | // There are separate children lists for each instruction state, the |
251 | // container's current state and the container's pending state | 231 | // container's current state and the container's pending state |
252 | // (ie. con->children). The list itself needs to be freed here. | 232 | // (ie. con->children). The list itself needs to be freed here. |
@@ -256,35 +236,443 @@ static void apply_container_state(struct sway_container *container, | |||
256 | 236 | ||
257 | memcpy(&container->current, state, sizeof(struct sway_container_state)); | 237 | memcpy(&container->current, state, sizeof(struct sway_container_state)); |
258 | 238 | ||
259 | if (view && !wl_list_empty(&view->saved_buffers)) { | 239 | if (view) { |
260 | if (!container->node.destroying || container->node.ntxnrefs == 1) { | 240 | if (view->saved_surface_tree) { |
261 | view_remove_saved_buffer(view); | 241 | if (!container->node.destroying || container->node.ntxnrefs == 1) { |
242 | view_remove_saved_buffer(view); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | // If the view hasn't responded to the configure, center it within | ||
247 | // the container. This is important for fullscreen views which | ||
248 | // refuse to resize to the size of the output. | ||
249 | if (view->surface) { | ||
250 | view_center_and_clip_surface(view); | ||
251 | } | ||
252 | } | ||
253 | } | ||
254 | |||
255 | static void arrange_title_bar(struct sway_container *con, | ||
256 | int x, int y, int width, int height) { | ||
257 | container_update(con); | ||
258 | |||
259 | bool has_title_bar = height > 0; | ||
260 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar); | ||
261 | if (!has_title_bar) { | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | wlr_scene_node_set_position(&con->title_bar.tree->node, x, y); | ||
266 | |||
267 | con->title_width = width; | ||
268 | container_arrange_title_bar(con); | ||
269 | } | ||
270 | |||
271 | static void disable_container(struct sway_container *con) { | ||
272 | if (con->view) { | ||
273 | wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); | ||
274 | } else { | ||
275 | for (int i = 0; i < con->current.children->length; i++) { | ||
276 | struct sway_container *child = con->current.children->items[i]; | ||
277 | |||
278 | wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); | ||
279 | |||
280 | disable_container(child); | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | |||
285 | static void arrange_container(struct sway_container *con, | ||
286 | int width, int height, bool title_bar, int gaps); | ||
287 | |||
288 | static void arrange_children(enum sway_container_layout layout, list_t *children, | ||
289 | struct sway_container *active, struct wlr_scene_tree *content, | ||
290 | int width, int height, int gaps) { | ||
291 | int title_bar_height = container_titlebar_height(); | ||
292 | |||
293 | if (layout == L_TABBED) { | ||
294 | struct sway_container *first = children->length == 1 ? | ||
295 | ((struct sway_container *)children->items[0]) : NULL; | ||
296 | if (config->hide_lone_tab && first && first->view && | ||
297 | first->current.border != B_NORMAL) { | ||
298 | title_bar_height = 0; | ||
299 | } | ||
300 | |||
301 | double w = (double) width / children->length; | ||
302 | int title_offset = 0; | ||
303 | for (int i = 0; i < children->length; i++) { | ||
304 | struct sway_container *child = children->items[i]; | ||
305 | bool activated = child == active; | ||
306 | int next_title_offset = round(w * i + w); | ||
307 | |||
308 | arrange_title_bar(child, title_offset, -title_bar_height, | ||
309 | next_title_offset - title_offset, title_bar_height); | ||
310 | wlr_scene_node_set_enabled(&child->border.tree->node, activated); | ||
311 | wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); | ||
312 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
313 | |||
314 | if (activated) { | ||
315 | arrange_container(child, width, height - title_bar_height, | ||
316 | false, 0); | ||
317 | } else { | ||
318 | disable_container(child); | ||
319 | } | ||
320 | |||
321 | title_offset = next_title_offset; | ||
322 | } | ||
323 | } else if (layout == L_STACKED) { | ||
324 | struct sway_container *first = children->length == 1 ? | ||
325 | ((struct sway_container *)children->items[0]) : NULL; | ||
326 | if (config->hide_lone_tab && first && first->view && | ||
327 | first->current.border != B_NORMAL) { | ||
328 | title_bar_height = 0; | ||
329 | } | ||
330 | |||
331 | int title_height = title_bar_height * children->length; | ||
332 | |||
333 | int y = 0; | ||
334 | for (int i = 0; i < children->length; i++) { | ||
335 | struct sway_container *child = children->items[i]; | ||
336 | bool activated = child == active; | ||
337 | |||
338 | arrange_title_bar(child, 0, y - title_height, width, title_bar_height); | ||
339 | wlr_scene_node_set_enabled(&child->border.tree->node, activated); | ||
340 | wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); | ||
341 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
342 | |||
343 | if (activated) { | ||
344 | arrange_container(child, width, height - title_height, | ||
345 | false, 0); | ||
346 | } else { | ||
347 | disable_container(child); | ||
348 | } | ||
349 | |||
350 | y += title_bar_height; | ||
351 | } | ||
352 | } else if (layout == L_VERT) { | ||
353 | int off = 0; | ||
354 | for (int i = 0; i < children->length; i++) { | ||
355 | struct sway_container *child = children->items[i]; | ||
356 | int cheight = child->current.height; | ||
357 | |||
358 | wlr_scene_node_set_enabled(&child->border.tree->node, true); | ||
359 | wlr_scene_node_set_position(&child->scene_tree->node, 0, off); | ||
360 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
361 | arrange_container(child, width, cheight, true, gaps); | ||
362 | off += cheight + gaps; | ||
363 | } | ||
364 | } else if (layout == L_HORIZ) { | ||
365 | int off = 0; | ||
366 | for (int i = 0; i < children->length; i++) { | ||
367 | struct sway_container *child = children->items[i]; | ||
368 | int cwidth = child->current.width; | ||
369 | |||
370 | wlr_scene_node_set_enabled(&child->border.tree->node, true); | ||
371 | wlr_scene_node_set_position(&child->scene_tree->node, off, 0); | ||
372 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
373 | arrange_container(child, cwidth, height, true, gaps); | ||
374 | off += cwidth + gaps; | ||
375 | } | ||
376 | } else { | ||
377 | sway_assert(false, "unreachable"); | ||
378 | } | ||
379 | } | ||
380 | |||
381 | static void arrange_container(struct sway_container *con, | ||
382 | int width, int height, bool title_bar, int gaps) { | ||
383 | // this container might have previously been in the scratchpad, | ||
384 | // make sure it's enabled for viewing | ||
385 | wlr_scene_node_set_enabled(&con->scene_tree->node, true); | ||
386 | |||
387 | if (con->output_handler) { | ||
388 | wlr_scene_buffer_set_dest_size(con->output_handler, width, height); | ||
389 | } | ||
390 | |||
391 | if (con->view) { | ||
392 | int border_top = container_titlebar_height(); | ||
393 | int border_width = con->current.border_thickness; | ||
394 | |||
395 | if (title_bar && con->current.border != B_NORMAL) { | ||
396 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); | ||
397 | wlr_scene_node_set_enabled(&con->border.top->node, true); | ||
398 | } else { | ||
399 | wlr_scene_node_set_enabled(&con->border.top->node, false); | ||
400 | } | ||
401 | |||
402 | if (con->current.border == B_NORMAL) { | ||
403 | if (title_bar) { | ||
404 | arrange_title_bar(con, 0, 0, width, border_top); | ||
405 | } else { | ||
406 | border_top = 0; | ||
407 | // should be handled by the parent container | ||
408 | } | ||
409 | } else if (con->current.border == B_PIXEL) { | ||
410 | container_update(con); | ||
411 | border_top = title_bar && con->current.border_top ? border_width : 0; | ||
412 | } else if (con->current.border == B_NONE) { | ||
413 | container_update(con); | ||
414 | border_top = 0; | ||
415 | border_width = 0; | ||
416 | } else if (con->current.border == B_CSD) { | ||
417 | border_top = 0; | ||
418 | border_width = 0; | ||
419 | } else { | ||
420 | sway_assert(false, "unreachable"); | ||
421 | } | ||
422 | |||
423 | int border_bottom = con->current.border_bottom ? border_width : 0; | ||
424 | int border_left = con->current.border_left ? border_width : 0; | ||
425 | int border_right = con->current.border_right ? border_width : 0; | ||
426 | |||
427 | wlr_scene_rect_set_size(con->border.top, width, border_top); | ||
428 | wlr_scene_rect_set_size(con->border.bottom, width, border_bottom); | ||
429 | wlr_scene_rect_set_size(con->border.left, | ||
430 | border_left, height - border_top - border_bottom); | ||
431 | wlr_scene_rect_set_size(con->border.right, | ||
432 | border_right, height - border_top - border_bottom); | ||
433 | |||
434 | wlr_scene_node_set_position(&con->border.top->node, 0, 0); | ||
435 | wlr_scene_node_set_position(&con->border.bottom->node, | ||
436 | 0, height - border_bottom); | ||
437 | wlr_scene_node_set_position(&con->border.left->node, | ||
438 | 0, border_top); | ||
439 | wlr_scene_node_set_position(&con->border.right->node, | ||
440 | width - border_right, border_top); | ||
441 | |||
442 | // make sure to reparent, it's possible that the client just came out of | ||
443 | // fullscreen mode where the parent of the surface is not the container | ||
444 | wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); | ||
445 | wlr_scene_node_set_position(&con->view->scene_tree->node, | ||
446 | border_left, border_top); | ||
447 | } else { | ||
448 | // make sure to disable the title bar if the parent is not managing it | ||
449 | if (title_bar) { | ||
450 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); | ||
451 | } | ||
452 | |||
453 | arrange_children(con->current.layout, con->current.children, | ||
454 | con->current.focused_inactive_child, con->content_tree, | ||
455 | width, height, gaps); | ||
456 | } | ||
457 | } | ||
458 | |||
459 | static int container_get_gaps(struct sway_container *con) { | ||
460 | struct sway_workspace *ws = con->current.workspace; | ||
461 | struct sway_container *temp = con; | ||
462 | while (temp) { | ||
463 | enum sway_container_layout layout; | ||
464 | if (temp->current.parent) { | ||
465 | layout = temp->current.parent->current.layout; | ||
466 | } else { | ||
467 | layout = ws->current.layout; | ||
468 | } | ||
469 | if (layout == L_TABBED || layout == L_STACKED) { | ||
470 | return 0; | ||
471 | } | ||
472 | temp = temp->pending.parent; | ||
473 | } | ||
474 | return ws->gaps_inner; | ||
475 | } | ||
476 | |||
477 | static void arrange_fullscreen(struct wlr_scene_tree *tree, | ||
478 | struct sway_container *fs, struct sway_workspace *ws, | ||
479 | int width, int height) { | ||
480 | struct wlr_scene_node *fs_node; | ||
481 | if (fs->view) { | ||
482 | fs_node = &fs->view->scene_tree->node; | ||
483 | |||
484 | // if we only care about the view, disable any decorations | ||
485 | wlr_scene_node_set_enabled(&fs->scene_tree->node, false); | ||
486 | } else { | ||
487 | fs_node = &fs->scene_tree->node; | ||
488 | arrange_container(fs, width, height, true, container_get_gaps(fs)); | ||
489 | } | ||
490 | |||
491 | wlr_scene_node_reparent(fs_node, tree); | ||
492 | wlr_scene_node_lower_to_bottom(fs_node); | ||
493 | wlr_scene_node_set_position(fs_node, 0, 0); | ||
494 | } | ||
495 | |||
496 | static void arrange_workspace_floating(struct sway_workspace *ws) { | ||
497 | for (int i = 0; i < ws->current.floating->length; i++) { | ||
498 | struct sway_container *floater = ws->current.floating->items[i]; | ||
499 | struct wlr_scene_tree *layer = root->layers.floating; | ||
500 | |||
501 | if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { | ||
502 | continue; | ||
503 | } | ||
504 | |||
505 | if (root->fullscreen_global) { | ||
506 | if (container_is_transient_for(floater, root->fullscreen_global)) { | ||
507 | layer = root->layers.fullscreen_global; | ||
508 | } | ||
509 | } else { | ||
510 | for (int i = 0; i < root->outputs->length; i++) { | ||
511 | struct sway_output *output = root->outputs->items[i]; | ||
512 | struct sway_workspace *active = output->current.active_workspace; | ||
513 | |||
514 | if (active && active->fullscreen && | ||
515 | container_is_transient_for(floater, active->fullscreen)) { | ||
516 | layer = root->layers.fullscreen; | ||
517 | } | ||
518 | } | ||
519 | } | ||
520 | |||
521 | wlr_scene_node_reparent(&floater->scene_tree->node, layer); | ||
522 | wlr_scene_node_set_position(&floater->scene_tree->node, | ||
523 | floater->current.x, floater->current.y); | ||
524 | wlr_scene_node_set_enabled(&floater->scene_tree->node, true); | ||
525 | |||
526 | arrange_container(floater, floater->current.width, floater->current.height, | ||
527 | true, ws->gaps_inner); | ||
528 | } | ||
529 | } | ||
530 | |||
531 | static void arrange_workspace_tiling(struct sway_workspace *ws, | ||
532 | int width, int height) { | ||
533 | arrange_children(ws->current.layout, ws->current.tiling, | ||
534 | ws->current.focused_inactive_child, ws->layers.tiling, | ||
535 | width, height, ws->gaps_inner); | ||
536 | } | ||
537 | |||
538 | static void disable_workspace(struct sway_workspace *ws) { | ||
539 | // if any containers were just moved to a disabled workspace it will | ||
540 | // have the parent of the old workspace. Move the workspace so that it won't | ||
541 | // be shown. | ||
542 | for (int i = 0; i < ws->current.tiling->length; i++) { | ||
543 | struct sway_container *child = ws->current.tiling->items[i]; | ||
544 | |||
545 | wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling); | ||
546 | disable_container(child); | ||
547 | } | ||
548 | |||
549 | for (int i = 0; i < ws->current.floating->length; i++) { | ||
550 | struct sway_container *floater = ws->current.floating->items[i]; | ||
551 | wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); | ||
552 | disable_container(floater); | ||
553 | wlr_scene_node_set_enabled(&floater->scene_tree->node, false); | ||
554 | } | ||
555 | } | ||
556 | |||
557 | static void arrange_output(struct sway_output *output, int width, int height) { | ||
558 | for (int i = 0; i < output->current.workspaces->length; i++) { | ||
559 | struct sway_workspace *child = output->current.workspaces->items[i]; | ||
560 | |||
561 | bool activated = output->current.active_workspace == child; | ||
562 | |||
563 | wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling); | ||
564 | wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen); | ||
565 | |||
566 | for (int i = 0; i < child->current.floating->length; i++) { | ||
567 | struct sway_container *floater = child->current.floating->items[i]; | ||
568 | wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); | ||
569 | wlr_scene_node_set_enabled(&floater->scene_tree->node, activated); | ||
570 | } | ||
571 | |||
572 | if (activated) { | ||
573 | struct sway_container *fs = child->current.fullscreen; | ||
574 | wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs); | ||
575 | wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs); | ||
576 | |||
577 | arrange_workspace_floating(child); | ||
578 | |||
579 | wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs); | ||
580 | wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs); | ||
581 | wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs); | ||
582 | |||
583 | if (fs) { | ||
584 | wlr_scene_rect_set_size(output->fullscreen_background, width, height); | ||
585 | |||
586 | arrange_fullscreen(child->layers.fullscreen, fs, child, | ||
587 | width, height); | ||
588 | } else { | ||
589 | struct wlr_box *area = &output->usable_area; | ||
590 | struct side_gaps *gaps = &child->current_gaps; | ||
591 | |||
592 | wlr_scene_node_set_position(&child->layers.tiling->node, | ||
593 | gaps->left + area->x, gaps->top + area->y); | ||
594 | |||
595 | arrange_workspace_tiling(child, | ||
596 | area->width - gaps->left - gaps->right, | ||
597 | area->height - gaps->top - gaps->bottom); | ||
598 | } | ||
599 | } else { | ||
600 | wlr_scene_node_set_enabled(&child->layers.tiling->node, false); | ||
601 | wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false); | ||
602 | |||
603 | disable_workspace(child); | ||
262 | } | 604 | } |
263 | } | 605 | } |
606 | } | ||
264 | 607 | ||
265 | // If the view hasn't responded to the configure, center it within | 608 | void arrange_popups(struct wlr_scene_tree *popups) { |
266 | // the container. This is important for fullscreen views which | 609 | struct wlr_scene_node *node; |
267 | // refuse to resize to the size of the output. | 610 | wl_list_for_each(node, &popups->children, link) { |
268 | if (view && view->surface) { | 611 | struct sway_popup_desc *popup = scene_descriptor_try_get(node, |
269 | view_center_surface(view); | 612 | SWAY_SCENE_DESC_POPUP); |
613 | |||
614 | int lx, ly; | ||
615 | wlr_scene_node_coords(popup->relative, &lx, &ly); | ||
616 | wlr_scene_node_set_position(node, lx, ly); | ||
270 | } | 617 | } |
618 | } | ||
619 | |||
620 | static void arrange_root(struct sway_root *root) { | ||
621 | struct sway_container *fs = root->fullscreen_global; | ||
622 | |||
623 | wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs); | ||
624 | wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs); | ||
625 | wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs); | ||
626 | wlr_scene_node_set_enabled(&root->layers.floating->node, !fs); | ||
627 | wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs); | ||
628 | wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs); | ||
271 | 629 | ||
272 | // Damage the new location | 630 | // hide all contents in the scratchpad |
273 | desktop_damage_whole_container(container); | 631 | for (int i = 0; i < root->scratchpad->length; i++) { |
274 | if (view && view->surface) { | 632 | struct sway_container *con = root->scratchpad->items[i]; |
275 | struct wlr_surface *surface = view->surface; | 633 | |
276 | struct wlr_box box = { | 634 | wlr_scene_node_set_enabled(&con->scene_tree->node, false); |
277 | .x = container->current.content_x - view->geometry.x, | ||
278 | .y = container->current.content_y - view->geometry.y, | ||
279 | .width = surface->current.width, | ||
280 | .height = surface->current.height, | ||
281 | }; | ||
282 | desktop_damage_box(&box); | ||
283 | } | 635 | } |
284 | 636 | ||
285 | if (!container->node.destroying) { | 637 | if (fs) { |
286 | container_discover_outputs(container); | 638 | for (int i = 0; i < root->outputs->length; i++) { |
639 | struct sway_output *output = root->outputs->items[i]; | ||
640 | struct sway_workspace *ws = output->current.active_workspace; | ||
641 | |||
642 | if (ws) { | ||
643 | arrange_workspace_floating(ws); | ||
644 | } | ||
645 | } | ||
646 | |||
647 | arrange_fullscreen(root->layers.fullscreen_global, fs, NULL, | ||
648 | root->width, root->height); | ||
649 | } else { | ||
650 | for (int i = 0; i < root->outputs->length; i++) { | ||
651 | struct sway_output *output = root->outputs->items[i]; | ||
652 | |||
653 | wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); | ||
654 | |||
655 | wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background); | ||
656 | wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom); | ||
657 | wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling); | ||
658 | wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top); | ||
659 | wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay); | ||
660 | wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen); | ||
661 | wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock); | ||
662 | |||
663 | wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly); | ||
664 | wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly); | ||
665 | wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly); | ||
666 | wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly); | ||
667 | wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly); | ||
668 | wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly); | ||
669 | wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly); | ||
670 | |||
671 | arrange_output(output, output->width, output->height); | ||
672 | } | ||
287 | } | 673 | } |
674 | |||
675 | arrange_popups(root->layers.popup); | ||
288 | } | 676 | } |
289 | 677 | ||
290 | /** | 678 | /** |
@@ -326,8 +714,6 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
326 | 714 | ||
327 | node->instruction = NULL; | 715 | node->instruction = NULL; |
328 | } | 716 | } |
329 | |||
330 | cursor_rebase_all(); | ||
331 | } | 717 | } |
332 | 718 | ||
333 | static void transaction_commit_pending(void); | 719 | static void transaction_commit_pending(void); |
@@ -340,11 +726,13 @@ static void transaction_progress(void) { | |||
340 | return; | 726 | return; |
341 | } | 727 | } |
342 | transaction_apply(server.queued_transaction); | 728 | transaction_apply(server.queued_transaction); |
729 | arrange_root(root); | ||
730 | cursor_rebase_all(); | ||
343 | transaction_destroy(server.queued_transaction); | 731 | transaction_destroy(server.queued_transaction); |
344 | server.queued_transaction = NULL; | 732 | server.queued_transaction = NULL; |
345 | 733 | ||
346 | if (!server.pending_transaction) { | 734 | if (!server.pending_transaction) { |
347 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | 735 | sway_idle_inhibit_v1_check_active(); |
348 | return; | 736 | return; |
349 | } | 737 | } |
350 | 738 | ||
@@ -402,7 +790,7 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
402 | struct sway_transaction_instruction *instruction = | 790 | struct sway_transaction_instruction *instruction = |
403 | transaction->instructions->items[i]; | 791 | transaction->instructions->items[i]; |
404 | struct sway_node *node = instruction->node; | 792 | struct sway_node *node = instruction->node; |
405 | bool hidden = node_is_view(node) && | 793 | bool hidden = node_is_view(node) && !node->destroying && |
406 | !view_is_visible(node->sway_container->view); | 794 | !view_is_visible(node->sway_container->view); |
407 | if (should_configure(node, instruction)) { | 795 | if (should_configure(node, instruction)) { |
408 | instruction->serial = view_configure(node->sway_container->view, | 796 | instruction->serial = view_configure(node->sway_container->view, |
@@ -415,21 +803,11 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
415 | ++transaction->num_waiting; | 803 | ++transaction->num_waiting; |
416 | } | 804 | } |
417 | 805 | ||
418 | // From here on we are rendering a saved buffer of the view, which | 806 | view_send_frame_done(node->sway_container->view); |
419 | // means we can send a frame done event to make the client redraw it | ||
420 | // as soon as possible. Additionally, this is required if a view is | ||
421 | // mapping and its default geometry doesn't intersect an output. | ||
422 | struct timespec now; | ||
423 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
424 | wlr_surface_send_frame_done( | ||
425 | node->sway_container->view->surface, &now); | ||
426 | } | 807 | } |
427 | if (!hidden && node_is_view(node) && | 808 | if (!hidden && node_is_view(node) && |
428 | wl_list_empty(&node->sway_container->view->saved_buffers)) { | 809 | !node->sway_container->view->saved_surface_tree) { |
429 | view_save_buffer(node->sway_container->view); | 810 | view_save_buffer(node->sway_container->view); |
430 | memcpy(&node->sway_container->view->saved_geometry, | ||
431 | &node->sway_container->view->geometry, | ||
432 | sizeof(struct wlr_box)); | ||
433 | } | 811 | } |
434 | node->instruction = instruction; | 812 | node->instruction = instruction; |
435 | } | 813 | } |
@@ -499,16 +877,18 @@ static void set_instruction_ready( | |||
499 | transaction_progress(); | 877 | transaction_progress(); |
500 | } | 878 | } |
501 | 879 | ||
502 | void transaction_notify_view_ready_by_serial(struct sway_view *view, | 880 | bool transaction_notify_view_ready_by_serial(struct sway_view *view, |
503 | uint32_t serial) { | 881 | uint32_t serial) { |
504 | struct sway_transaction_instruction *instruction = | 882 | struct sway_transaction_instruction *instruction = |
505 | view->container->node.instruction; | 883 | view->container->node.instruction; |
506 | if (instruction != NULL && instruction->serial == serial) { | 884 | if (instruction != NULL && instruction->serial == serial) { |
507 | set_instruction_ready(instruction); | 885 | set_instruction_ready(instruction); |
886 | return true; | ||
508 | } | 887 | } |
888 | return false; | ||
509 | } | 889 | } |
510 | 890 | ||
511 | void transaction_notify_view_ready_by_geometry(struct sway_view *view, | 891 | bool transaction_notify_view_ready_by_geometry(struct sway_view *view, |
512 | double x, double y, int width, int height) { | 892 | double x, double y, int width, int height) { |
513 | struct sway_transaction_instruction *instruction = | 893 | struct sway_transaction_instruction *instruction = |
514 | view->container->node.instruction; | 894 | view->container->node.instruction; |
@@ -518,7 +898,9 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view, | |||
518 | instruction->container_state.content_width == width && | 898 | instruction->container_state.content_width == width && |
519 | instruction->container_state.content_height == height) { | 899 | instruction->container_state.content_height == height) { |
520 | set_instruction_ready(instruction); | 900 | set_instruction_ready(instruction); |
901 | return true; | ||
521 | } | 902 | } |
903 | return false; | ||
522 | } | 904 | } |
523 | 905 | ||
524 | static void _transaction_commit_dirty(bool server_request) { | 906 | static void _transaction_commit_dirty(bool server_request) { |
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index d34654fd..7c417891 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -7,7 +6,7 @@ | |||
7 | #include <wlr/util/edges.h> | 6 | #include <wlr/util/edges.h> |
8 | #include "log.h" | 7 | #include "log.h" |
9 | #include "sway/decoration.h" | 8 | #include "sway/decoration.h" |
10 | #include "sway/desktop.h" | 9 | #include "sway/scene_descriptor.h" |
11 | #include "sway/desktop/transaction.h" | 10 | #include "sway/desktop/transaction.h" |
12 | #include "sway/input/cursor.h" | 11 | #include "sway/input/cursor.h" |
13 | #include "sway/input/input-manager.h" | 12 | #include "sway/input/input-manager.h" |
@@ -19,64 +18,45 @@ | |||
19 | #include "sway/tree/workspace.h" | 18 | #include "sway/tree/workspace.h" |
20 | #include "sway/xdg_decoration.h" | 19 | #include "sway/xdg_decoration.h" |
21 | 20 | ||
22 | static const struct sway_view_child_impl popup_impl; | ||
23 | |||
24 | static void popup_get_root_coords(struct sway_view_child *child, | ||
25 | int *root_sx, int *root_sy) { | ||
26 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
27 | struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; | ||
28 | |||
29 | int x_offset = -child->view->geometry.x - surface->geometry.x; | ||
30 | int y_offset = -child->view->geometry.y - surface->geometry.y; | ||
31 | |||
32 | wlr_xdg_popup_get_toplevel_coords(surface->popup, | ||
33 | x_offset + surface->popup->geometry.x, | ||
34 | y_offset + surface->popup->geometry.y, | ||
35 | root_sx, root_sy); | ||
36 | } | ||
37 | |||
38 | static void popup_destroy(struct sway_view_child *child) { | ||
39 | if (!sway_assert(child->impl == &popup_impl, | ||
40 | "Expected an xdg_shell popup")) { | ||
41 | return; | ||
42 | } | ||
43 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
44 | wl_list_remove(&popup->new_popup.link); | ||
45 | wl_list_remove(&popup->destroy.link); | ||
46 | free(popup); | ||
47 | } | ||
48 | |||
49 | static const struct sway_view_child_impl popup_impl = { | ||
50 | .get_root_coords = popup_get_root_coords, | ||
51 | .destroy = popup_destroy, | ||
52 | }; | ||
53 | |||
54 | static struct sway_xdg_popup *popup_create( | 21 | static struct sway_xdg_popup *popup_create( |
55 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view); | 22 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view, |
23 | struct wlr_scene_tree *parent); | ||
56 | 24 | ||
57 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | 25 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { |
58 | struct sway_xdg_popup *popup = | 26 | struct sway_xdg_popup *popup = |
59 | wl_container_of(listener, popup, new_popup); | 27 | wl_container_of(listener, popup, new_popup); |
60 | struct wlr_xdg_popup *wlr_popup = data; | 28 | struct wlr_xdg_popup *wlr_popup = data; |
61 | popup_create(wlr_popup, popup->child.view); | 29 | popup_create(wlr_popup, popup->view, popup->xdg_surface_tree); |
62 | } | 30 | } |
63 | 31 | ||
64 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 32 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
65 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); | 33 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); |
66 | view_child_destroy(&popup->child); | 34 | |
35 | wl_list_remove(&popup->new_popup.link); | ||
36 | wl_list_remove(&popup->destroy.link); | ||
37 | wl_list_remove(&popup->surface_commit.link); | ||
38 | wl_list_remove(&popup->reposition.link); | ||
39 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
40 | free(popup); | ||
67 | } | 41 | } |
68 | 42 | ||
69 | static void popup_unconstrain(struct sway_xdg_popup *popup) { | 43 | static void popup_unconstrain(struct sway_xdg_popup *popup) { |
70 | struct sway_view *view = popup->child.view; | 44 | struct sway_view *view = popup->view; |
71 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; | 45 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; |
46 | |||
47 | struct sway_workspace *workspace = view->container->pending.workspace; | ||
48 | if (!workspace) { | ||
49 | // is null if in the scratchpad | ||
50 | return; | ||
51 | } | ||
72 | 52 | ||
73 | struct sway_output *output = view->container->pending.workspace->output; | 53 | struct sway_output *output = workspace->output; |
74 | 54 | ||
75 | // the output box expressed in the coordinate system of the toplevel parent | 55 | // the output box expressed in the coordinate system of the toplevel parent |
76 | // of the popup | 56 | // of the popup |
77 | struct wlr_box output_toplevel_sx_box = { | 57 | struct wlr_box output_toplevel_sx_box = { |
78 | .x = output->lx - view->container->pending.content_x, | 58 | .x = output->lx - view->container->pending.content_x + view->geometry.x, |
79 | .y = output->ly - view->container->pending.content_y, | 59 | .y = output->ly - view->container->pending.content_y + view->geometry.y, |
80 | .width = output->width, | 60 | .width = output->width, |
81 | .height = output->height, | 61 | .height = output->height, |
82 | }; | 62 | }; |
@@ -84,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { | |||
84 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | 64 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); |
85 | } | 65 | } |
86 | 66 | ||
87 | static struct sway_xdg_popup *popup_create( | 67 | static void popup_handle_surface_commit(struct wl_listener *listener, void *data) { |
88 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { | 68 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit); |
69 | if (popup->wlr_xdg_popup->base->initial_commit) { | ||
70 | popup_unconstrain(popup); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | static void popup_handle_reposition(struct wl_listener *listener, void *data) { | ||
75 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, reposition); | ||
76 | popup_unconstrain(popup); | ||
77 | } | ||
78 | |||
79 | static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, | ||
80 | struct sway_view *view, struct wlr_scene_tree *parent) { | ||
89 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; | 81 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; |
90 | 82 | ||
91 | struct sway_xdg_popup *popup = | 83 | struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); |
92 | calloc(1, sizeof(struct sway_xdg_popup)); | 84 | if (!popup) { |
93 | if (popup == NULL) { | 85 | return NULL; |
86 | } | ||
87 | |||
88 | popup->wlr_xdg_popup = wlr_popup; | ||
89 | popup->view = view; | ||
90 | |||
91 | popup->scene_tree = wlr_scene_tree_create(parent); | ||
92 | if (!popup->scene_tree) { | ||
93 | free(popup); | ||
94 | return NULL; | ||
95 | } | ||
96 | |||
97 | popup->xdg_surface_tree = wlr_scene_xdg_surface_create( | ||
98 | popup->scene_tree, xdg_surface); | ||
99 | if (!popup->xdg_surface_tree) { | ||
100 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
101 | free(popup); | ||
94 | return NULL; | 102 | return NULL; |
95 | } | 103 | } |
96 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | ||
97 | popup->wlr_xdg_surface = xdg_surface; | ||
98 | 104 | ||
105 | popup->desc.relative = &view->content_tree->node; | ||
106 | popup->desc.view = view; | ||
107 | |||
108 | if (!scene_descriptor_assign(&popup->scene_tree->node, | ||
109 | SWAY_SCENE_DESC_POPUP, &popup->desc)) { | ||
110 | sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); | ||
111 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
112 | free(popup); | ||
113 | return NULL; | ||
114 | } | ||
115 | |||
116 | popup->wlr_xdg_popup = xdg_surface->popup; | ||
117 | struct sway_xdg_shell_view *shell_view = | ||
118 | wl_container_of(view, shell_view, view); | ||
119 | xdg_surface->data = shell_view; | ||
120 | |||
121 | wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit); | ||
122 | popup->surface_commit.notify = popup_handle_surface_commit; | ||
99 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 123 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
100 | popup->new_popup.notify = popup_handle_new_popup; | 124 | popup->new_popup.notify = popup_handle_new_popup; |
101 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); | 125 | wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); |
126 | popup->reposition.notify = popup_handle_reposition; | ||
127 | wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); | ||
102 | popup->destroy.notify = popup_handle_destroy; | 128 | popup->destroy.notify = popup_handle_destroy; |
103 | 129 | ||
104 | wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); | ||
105 | wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); | ||
106 | |||
107 | popup_unconstrain(popup); | ||
108 | |||
109 | return popup; | 130 | return popup; |
110 | } | 131 | } |
111 | 132 | ||
112 | |||
113 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( | 133 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( |
114 | struct sway_view *view) { | 134 | struct sway_view *view) { |
115 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, | 135 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, |
@@ -122,7 +142,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( | |||
122 | static void get_constraints(struct sway_view *view, double *min_width, | 142 | static void get_constraints(struct sway_view *view, double *min_width, |
123 | double *max_width, double *min_height, double *max_height) { | 143 | double *max_width, double *min_height, double *max_height) { |
124 | struct wlr_xdg_toplevel_state *state = | 144 | struct wlr_xdg_toplevel_state *state = |
125 | &view->wlr_xdg_surface->toplevel->current; | 145 | &view->wlr_xdg_toplevel->current; |
126 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | 146 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; |
127 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | 147 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; |
128 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | 148 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; |
@@ -136,9 +156,9 @@ static const char *get_string_prop(struct sway_view *view, | |||
136 | } | 156 | } |
137 | switch (prop) { | 157 | switch (prop) { |
138 | case VIEW_PROP_TITLE: | 158 | case VIEW_PROP_TITLE: |
139 | return view->wlr_xdg_surface->toplevel->title; | 159 | return view->wlr_xdg_toplevel->title; |
140 | case VIEW_PROP_APP_ID: | 160 | case VIEW_PROP_APP_ID: |
141 | return view->wlr_xdg_surface->toplevel->app_id; | 161 | return view->wlr_xdg_toplevel->app_id; |
142 | default: | 162 | default: |
143 | return NULL; | 163 | return NULL; |
144 | } | 164 | } |
@@ -151,50 +171,52 @@ static uint32_t configure(struct sway_view *view, double lx, double ly, | |||
151 | if (xdg_shell_view == NULL) { | 171 | if (xdg_shell_view == NULL) { |
152 | return 0; | 172 | return 0; |
153 | } | 173 | } |
154 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); | 174 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, |
175 | width, height); | ||
155 | } | 176 | } |
156 | 177 | ||
157 | static void set_activated(struct sway_view *view, bool activated) { | 178 | static void set_activated(struct sway_view *view, bool activated) { |
158 | if (xdg_shell_view_from_view(view) == NULL) { | 179 | if (xdg_shell_view_from_view(view) == NULL) { |
159 | return; | 180 | return; |
160 | } | 181 | } |
161 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 182 | wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated); |
162 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | ||
163 | wlr_xdg_toplevel_set_activated(surface, activated); | ||
164 | } | ||
165 | } | 183 | } |
166 | 184 | ||
167 | static void set_tiled(struct sway_view *view, bool tiled) { | 185 | static void set_tiled(struct sway_view *view, bool tiled) { |
168 | if (xdg_shell_view_from_view(view) == NULL) { | 186 | if (xdg_shell_view_from_view(view) == NULL) { |
169 | return; | 187 | return; |
170 | } | 188 | } |
171 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 189 | if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >= |
172 | enum wlr_edges edges = WLR_EDGE_NONE; | 190 | XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { |
173 | if (tiled) { | 191 | enum wlr_edges edges = WLR_EDGE_NONE; |
174 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | | 192 | if (tiled) { |
175 | WLR_EDGE_BOTTOM; | 193 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | |
194 | WLR_EDGE_BOTTOM; | ||
195 | } | ||
196 | wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); | ||
197 | } else { | ||
198 | // The version is too low for the tiled state; configure as maximized instead | ||
199 | // to stop the client from drawing decorations outside of the toplevel geometry. | ||
200 | wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled); | ||
176 | } | 201 | } |
177 | wlr_xdg_toplevel_set_tiled(surface, edges); | ||
178 | } | 202 | } |
179 | 203 | ||
180 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { | 204 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { |
181 | if (xdg_shell_view_from_view(view) == NULL) { | 205 | if (xdg_shell_view_from_view(view) == NULL) { |
182 | return; | 206 | return; |
183 | } | 207 | } |
184 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 208 | wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen); |
185 | wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); | ||
186 | } | 209 | } |
187 | 210 | ||
188 | static void set_resizing(struct sway_view *view, bool resizing) { | 211 | static void set_resizing(struct sway_view *view, bool resizing) { |
189 | if (xdg_shell_view_from_view(view) == NULL) { | 212 | if (xdg_shell_view_from_view(view) == NULL) { |
190 | return; | 213 | return; |
191 | } | 214 | } |
192 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 215 | wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing); |
193 | wlr_xdg_toplevel_set_resizing(surface, resizing); | ||
194 | } | 216 | } |
195 | 217 | ||
196 | static bool wants_floating(struct sway_view *view) { | 218 | static bool wants_floating(struct sway_view *view) { |
197 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; | 219 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
198 | struct wlr_xdg_toplevel_state *state = &toplevel->current; | 220 | struct wlr_xdg_toplevel_state *state = &toplevel->current; |
199 | return (state->min_width != 0 && state->min_height != 0 | 221 | return (state->min_width != 0 && state->min_height != 0 |
200 | && (state->min_width == state->max_width | 222 | && (state->min_width == state->max_width |
@@ -202,35 +224,17 @@ static bool wants_floating(struct sway_view *view) { | |||
202 | || toplevel->parent; | 224 | || toplevel->parent; |
203 | } | 225 | } |
204 | 226 | ||
205 | static void for_each_surface(struct sway_view *view, | ||
206 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
207 | if (xdg_shell_view_from_view(view) == NULL) { | ||
208 | return; | ||
209 | } | ||
210 | wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, | ||
211 | user_data); | ||
212 | } | ||
213 | |||
214 | static void for_each_popup_surface(struct sway_view *view, | ||
215 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
216 | if (xdg_shell_view_from_view(view) == NULL) { | ||
217 | return; | ||
218 | } | ||
219 | wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator, | ||
220 | user_data); | ||
221 | } | ||
222 | |||
223 | static bool is_transient_for(struct sway_view *child, | 227 | static bool is_transient_for(struct sway_view *child, |
224 | struct sway_view *ancestor) { | 228 | struct sway_view *ancestor) { |
225 | if (xdg_shell_view_from_view(child) == NULL) { | 229 | if (xdg_shell_view_from_view(child) == NULL) { |
226 | return false; | 230 | return false; |
227 | } | 231 | } |
228 | struct wlr_xdg_surface *surface = child->wlr_xdg_surface; | 232 | struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel; |
229 | while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | 233 | while (toplevel) { |
230 | if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { | 234 | if (toplevel->parent == ancestor->wlr_xdg_toplevel) { |
231 | return true; | 235 | return true; |
232 | } | 236 | } |
233 | surface = surface->toplevel->parent; | 237 | toplevel = toplevel->parent; |
234 | } | 238 | } |
235 | return false; | 239 | return false; |
236 | } | 240 | } |
@@ -239,17 +243,13 @@ static void _close(struct sway_view *view) { | |||
239 | if (xdg_shell_view_from_view(view) == NULL) { | 243 | if (xdg_shell_view_from_view(view) == NULL) { |
240 | return; | 244 | return; |
241 | } | 245 | } |
242 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 246 | wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel); |
243 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL | ||
244 | && surface->toplevel) { | ||
245 | wlr_xdg_toplevel_send_close(surface); | ||
246 | } | ||
247 | } | 247 | } |
248 | 248 | ||
249 | static void close_popups(struct sway_view *view) { | 249 | static void close_popups(struct sway_view *view) { |
250 | struct wlr_xdg_popup *popup, *tmp; | 250 | struct wlr_xdg_popup *popup, *tmp; |
251 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { | 251 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) { |
252 | wlr_xdg_popup_destroy(popup->base); | 252 | wlr_xdg_popup_destroy(popup); |
253 | } | 253 | } |
254 | } | 254 | } |
255 | 255 | ||
@@ -271,8 +271,6 @@ static const struct sway_view_impl view_impl = { | |||
271 | .set_fullscreen = set_fullscreen, | 271 | .set_fullscreen = set_fullscreen, |
272 | .set_resizing = set_resizing, | 272 | .set_resizing = set_resizing, |
273 | .wants_floating = wants_floating, | 273 | .wants_floating = wants_floating, |
274 | .for_each_surface = for_each_surface, | ||
275 | .for_each_popup_surface = for_each_popup_surface, | ||
276 | .is_transient_for = is_transient_for, | 274 | .is_transient_for = is_transient_for, |
277 | .close = _close, | 275 | .close = _close, |
278 | .close_popups = close_popups, | 276 | .close_popups = close_popups, |
@@ -283,7 +281,21 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
283 | struct sway_xdg_shell_view *xdg_shell_view = | 281 | struct sway_xdg_shell_view *xdg_shell_view = |
284 | wl_container_of(listener, xdg_shell_view, commit); | 282 | wl_container_of(listener, xdg_shell_view, commit); |
285 | struct sway_view *view = &xdg_shell_view->view; | 283 | struct sway_view *view = &xdg_shell_view->view; |
286 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 284 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; |
285 | |||
286 | if (xdg_surface->initial_commit) { | ||
287 | if (view->xdg_decoration != NULL) { | ||
288 | set_xdg_decoration_mode(view->xdg_decoration); | ||
289 | } | ||
290 | // XXX: https://github.com/swaywm/sway/issues/2176 | ||
291 | wlr_xdg_surface_schedule_configure(xdg_surface); | ||
292 | // TODO: wlr_xdg_toplevel_set_bounds() | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | if (!xdg_surface->surface->mapped) { | ||
297 | return; | ||
298 | } | ||
287 | 299 | ||
288 | struct wlr_box new_geo; | 300 | struct wlr_box new_geo; |
289 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); | 301 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); |
@@ -296,23 +308,32 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
296 | // The client changed its surface size in this commit. For floating | 308 | // The client changed its surface size in this commit. For floating |
297 | // containers, we resize the container to match. For tiling containers, | 309 | // containers, we resize the container to match. For tiling containers, |
298 | // we only recenter the surface. | 310 | // we only recenter the surface. |
299 | desktop_damage_view(view); | ||
300 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | 311 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); |
301 | if (container_is_floating(view->container)) { | 312 | if (container_is_floating(view->container)) { |
302 | view_update_size(view); | 313 | view_update_size(view); |
314 | // Only set the toplevel size the current container actually has a size. | ||
315 | if (view->container->current.width) { | ||
316 | wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width, | ||
317 | view->geometry.height); | ||
318 | } | ||
303 | transaction_commit_dirty_client(); | 319 | transaction_commit_dirty_client(); |
304 | } else { | ||
305 | view_center_surface(view); | ||
306 | } | 320 | } |
307 | desktop_damage_view(view); | 321 | |
322 | view_center_and_clip_surface(view); | ||
308 | } | 323 | } |
309 | 324 | ||
310 | if (view->container->node.instruction) { | 325 | if (view->container->node.instruction) { |
311 | transaction_notify_view_ready_by_serial(view, | 326 | bool successful = transaction_notify_view_ready_by_serial(view, |
312 | xdg_surface->configure_serial); | 327 | xdg_surface->current.configure_serial); |
328 | |||
329 | // If we saved the view and this commit isn't what we're looking for | ||
330 | // that means the user will never actually see the buffers submitted to | ||
331 | // us here. Just send frame done events to these surfaces so they can | ||
332 | // commit another time for us. | ||
333 | if (view->saved_surface_tree && !successful) { | ||
334 | view_send_frame_done(view); | ||
335 | } | ||
313 | } | 336 | } |
314 | |||
315 | view_damage_from(view); | ||
316 | } | 337 | } |
317 | 338 | ||
318 | static void handle_set_title(struct wl_listener *listener, void *data) { | 339 | static void handle_set_title(struct wl_listener *listener, void *data) { |
@@ -327,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) { | |||
327 | struct sway_xdg_shell_view *xdg_shell_view = | 348 | struct sway_xdg_shell_view *xdg_shell_view = |
328 | wl_container_of(listener, xdg_shell_view, set_app_id); | 349 | wl_container_of(listener, xdg_shell_view, set_app_id); |
329 | struct sway_view *view = &xdg_shell_view->view; | 350 | struct sway_view *view = &xdg_shell_view->view; |
351 | view_update_app_id(view); | ||
330 | view_execute_criteria(view); | 352 | view_execute_criteria(view); |
331 | } | 353 | } |
332 | 354 | ||
@@ -334,29 +356,39 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { | |||
334 | struct sway_xdg_shell_view *xdg_shell_view = | 356 | struct sway_xdg_shell_view *xdg_shell_view = |
335 | wl_container_of(listener, xdg_shell_view, new_popup); | 357 | wl_container_of(listener, xdg_shell_view, new_popup); |
336 | struct wlr_xdg_popup *wlr_popup = data; | 358 | struct wlr_xdg_popup *wlr_popup = data; |
337 | popup_create(wlr_popup, &xdg_shell_view->view); | 359 | |
360 | struct sway_xdg_popup *popup = popup_create(wlr_popup, | ||
361 | &xdg_shell_view->view, root->layers.popup); | ||
362 | if (!popup) { | ||
363 | return; | ||
364 | } | ||
365 | |||
366 | int lx, ly; | ||
367 | wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly); | ||
368 | wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); | ||
369 | } | ||
370 | |||
371 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | ||
372 | struct sway_xdg_shell_view *xdg_shell_view = | ||
373 | wl_container_of(listener, xdg_shell_view, request_maximize); | ||
374 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; | ||
375 | wlr_xdg_surface_schedule_configure(toplevel->base); | ||
338 | } | 376 | } |
339 | 377 | ||
340 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { | 378 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { |
341 | struct sway_xdg_shell_view *xdg_shell_view = | 379 | struct sway_xdg_shell_view *xdg_shell_view = |
342 | wl_container_of(listener, xdg_shell_view, request_fullscreen); | 380 | wl_container_of(listener, xdg_shell_view, request_fullscreen); |
343 | struct wlr_xdg_toplevel_set_fullscreen_event *e = data; | 381 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; |
344 | struct wlr_xdg_surface *xdg_surface = | ||
345 | xdg_shell_view->view.wlr_xdg_surface; | ||
346 | struct sway_view *view = &xdg_shell_view->view; | 382 | struct sway_view *view = &xdg_shell_view->view; |
347 | 383 | ||
348 | if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, | 384 | if (!toplevel->base->surface->mapped) { |
349 | "xdg_shell requested fullscreen of surface with role %i", | ||
350 | xdg_surface->role)) { | ||
351 | return; | ||
352 | } | ||
353 | if (!xdg_surface->mapped) { | ||
354 | return; | 385 | return; |
355 | } | 386 | } |
356 | 387 | ||
357 | struct sway_container *container = view->container; | 388 | struct sway_container *container = view->container; |
358 | if (e->fullscreen && e->output && e->output->data) { | 389 | struct wlr_xdg_toplevel_requested *req = &toplevel->requested; |
359 | struct sway_output *output = e->output->data; | 390 | if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) { |
391 | struct sway_output *output = req->fullscreen_output->data; | ||
360 | struct sway_workspace *ws = output_get_active_workspace(output); | 392 | struct sway_workspace *ws = output_get_active_workspace(output); |
361 | if (ws && !container_is_scratchpad_hidden(container) && | 393 | if (ws && !container_is_scratchpad_hidden(container) && |
362 | container->pending.workspace != ws) { | 394 | container->pending.workspace != ws) { |
@@ -368,22 +400,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
368 | } | 400 | } |
369 | } | 401 | } |
370 | 402 | ||
371 | container_set_fullscreen(container, e->fullscreen); | 403 | container_set_fullscreen(container, req->fullscreen); |
372 | 404 | ||
373 | arrange_root(); | 405 | arrange_root(); |
374 | transaction_commit_dirty(); | 406 | transaction_commit_dirty(); |
375 | } | 407 | } |
376 | 408 | ||
377 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | ||
378 | struct wlr_xdg_surface *surface = data; | ||
379 | wlr_xdg_surface_schedule_configure(surface); | ||
380 | } | ||
381 | |||
382 | static void handle_request_move(struct wl_listener *listener, void *data) { | 409 | static void handle_request_move(struct wl_listener *listener, void *data) { |
383 | struct sway_xdg_shell_view *xdg_shell_view = | 410 | struct sway_xdg_shell_view *xdg_shell_view = |
384 | wl_container_of(listener, xdg_shell_view, request_move); | 411 | wl_container_of(listener, xdg_shell_view, request_move); |
385 | struct sway_view *view = &xdg_shell_view->view; | 412 | struct sway_view *view = &xdg_shell_view->view; |
386 | if (!container_is_floating(view->container)) { | 413 | if (!container_is_floating(view->container) || |
414 | view->container->pending.fullscreen_mode) { | ||
387 | return; | 415 | return; |
388 | } | 416 | } |
389 | struct wlr_xdg_toplevel_move_event *e = data; | 417 | struct wlr_xdg_toplevel_move_event *e = data; |
@@ -418,10 +446,9 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
418 | 446 | ||
419 | view_unmap(view); | 447 | view_unmap(view); |
420 | 448 | ||
421 | wl_list_remove(&xdg_shell_view->commit.link); | ||
422 | wl_list_remove(&xdg_shell_view->new_popup.link); | 449 | wl_list_remove(&xdg_shell_view->new_popup.link); |
423 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
424 | wl_list_remove(&xdg_shell_view->request_maximize.link); | 450 | wl_list_remove(&xdg_shell_view->request_maximize.link); |
451 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
425 | wl_list_remove(&xdg_shell_view->request_move.link); | 452 | wl_list_remove(&xdg_shell_view->request_move.link); |
426 | wl_list_remove(&xdg_shell_view->request_resize.link); | 453 | wl_list_remove(&xdg_shell_view->request_resize.link); |
427 | wl_list_remove(&xdg_shell_view->set_title.link); | 454 | wl_list_remove(&xdg_shell_view->set_title.link); |
@@ -432,62 +459,61 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
432 | struct sway_xdg_shell_view *xdg_shell_view = | 459 | struct sway_xdg_shell_view *xdg_shell_view = |
433 | wl_container_of(listener, xdg_shell_view, map); | 460 | wl_container_of(listener, xdg_shell_view, map); |
434 | struct sway_view *view = &xdg_shell_view->view; | 461 | struct sway_view *view = &xdg_shell_view->view; |
435 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 462 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
436 | 463 | ||
437 | view->natural_width = view->wlr_xdg_surface->geometry.width; | 464 | view->natural_width = toplevel->base->current.geometry.width; |
438 | view->natural_height = view->wlr_xdg_surface->geometry.height; | 465 | view->natural_height = toplevel->base->current.geometry.height; |
439 | if (!view->natural_width && !view->natural_height) { | 466 | if (!view->natural_width && !view->natural_height) { |
440 | view->natural_width = view->wlr_xdg_surface->surface->current.width; | 467 | view->natural_width = toplevel->base->surface->current.width; |
441 | view->natural_height = view->wlr_xdg_surface->surface->current.height; | 468 | view->natural_height = toplevel->base->surface->current.height; |
442 | } | 469 | } |
443 | 470 | ||
444 | bool csd = false; | 471 | bool csd = false; |
445 | 472 | ||
446 | if (!view->xdg_decoration) { | 473 | if (view->xdg_decoration) { |
474 | enum wlr_xdg_toplevel_decoration_v1_mode mode = | ||
475 | view->xdg_decoration->wlr_xdg_decoration->requested_mode; | ||
476 | csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; | ||
477 | } else { | ||
447 | struct sway_server_decoration *deco = | 478 | struct sway_server_decoration *deco = |
448 | decoration_from_surface(xdg_surface->surface); | 479 | decoration_from_surface(toplevel->base->surface); |
449 | csd = !deco || deco->wlr_server_decoration->mode == | 480 | csd = !deco || deco->wlr_server_decoration->mode == |
450 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; | 481 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; |
451 | |||
452 | } | 482 | } |
453 | 483 | ||
454 | view_map(view, view->wlr_xdg_surface->surface, | 484 | view_map(view, toplevel->base->surface, |
455 | xdg_surface->toplevel->client_pending.fullscreen, | 485 | toplevel->requested.fullscreen, |
456 | xdg_surface->toplevel->client_pending.fullscreen_output, | 486 | toplevel->requested.fullscreen_output, |
457 | csd); | 487 | csd); |
458 | 488 | ||
459 | transaction_commit_dirty(); | 489 | transaction_commit_dirty(); |
460 | 490 | ||
461 | xdg_shell_view->commit.notify = handle_commit; | ||
462 | wl_signal_add(&xdg_surface->surface->events.commit, | ||
463 | &xdg_shell_view->commit); | ||
464 | |||
465 | xdg_shell_view->new_popup.notify = handle_new_popup; | 491 | xdg_shell_view->new_popup.notify = handle_new_popup; |
466 | wl_signal_add(&xdg_surface->events.new_popup, | 492 | wl_signal_add(&toplevel->base->events.new_popup, |
467 | &xdg_shell_view->new_popup); | 493 | &xdg_shell_view->new_popup); |
468 | 494 | ||
469 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
470 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | ||
471 | &xdg_shell_view->request_fullscreen); | ||
472 | |||
473 | xdg_shell_view->request_maximize.notify = handle_request_maximize; | 495 | xdg_shell_view->request_maximize.notify = handle_request_maximize; |
474 | wl_signal_add(&xdg_surface->toplevel->events.request_maximize, | 496 | wl_signal_add(&toplevel->events.request_maximize, |
475 | &xdg_shell_view->request_maximize); | 497 | &xdg_shell_view->request_maximize); |
476 | 498 | ||
499 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
500 | wl_signal_add(&toplevel->events.request_fullscreen, | ||
501 | &xdg_shell_view->request_fullscreen); | ||
502 | |||
477 | xdg_shell_view->request_move.notify = handle_request_move; | 503 | xdg_shell_view->request_move.notify = handle_request_move; |
478 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | 504 | wl_signal_add(&toplevel->events.request_move, |
479 | &xdg_shell_view->request_move); | 505 | &xdg_shell_view->request_move); |
480 | 506 | ||
481 | xdg_shell_view->request_resize.notify = handle_request_resize; | 507 | xdg_shell_view->request_resize.notify = handle_request_resize; |
482 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | 508 | wl_signal_add(&toplevel->events.request_resize, |
483 | &xdg_shell_view->request_resize); | 509 | &xdg_shell_view->request_resize); |
484 | 510 | ||
485 | xdg_shell_view->set_title.notify = handle_set_title; | 511 | xdg_shell_view->set_title.notify = handle_set_title; |
486 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | 512 | wl_signal_add(&toplevel->events.set_title, |
487 | &xdg_shell_view->set_title); | 513 | &xdg_shell_view->set_title); |
488 | 514 | ||
489 | xdg_shell_view->set_app_id.notify = handle_set_app_id; | 515 | xdg_shell_view->set_app_id.notify = handle_set_app_id; |
490 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | 516 | wl_signal_add(&toplevel->events.set_app_id, |
491 | &xdg_shell_view->set_app_id); | 517 | &xdg_shell_view->set_app_id); |
492 | } | 518 | } |
493 | 519 | ||
@@ -501,7 +527,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
501 | wl_list_remove(&xdg_shell_view->destroy.link); | 527 | wl_list_remove(&xdg_shell_view->destroy.link); |
502 | wl_list_remove(&xdg_shell_view->map.link); | 528 | wl_list_remove(&xdg_shell_view->map.link); |
503 | wl_list_remove(&xdg_shell_view->unmap.link); | 529 | wl_list_remove(&xdg_shell_view->unmap.link); |
504 | view->wlr_xdg_surface = NULL; | 530 | wl_list_remove(&xdg_shell_view->commit.link); |
531 | view->wlr_xdg_toplevel = NULL; | ||
505 | if (view->xdg_decoration) { | 532 | if (view->xdg_decoration) { |
506 | view->xdg_decoration->view = NULL; | 533 | view->xdg_decoration->view = NULL; |
507 | } | 534 | } |
@@ -513,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface( | |||
513 | return xdg_surface->data; | 540 | return xdg_surface->data; |
514 | } | 541 | } |
515 | 542 | ||
516 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | 543 | void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { |
517 | struct wlr_xdg_surface *xdg_surface = data; | 544 | struct wlr_xdg_toplevel *xdg_toplevel = data; |
518 | |||
519 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
520 | sway_log(SWAY_DEBUG, "New xdg_shell popup"); | ||
521 | return; | ||
522 | } | ||
523 | 545 | ||
524 | sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", | 546 | sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", |
525 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 547 | xdg_toplevel->title, xdg_toplevel->app_id); |
526 | wlr_xdg_surface_ping(xdg_surface); | 548 | wlr_xdg_surface_ping(xdg_toplevel->base); |
527 | 549 | ||
528 | struct sway_xdg_shell_view *xdg_shell_view = | 550 | struct sway_xdg_shell_view *xdg_shell_view = |
529 | calloc(1, sizeof(struct sway_xdg_shell_view)); | 551 | calloc(1, sizeof(struct sway_xdg_shell_view)); |
@@ -531,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
531 | return; | 553 | return; |
532 | } | 554 | } |
533 | 555 | ||
534 | view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); | 556 | if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) { |
535 | xdg_shell_view->view.wlr_xdg_surface = xdg_surface; | 557 | free(xdg_shell_view); |
558 | return; | ||
559 | } | ||
560 | xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel; | ||
536 | 561 | ||
537 | xdg_shell_view->map.notify = handle_map; | 562 | xdg_shell_view->map.notify = handle_map; |
538 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); | 563 | wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map); |
539 | 564 | ||
540 | xdg_shell_view->unmap.notify = handle_unmap; | 565 | xdg_shell_view->unmap.notify = handle_unmap; |
541 | wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); | 566 | wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap); |
567 | |||
568 | xdg_shell_view->commit.notify = handle_commit; | ||
569 | wl_signal_add(&xdg_toplevel->base->surface->events.commit, | ||
570 | &xdg_shell_view->commit); | ||
542 | 571 | ||
543 | xdg_shell_view->destroy.notify = handle_destroy; | 572 | xdg_shell_view->destroy.notify = handle_destroy; |
544 | wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); | 573 | wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy); |
574 | |||
575 | wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); | ||
576 | |||
577 | xdg_toplevel->base->data = xdg_shell_view; | ||
545 | 578 | ||
546 | xdg_surface->data = xdg_shell_view; | 579 | wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel, |
580 | XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); | ||
547 | } | 581 | } |
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 66cb3b02..270cf08f 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -1,20 +1,23 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
5 | #include <wayland-server-core.h> | 4 | #include <wayland-server-core.h> |
6 | #include <wlr/types/wlr_output_layout.h> | 5 | #include <wlr/types/wlr_output_layout.h> |
7 | #include <wlr/types/wlr_output.h> | 6 | #include <wlr/types/wlr_output.h> |
7 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
8 | #include <wlr/types/wlr_scene.h> | ||
8 | #include <wlr/xwayland.h> | 9 | #include <wlr/xwayland.h> |
10 | #include <xcb/xcb_icccm.h> | ||
9 | #include "log.h" | 11 | #include "log.h" |
10 | #include "sway/desktop.h" | ||
11 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
12 | #include "sway/input/cursor.h" | 13 | #include "sway/input/cursor.h" |
13 | #include "sway/input/input-manager.h" | 14 | #include "sway/input/input-manager.h" |
14 | #include "sway/input/seat.h" | 15 | #include "sway/input/seat.h" |
15 | #include "sway/output.h" | 16 | #include "sway/output.h" |
17 | #include "sway/scene_descriptor.h" | ||
16 | #include "sway/tree/arrange.h" | 18 | #include "sway/tree/arrange.h" |
17 | #include "sway/tree/container.h" | 19 | #include "sway/tree/container.h" |
20 | #include "sway/server.h" | ||
18 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
19 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
20 | 23 | ||
@@ -42,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener, | |||
42 | ev->width, ev->height); | 45 | ev->width, ev->height); |
43 | } | 46 | } |
44 | 47 | ||
45 | static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { | ||
46 | struct sway_xwayland_unmanaged *surface = | ||
47 | wl_container_of(listener, surface, commit); | ||
48 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
49 | |||
50 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
51 | false); | ||
52 | } | ||
53 | |||
54 | static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { | 48 | static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { |
55 | struct sway_xwayland_unmanaged *surface = | 49 | struct sway_xwayland_unmanaged *surface = |
56 | wl_container_of(listener, surface, set_geometry); | 50 | wl_container_of(listener, surface, set_geometry); |
57 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 51 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
58 | 52 | ||
59 | if (xsurface->x != surface->lx || xsurface->y != surface->ly) { | 53 | wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y); |
60 | // Surface has moved | ||
61 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
62 | true); | ||
63 | surface->lx = xsurface->x; | ||
64 | surface->ly = xsurface->y; | ||
65 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
66 | true); | ||
67 | } | ||
68 | } | 54 | } |
69 | 55 | ||
70 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | 56 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { |
@@ -72,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | |||
72 | wl_container_of(listener, surface, map); | 58 | wl_container_of(listener, surface, map); |
73 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 59 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
74 | 60 | ||
75 | wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); | 61 | surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged, |
76 | 62 | xsurface->surface); | |
77 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); | ||
78 | surface->set_geometry.notify = unmanaged_handle_set_geometry; | ||
79 | 63 | ||
80 | wl_signal_add(&xsurface->surface->events.commit, &surface->commit); | 64 | if (surface->surface_scene) { |
81 | surface->commit.notify = unmanaged_handle_commit; | 65 | scene_descriptor_assign(&surface->surface_scene->buffer->node, |
66 | SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface); | ||
67 | wlr_scene_node_set_position(&surface->surface_scene->buffer->node, | ||
68 | xsurface->x, xsurface->y); | ||
82 | 69 | ||
83 | surface->lx = xsurface->x; | 70 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); |
84 | surface->ly = xsurface->y; | 71 | surface->set_geometry.notify = unmanaged_handle_set_geometry; |
85 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); | 72 | } |
86 | 73 | ||
87 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { | 74 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { |
88 | struct sway_seat *seat = input_manager_current_seat(); | 75 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -96,10 +83,13 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
96 | struct sway_xwayland_unmanaged *surface = | 83 | struct sway_xwayland_unmanaged *surface = |
97 | wl_container_of(listener, surface, unmap); | 84 | wl_container_of(listener, surface, unmap); |
98 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 85 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
99 | desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); | 86 | |
100 | wl_list_remove(&surface->link); | 87 | if (surface->surface_scene) { |
101 | wl_list_remove(&surface->set_geometry.link); | 88 | wl_list_remove(&surface->set_geometry.link); |
102 | wl_list_remove(&surface->commit.link); | 89 | |
90 | wlr_scene_node_destroy(&surface->surface_scene->buffer->node); | ||
91 | surface->surface_scene = NULL; | ||
92 | } | ||
103 | 93 | ||
104 | struct sway_seat *seat = input_manager_current_seat(); | 94 | struct sway_seat *seat = input_manager_current_seat(); |
105 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { | 95 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { |
@@ -121,18 +111,53 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
121 | } | 111 | } |
122 | } | 112 | } |
123 | 113 | ||
114 | static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { | ||
115 | struct sway_xwayland_unmanaged *surface = | ||
116 | wl_container_of(listener, surface, request_activate); | ||
117 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
118 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { | ||
119 | return; | ||
120 | } | ||
121 | struct sway_seat *seat = input_manager_current_seat(); | ||
122 | struct sway_container *focus = seat_get_focused_container(seat); | ||
123 | if (focus && focus->view && focus->view->pid != xsurface->pid) { | ||
124 | return; | ||
125 | } | ||
126 | |||
127 | seat_set_focus_surface(seat, xsurface->surface, false); | ||
128 | } | ||
129 | |||
130 | static void unmanaged_handle_associate(struct wl_listener *listener, void *data) { | ||
131 | struct sway_xwayland_unmanaged *surface = | ||
132 | wl_container_of(listener, surface, associate); | ||
133 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
134 | wl_signal_add(&xsurface->surface->events.map, &surface->map); | ||
135 | surface->map.notify = unmanaged_handle_map; | ||
136 | wl_signal_add(&xsurface->surface->events.unmap, &surface->unmap); | ||
137 | surface->unmap.notify = unmanaged_handle_unmap; | ||
138 | } | ||
139 | |||
140 | static void unmanaged_handle_dissociate(struct wl_listener *listener, void *data) { | ||
141 | struct sway_xwayland_unmanaged *surface = | ||
142 | wl_container_of(listener, surface, dissociate); | ||
143 | wl_list_remove(&surface->map.link); | ||
144 | wl_list_remove(&surface->unmap.link); | ||
145 | } | ||
146 | |||
124 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { | 147 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { |
125 | struct sway_xwayland_unmanaged *surface = | 148 | struct sway_xwayland_unmanaged *surface = |
126 | wl_container_of(listener, surface, destroy); | 149 | wl_container_of(listener, surface, destroy); |
127 | wl_list_remove(&surface->request_configure.link); | 150 | wl_list_remove(&surface->request_configure.link); |
128 | wl_list_remove(&surface->map.link); | 151 | wl_list_remove(&surface->associate.link); |
129 | wl_list_remove(&surface->unmap.link); | 152 | wl_list_remove(&surface->dissociate.link); |
130 | wl_list_remove(&surface->destroy.link); | 153 | wl_list_remove(&surface->destroy.link); |
131 | wl_list_remove(&surface->override_redirect.link); | 154 | wl_list_remove(&surface->override_redirect.link); |
155 | wl_list_remove(&surface->request_activate.link); | ||
132 | free(surface); | 156 | free(surface); |
133 | } | 157 | } |
134 | 158 | ||
135 | static void handle_map(struct wl_listener *listener, void *data); | 159 | static void handle_map(struct wl_listener *listener, void *data); |
160 | static void handle_associate(struct wl_listener *listener, void *data); | ||
136 | 161 | ||
137 | struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); | 162 | struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); |
138 | 163 | ||
@@ -141,14 +166,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi | |||
141 | wl_container_of(listener, surface, override_redirect); | 166 | wl_container_of(listener, surface, override_redirect); |
142 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 167 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
143 | 168 | ||
144 | bool mapped = xsurface->mapped; | 169 | bool associated = xsurface->surface != NULL; |
170 | bool mapped = associated && xsurface->surface->mapped; | ||
145 | if (mapped) { | 171 | if (mapped) { |
146 | unmanaged_handle_unmap(&surface->unmap, NULL); | 172 | unmanaged_handle_unmap(&surface->unmap, NULL); |
147 | } | 173 | } |
174 | if (associated) { | ||
175 | unmanaged_handle_dissociate(&surface->dissociate, NULL); | ||
176 | } | ||
148 | 177 | ||
149 | unmanaged_handle_destroy(&surface->destroy, NULL); | 178 | unmanaged_handle_destroy(&surface->destroy, NULL); |
150 | xsurface->data = NULL; | 179 | xsurface->data = NULL; |
180 | |||
151 | struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); | 181 | struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); |
182 | if (associated) { | ||
183 | handle_associate(&xwayland_view->associate, NULL); | ||
184 | } | ||
152 | if (mapped) { | 185 | if (mapped) { |
153 | handle_map(&xwayland_view->map, xsurface); | 186 | handle_map(&xwayland_view->map, xsurface); |
154 | } | 187 | } |
@@ -168,14 +201,16 @@ static struct sway_xwayland_unmanaged *create_unmanaged( | |||
168 | wl_signal_add(&xsurface->events.request_configure, | 201 | wl_signal_add(&xsurface->events.request_configure, |
169 | &surface->request_configure); | 202 | &surface->request_configure); |
170 | surface->request_configure.notify = unmanaged_handle_request_configure; | 203 | surface->request_configure.notify = unmanaged_handle_request_configure; |
171 | wl_signal_add(&xsurface->events.map, &surface->map); | 204 | wl_signal_add(&xsurface->events.associate, &surface->associate); |
172 | surface->map.notify = unmanaged_handle_map; | 205 | surface->associate.notify = unmanaged_handle_associate; |
173 | wl_signal_add(&xsurface->events.unmap, &surface->unmap); | 206 | wl_signal_add(&xsurface->events.dissociate, &surface->dissociate); |
174 | surface->unmap.notify = unmanaged_handle_unmap; | 207 | surface->dissociate.notify = unmanaged_handle_dissociate; |
175 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); | 208 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); |
176 | surface->destroy.notify = unmanaged_handle_destroy; | 209 | surface->destroy.notify = unmanaged_handle_destroy; |
177 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); | 210 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); |
178 | surface->override_redirect.notify = unmanaged_handle_override_redirect; | 211 | surface->override_redirect.notify = unmanaged_handle_override_redirect; |
212 | wl_signal_add(&xsurface->events.request_activate, &surface->request_activate); | ||
213 | surface->request_activate.notify = unmanaged_handle_request_activate; | ||
179 | 214 | ||
180 | return surface; | 215 | return surface; |
181 | } | 216 | } |
@@ -254,6 +289,7 @@ static void set_activated(struct sway_view *view, bool activated) { | |||
254 | } | 289 | } |
255 | 290 | ||
256 | wlr_xwayland_surface_activate(surface, activated); | 291 | wlr_xwayland_surface_activate(surface, activated); |
292 | wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); | ||
257 | } | 293 | } |
258 | 294 | ||
259 | static void set_tiled(struct sway_view *view, bool tiled) { | 295 | static void set_tiled(struct sway_view *view, bool tiled) { |
@@ -293,7 +329,7 @@ static bool wants_floating(struct sway_view *view) { | |||
293 | } | 329 | } |
294 | } | 330 | } |
295 | 331 | ||
296 | struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; | 332 | xcb_size_hints_t *size_hints = surface->size_hints; |
297 | if (size_hints != NULL && | 333 | if (size_hints != NULL && |
298 | size_hints->min_width > 0 && size_hints->min_height > 0 && | 334 | size_hints->min_width > 0 && size_hints->min_height > 0 && |
299 | (size_hints->max_width == size_hints->min_width || | 335 | (size_hints->max_width == size_hints->min_width || |
@@ -347,7 +383,7 @@ static void destroy(struct sway_view *view) { | |||
347 | static void get_constraints(struct sway_view *view, double *min_width, | 383 | static void get_constraints(struct sway_view *view, double *min_width, |
348 | double *max_width, double *min_height, double *max_height) { | 384 | double *max_width, double *min_height, double *max_height) { |
349 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; | 385 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; |
350 | struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; | 386 | xcb_size_hints_t *size_hints = surface->size_hints; |
351 | 387 | ||
352 | if (size_hints == NULL) { | 388 | if (size_hints == NULL) { |
353 | *min_width = DBL_MIN; | 389 | *min_width = DBL_MIN; |
@@ -377,17 +413,6 @@ static const struct sway_view_impl view_impl = { | |||
377 | .destroy = destroy, | 413 | .destroy = destroy, |
378 | }; | 414 | }; |
379 | 415 | ||
380 | static void get_geometry(struct sway_view *view, struct wlr_box *box) { | ||
381 | box->x = box->y = 0; | ||
382 | if (view->surface) { | ||
383 | box->width = view->surface->current.width; | ||
384 | box->height = view->surface->current.height; | ||
385 | } else { | ||
386 | box->width = 0; | ||
387 | box->height = 0; | ||
388 | } | ||
389 | } | ||
390 | |||
391 | static void handle_commit(struct wl_listener *listener, void *data) { | 416 | static void handle_commit(struct wl_listener *listener, void *data) { |
392 | struct sway_xwayland_view *xwayland_view = | 417 | struct sway_xwayland_view *xwayland_view = |
393 | wl_container_of(listener, xwayland_view, commit); | 418 | wl_container_of(listener, xwayland_view, commit); |
@@ -395,34 +420,38 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
395 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 420 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
396 | struct wlr_surface_state *state = &xsurface->surface->current; | 421 | struct wlr_surface_state *state = &xsurface->surface->current; |
397 | 422 | ||
398 | struct wlr_box new_geo; | 423 | struct wlr_box new_geo = {0}; |
399 | get_geometry(view, &new_geo); | 424 | new_geo.width = state->width; |
425 | new_geo.height = state->height; | ||
426 | |||
400 | bool new_size = new_geo.width != view->geometry.width || | 427 | bool new_size = new_geo.width != view->geometry.width || |
401 | new_geo.height != view->geometry.height || | 428 | new_geo.height != view->geometry.height; |
402 | new_geo.x != view->geometry.x || | ||
403 | new_geo.y != view->geometry.y; | ||
404 | 429 | ||
405 | if (new_size) { | 430 | if (new_size) { |
406 | // The client changed its surface size in this commit. For floating | 431 | // The client changed its surface size in this commit. For floating |
407 | // containers, we resize the container to match. For tiling containers, | 432 | // containers, we resize the container to match. For tiling containers, |
408 | // we only recenter the surface. | 433 | // we only recenter the surface. |
409 | desktop_damage_view(view); | ||
410 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | 434 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); |
411 | if (container_is_floating(view->container)) { | 435 | if (container_is_floating(view->container)) { |
412 | view_update_size(view); | 436 | view_update_size(view); |
413 | transaction_commit_dirty_client(); | 437 | transaction_commit_dirty_client(); |
414 | } else { | ||
415 | view_center_surface(view); | ||
416 | } | 438 | } |
417 | desktop_damage_view(view); | 439 | |
440 | view_center_and_clip_surface(view); | ||
418 | } | 441 | } |
419 | 442 | ||
420 | if (view->container->node.instruction) { | 443 | if (view->container->node.instruction) { |
421 | transaction_notify_view_ready_by_geometry(view, | 444 | bool successful = transaction_notify_view_ready_by_geometry(view, |
422 | xsurface->x, xsurface->y, state->width, state->height); | 445 | xsurface->x, xsurface->y, state->width, state->height); |
423 | } | ||
424 | 446 | ||
425 | view_damage_from(view); | 447 | // If we saved the view and this commit isn't what we're looking for |
448 | // that means the user will never actually see the buffers submitted to | ||
449 | // us here. Just send frame done events to these surfaces so they can | ||
450 | // commit another time for us. | ||
451 | if (view->saved_surface_tree && !successful) { | ||
452 | view_send_frame_done(view); | ||
453 | } | ||
454 | } | ||
426 | } | 455 | } |
427 | 456 | ||
428 | static void handle_destroy(struct wl_listener *listener, void *data) { | 457 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -435,6 +464,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
435 | wl_list_remove(&xwayland_view->commit.link); | 464 | wl_list_remove(&xwayland_view->commit.link); |
436 | } | 465 | } |
437 | 466 | ||
467 | xwayland_view->view.wlr_xwayland_surface = NULL; | ||
468 | |||
438 | wl_list_remove(&xwayland_view->destroy.link); | 469 | wl_list_remove(&xwayland_view->destroy.link); |
439 | wl_list_remove(&xwayland_view->request_configure.link); | 470 | wl_list_remove(&xwayland_view->request_configure.link); |
440 | wl_list_remove(&xwayland_view->request_fullscreen.link); | 471 | wl_list_remove(&xwayland_view->request_fullscreen.link); |
@@ -445,11 +476,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
445 | wl_list_remove(&xwayland_view->set_title.link); | 476 | wl_list_remove(&xwayland_view->set_title.link); |
446 | wl_list_remove(&xwayland_view->set_class.link); | 477 | wl_list_remove(&xwayland_view->set_class.link); |
447 | wl_list_remove(&xwayland_view->set_role.link); | 478 | wl_list_remove(&xwayland_view->set_role.link); |
479 | wl_list_remove(&xwayland_view->set_startup_id.link); | ||
448 | wl_list_remove(&xwayland_view->set_window_type.link); | 480 | wl_list_remove(&xwayland_view->set_window_type.link); |
449 | wl_list_remove(&xwayland_view->set_hints.link); | 481 | wl_list_remove(&xwayland_view->set_hints.link); |
450 | wl_list_remove(&xwayland_view->set_decorations.link); | 482 | wl_list_remove(&xwayland_view->set_decorations.link); |
451 | wl_list_remove(&xwayland_view->map.link); | 483 | wl_list_remove(&xwayland_view->associate.link); |
452 | wl_list_remove(&xwayland_view->unmap.link); | 484 | wl_list_remove(&xwayland_view->dissociate.link); |
453 | wl_list_remove(&xwayland_view->override_redirect.link); | 485 | wl_list_remove(&xwayland_view->override_redirect.link); |
454 | view_begin_destroy(&xwayland_view->view); | 486 | view_begin_destroy(&xwayland_view->view); |
455 | } | 487 | } |
@@ -463,16 +495,28 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
463 | return; | 495 | return; |
464 | } | 496 | } |
465 | 497 | ||
498 | wl_list_remove(&xwayland_view->commit.link); | ||
499 | wl_list_remove(&xwayland_view->surface_tree_destroy.link); | ||
500 | |||
501 | if (xwayland_view->surface_tree) { | ||
502 | wlr_scene_node_destroy(&xwayland_view->surface_tree->node); | ||
503 | xwayland_view->surface_tree = NULL; | ||
504 | } | ||
505 | |||
466 | view_unmap(view); | 506 | view_unmap(view); |
507 | } | ||
467 | 508 | ||
468 | wl_list_remove(&xwayland_view->commit.link); | 509 | static void handle_surface_tree_destroy(struct wl_listener *listener, void *data) { |
510 | struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, | ||
511 | surface_tree_destroy); | ||
512 | xwayland_view->surface_tree = NULL; | ||
469 | } | 513 | } |
470 | 514 | ||
471 | static void handle_map(struct wl_listener *listener, void *data) { | 515 | static void handle_map(struct wl_listener *listener, void *data) { |
472 | struct sway_xwayland_view *xwayland_view = | 516 | struct sway_xwayland_view *xwayland_view = |
473 | wl_container_of(listener, xwayland_view, map); | 517 | wl_container_of(listener, xwayland_view, map); |
474 | struct wlr_xwayland_surface *xsurface = data; | ||
475 | struct sway_view *view = &xwayland_view->view; | 518 | struct sway_view *view = &xwayland_view->view; |
519 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
476 | 520 | ||
477 | view->natural_width = xsurface->width; | 521 | view->natural_width = xsurface->width; |
478 | view->natural_height = xsurface->height; | 522 | view->natural_height = xsurface->height; |
@@ -485,23 +529,42 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
485 | // Put it back into the tree | 529 | // Put it back into the tree |
486 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); | 530 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); |
487 | 531 | ||
532 | xwayland_view->surface_tree = wlr_scene_subsurface_tree_create( | ||
533 | xwayland_view->view.content_tree, xsurface->surface); | ||
534 | |||
535 | if (xwayland_view->surface_tree) { | ||
536 | xwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy; | ||
537 | wl_signal_add(&xwayland_view->surface_tree->node.events.destroy, | ||
538 | &xwayland_view->surface_tree_destroy); | ||
539 | } | ||
540 | |||
488 | transaction_commit_dirty(); | 541 | transaction_commit_dirty(); |
489 | } | 542 | } |
490 | 543 | ||
544 | static void handle_dissociate(struct wl_listener *listener, void *data); | ||
545 | |||
491 | static void handle_override_redirect(struct wl_listener *listener, void *data) { | 546 | static void handle_override_redirect(struct wl_listener *listener, void *data) { |
492 | struct sway_xwayland_view *xwayland_view = | 547 | struct sway_xwayland_view *xwayland_view = |
493 | wl_container_of(listener, xwayland_view, override_redirect); | 548 | wl_container_of(listener, xwayland_view, override_redirect); |
494 | struct wlr_xwayland_surface *xsurface = data; | ||
495 | struct sway_view *view = &xwayland_view->view; | 549 | struct sway_view *view = &xwayland_view->view; |
550 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
496 | 551 | ||
497 | bool mapped = xsurface->mapped; | 552 | bool associated = xsurface->surface != NULL; |
553 | bool mapped = associated && xsurface->surface->mapped; | ||
498 | if (mapped) { | 554 | if (mapped) { |
499 | handle_unmap(&xwayland_view->unmap, NULL); | 555 | handle_unmap(&xwayland_view->unmap, NULL); |
500 | } | 556 | } |
557 | if (associated) { | ||
558 | handle_dissociate(&xwayland_view->dissociate, NULL); | ||
559 | } | ||
501 | 560 | ||
502 | handle_destroy(&xwayland_view->destroy, view); | 561 | handle_destroy(&xwayland_view->destroy, view); |
503 | xsurface->data = NULL; | 562 | xsurface->data = NULL; |
563 | |||
504 | struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); | 564 | struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); |
565 | if (associated) { | ||
566 | unmanaged_handle_associate(&unmanaged->associate, NULL); | ||
567 | } | ||
505 | if (mapped) { | 568 | if (mapped) { |
506 | unmanaged_handle_map(&unmanaged->map, xsurface); | 569 | unmanaged_handle_map(&unmanaged->map, xsurface); |
507 | } | 570 | } |
@@ -513,7 +576,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { | |||
513 | struct wlr_xwayland_surface_configure_event *ev = data; | 576 | struct wlr_xwayland_surface_configure_event *ev = data; |
514 | struct sway_view *view = &xwayland_view->view; | 577 | struct sway_view *view = &xwayland_view->view; |
515 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 578 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
516 | if (!xsurface->mapped) { | 579 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
517 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, | 580 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, |
518 | ev->width, ev->height); | 581 | ev->width, ev->height); |
519 | return; | 582 | return; |
@@ -542,7 +605,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
542 | wl_container_of(listener, xwayland_view, request_fullscreen); | 605 | wl_container_of(listener, xwayland_view, request_fullscreen); |
543 | struct sway_view *view = &xwayland_view->view; | 606 | struct sway_view *view = &xwayland_view->view; |
544 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 607 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
545 | if (!xsurface->mapped) { | 608 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
546 | return; | 609 | return; |
547 | } | 610 | } |
548 | container_set_fullscreen(view->container, xsurface->fullscreen); | 611 | container_set_fullscreen(view->container, xsurface->fullscreen); |
@@ -556,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) { | |||
556 | wl_container_of(listener, xwayland_view, request_minimize); | 619 | wl_container_of(listener, xwayland_view, request_minimize); |
557 | struct sway_view *view = &xwayland_view->view; | 620 | struct sway_view *view = &xwayland_view->view; |
558 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 621 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
559 | if (!xsurface->mapped) { | 622 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
560 | return; | 623 | return; |
561 | } | 624 | } |
562 | 625 | ||
@@ -571,10 +634,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) { | |||
571 | wl_container_of(listener, xwayland_view, request_move); | 634 | wl_container_of(listener, xwayland_view, request_move); |
572 | struct sway_view *view = &xwayland_view->view; | 635 | struct sway_view *view = &xwayland_view->view; |
573 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 636 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
574 | if (!xsurface->mapped) { | 637 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
575 | return; | 638 | return; |
576 | } | 639 | } |
577 | if (!container_is_floating(view->container)) { | 640 | if (!container_is_floating(view->container) || |
641 | view->container->pending.fullscreen_mode) { | ||
578 | return; | 642 | return; |
579 | } | 643 | } |
580 | struct sway_seat *seat = input_manager_current_seat(); | 644 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -586,7 +650,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { | |||
586 | wl_container_of(listener, xwayland_view, request_resize); | 650 | wl_container_of(listener, xwayland_view, request_resize); |
587 | struct sway_view *view = &xwayland_view->view; | 651 | struct sway_view *view = &xwayland_view->view; |
588 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 652 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
589 | if (!xsurface->mapped) { | 653 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
590 | return; | 654 | return; |
591 | } | 655 | } |
592 | if (!container_is_floating(view->container)) { | 656 | if (!container_is_floating(view->container)) { |
@@ -602,10 +666,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) { | |||
602 | wl_container_of(listener, xwayland_view, request_activate); | 666 | wl_container_of(listener, xwayland_view, request_activate); |
603 | struct sway_view *view = &xwayland_view->view; | 667 | struct sway_view *view = &xwayland_view->view; |
604 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 668 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
605 | if (!xsurface->mapped) { | 669 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
606 | return; | 670 | return; |
607 | } | 671 | } |
608 | view_request_activate(view); | 672 | view_request_activate(view, NULL); |
609 | 673 | ||
610 | transaction_commit_dirty(); | 674 | transaction_commit_dirty(); |
611 | } | 675 | } |
@@ -615,7 +679,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { | |||
615 | wl_container_of(listener, xwayland_view, set_title); | 679 | wl_container_of(listener, xwayland_view, set_title); |
616 | struct sway_view *view = &xwayland_view->view; | 680 | struct sway_view *view = &xwayland_view->view; |
617 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 681 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
618 | if (!xsurface->mapped) { | 682 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
619 | return; | 683 | return; |
620 | } | 684 | } |
621 | view_update_title(view, false); | 685 | view_update_title(view, false); |
@@ -627,7 +691,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) { | |||
627 | wl_container_of(listener, xwayland_view, set_class); | 691 | wl_container_of(listener, xwayland_view, set_class); |
628 | struct sway_view *view = &xwayland_view->view; | 692 | struct sway_view *view = &xwayland_view->view; |
629 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 693 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
630 | if (!xsurface->mapped) { | 694 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
631 | return; | 695 | return; |
632 | } | 696 | } |
633 | view_execute_criteria(view); | 697 | view_execute_criteria(view); |
@@ -638,18 +702,43 @@ static void handle_set_role(struct wl_listener *listener, void *data) { | |||
638 | wl_container_of(listener, xwayland_view, set_role); | 702 | wl_container_of(listener, xwayland_view, set_role); |
639 | struct sway_view *view = &xwayland_view->view; | 703 | struct sway_view *view = &xwayland_view->view; |
640 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 704 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
641 | if (!xsurface->mapped) { | 705 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
642 | return; | 706 | return; |
643 | } | 707 | } |
644 | view_execute_criteria(view); | 708 | view_execute_criteria(view); |
645 | } | 709 | } |
646 | 710 | ||
711 | static void handle_set_startup_id(struct wl_listener *listener, void *data) { | ||
712 | struct sway_xwayland_view *xwayland_view = | ||
713 | wl_container_of(listener, xwayland_view, set_startup_id); | ||
714 | struct sway_view *view = &xwayland_view->view; | ||
715 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
716 | if (xsurface->startup_id == NULL) { | ||
717 | return; | ||
718 | } | ||
719 | |||
720 | struct wlr_xdg_activation_token_v1 *token = | ||
721 | wlr_xdg_activation_v1_find_token( | ||
722 | server.xdg_activation_v1, xsurface->startup_id); | ||
723 | if (token == NULL) { | ||
724 | // Tried to activate with an unknown or expired token | ||
725 | return; | ||
726 | } | ||
727 | |||
728 | struct launcher_ctx *ctx = token->data; | ||
729 | if (token->data == NULL) { | ||
730 | // TODO: support external launchers in X | ||
731 | return; | ||
732 | } | ||
733 | view_assign_ctx(view, ctx); | ||
734 | } | ||
735 | |||
647 | static void handle_set_window_type(struct wl_listener *listener, void *data) { | 736 | static void handle_set_window_type(struct wl_listener *listener, void *data) { |
648 | struct sway_xwayland_view *xwayland_view = | 737 | struct sway_xwayland_view *xwayland_view = |
649 | wl_container_of(listener, xwayland_view, set_window_type); | 738 | wl_container_of(listener, xwayland_view, set_window_type); |
650 | struct sway_view *view = &xwayland_view->view; | 739 | struct sway_view *view = &xwayland_view->view; |
651 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 740 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
652 | if (!xsurface->mapped) { | 741 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
653 | return; | 742 | return; |
654 | } | 743 | } |
655 | view_execute_criteria(view); | 744 | view_execute_criteria(view); |
@@ -660,20 +749,39 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { | |||
660 | wl_container_of(listener, xwayland_view, set_hints); | 749 | wl_container_of(listener, xwayland_view, set_hints); |
661 | struct sway_view *view = &xwayland_view->view; | 750 | struct sway_view *view = &xwayland_view->view; |
662 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 751 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
663 | if (!xsurface->mapped) { | 752 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
664 | return; | 753 | return; |
665 | } | 754 | } |
666 | if (!xsurface->hints_urgency && view->urgent_timer) { | 755 | const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); |
756 | if (!hints_urgency && view->urgent_timer) { | ||
667 | // The view is in the timeout period. We'll ignore the request to | 757 | // The view is in the timeout period. We'll ignore the request to |
668 | // unset urgency so that the view remains urgent until the timer clears | 758 | // unset urgency so that the view remains urgent until the timer clears |
669 | // it. | 759 | // it. |
670 | return; | 760 | return; |
671 | } | 761 | } |
672 | if (view->allow_request_urgent) { | 762 | if (view->allow_request_urgent) { |
673 | view_set_urgent(view, (bool)xsurface->hints_urgency); | 763 | view_set_urgent(view, hints_urgency); |
674 | } | 764 | } |
675 | } | 765 | } |
676 | 766 | ||
767 | static void handle_associate(struct wl_listener *listener, void *data) { | ||
768 | struct sway_xwayland_view *xwayland_view = | ||
769 | wl_container_of(listener, xwayland_view, associate); | ||
770 | struct wlr_xwayland_surface *xsurface = | ||
771 | xwayland_view->view.wlr_xwayland_surface; | ||
772 | wl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap); | ||
773 | xwayland_view->unmap.notify = handle_unmap; | ||
774 | wl_signal_add(&xsurface->surface->events.map, &xwayland_view->map); | ||
775 | xwayland_view->map.notify = handle_map; | ||
776 | } | ||
777 | |||
778 | static void handle_dissociate(struct wl_listener *listener, void *data) { | ||
779 | struct sway_xwayland_view *xwayland_view = | ||
780 | wl_container_of(listener, xwayland_view, dissociate); | ||
781 | wl_list_remove(&xwayland_view->map.link); | ||
782 | wl_list_remove(&xwayland_view->unmap.link); | ||
783 | } | ||
784 | |||
677 | struct sway_view *view_from_wlr_xwayland_surface( | 785 | struct sway_view *view_from_wlr_xwayland_surface( |
678 | struct wlr_xwayland_surface *xsurface) { | 786 | struct wlr_xwayland_surface *xsurface) { |
679 | return xsurface->data; | 787 | return xsurface->data; |
@@ -689,7 +797,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
689 | return NULL; | 797 | return NULL; |
690 | } | 798 | } |
691 | 799 | ||
692 | view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); | 800 | if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) { |
801 | free(xwayland_view); | ||
802 | return NULL; | ||
803 | } | ||
693 | xwayland_view->view.wlr_xwayland_surface = xsurface; | 804 | xwayland_view->view.wlr_xwayland_surface = xsurface; |
694 | 805 | ||
695 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); | 806 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); |
@@ -728,6 +839,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
728 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); | 839 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); |
729 | xwayland_view->set_role.notify = handle_set_role; | 840 | xwayland_view->set_role.notify = handle_set_role; |
730 | 841 | ||
842 | wl_signal_add(&xsurface->events.set_startup_id, | ||
843 | &xwayland_view->set_startup_id); | ||
844 | xwayland_view->set_startup_id.notify = handle_set_startup_id; | ||
845 | |||
731 | wl_signal_add(&xsurface->events.set_window_type, | 846 | wl_signal_add(&xsurface->events.set_window_type, |
732 | &xwayland_view->set_window_type); | 847 | &xwayland_view->set_window_type); |
733 | xwayland_view->set_window_type.notify = handle_set_window_type; | 848 | xwayland_view->set_window_type.notify = handle_set_window_type; |
@@ -739,11 +854,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
739 | &xwayland_view->set_decorations); | 854 | &xwayland_view->set_decorations); |
740 | xwayland_view->set_decorations.notify = handle_set_decorations; | 855 | xwayland_view->set_decorations.notify = handle_set_decorations; |
741 | 856 | ||
742 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); | 857 | wl_signal_add(&xsurface->events.associate, &xwayland_view->associate); |
743 | xwayland_view->unmap.notify = handle_unmap; | 858 | xwayland_view->associate.notify = handle_associate; |
744 | 859 | ||
745 | wl_signal_add(&xsurface->events.map, &xwayland_view->map); | 860 | wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate); |
746 | xwayland_view->map.notify = handle_map; | 861 | xwayland_view->dissociate.notify = handle_dissociate; |
747 | 862 | ||
748 | wl_signal_add(&xsurface->events.set_override_redirect, | 863 | wl_signal_add(&xsurface->events.set_override_redirect, |
749 | &xwayland_view->override_redirect); | 864 | &xwayland_view->override_redirect); |
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 2fe5b202..3d04826c 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <math.h> | 2 | #include <math.h> |
4 | #include <libevdev/libevdev.h> | 3 | #include <libevdev/libevdev.h> |
@@ -7,8 +6,9 @@ | |||
7 | #include <time.h> | 6 | #include <time.h> |
8 | #include <strings.h> | 7 | #include <strings.h> |
9 | #include <wlr/types/wlr_cursor.h> | 8 | #include <wlr/types/wlr_cursor.h> |
10 | #include <wlr/types/wlr_idle.h> | 9 | #include <wlr/types/wlr_cursor_shape_v1.h> |
11 | #include <wlr/types/wlr_pointer.h> | 10 | #include <wlr/types/wlr_pointer.h> |
11 | #include <wlr/types/wlr_relative_pointer_v1.h> | ||
12 | #include <wlr/types/wlr_touch.h> | 12 | #include <wlr/types/wlr_touch.h> |
13 | #include <wlr/types/wlr_tablet_v2.h> | 13 | #include <wlr/types/wlr_tablet_v2.h> |
14 | #include <wlr/types/wlr_tablet_pad.h> | 14 | #include <wlr/types/wlr_tablet_pad.h> |
@@ -19,12 +19,12 @@ | |||
19 | #include "log.h" | 19 | #include "log.h" |
20 | #include "util.h" | 20 | #include "util.h" |
21 | #include "sway/commands.h" | 21 | #include "sway/commands.h" |
22 | #include "sway/desktop.h" | ||
23 | #include "sway/input/cursor.h" | 22 | #include "sway/input/cursor.h" |
24 | #include "sway/input/keyboard.h" | 23 | #include "sway/input/keyboard.h" |
25 | #include "sway/input/tablet.h" | 24 | #include "sway/input/tablet.h" |
26 | #include "sway/layers.h" | 25 | #include "sway/layers.h" |
27 | #include "sway/output.h" | 26 | #include "sway/output.h" |
27 | #include "sway/scene_descriptor.h" | ||
28 | #include "sway/tree/container.h" | 28 | #include "sway/tree/container.h" |
29 | #include "sway/tree/root.h" | 29 | #include "sway/tree/root.h" |
30 | #include "sway/tree/view.h" | 30 | #include "sway/tree/view.h" |
@@ -37,45 +37,6 @@ static uint32_t get_current_time_msec(void) { | |||
37 | return now.tv_sec * 1000 + now.tv_nsec / 1000000; | 37 | return now.tv_sec * 1000 + now.tv_nsec / 1000000; |
38 | } | 38 | } |
39 | 39 | ||
40 | static struct wlr_surface *layer_surface_at(struct sway_output *output, | ||
41 | struct wl_list *layer, double ox, double oy, double *sx, double *sy) { | ||
42 | struct sway_layer_surface *sway_layer; | ||
43 | wl_list_for_each_reverse(sway_layer, layer, link) { | ||
44 | double _sx = ox - sway_layer->geo.x; | ||
45 | double _sy = oy - sway_layer->geo.y; | ||
46 | struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( | ||
47 | sway_layer->layer_surface, _sx, _sy, sx, sy); | ||
48 | if (sub) { | ||
49 | return sub; | ||
50 | } | ||
51 | } | ||
52 | return NULL; | ||
53 | } | ||
54 | |||
55 | static bool surface_is_xdg_popup(struct wlr_surface *surface) { | ||
56 | if (wlr_surface_is_xdg_surface(surface)) { | ||
57 | struct wlr_xdg_surface *xdg_surface = | ||
58 | wlr_xdg_surface_from_wlr_surface(surface); | ||
59 | return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; | ||
60 | } | ||
61 | return false; | ||
62 | } | ||
63 | |||
64 | static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, | ||
65 | struct wl_list *layer, double ox, double oy, double *sx, double *sy) { | ||
66 | struct sway_layer_surface *sway_layer; | ||
67 | wl_list_for_each_reverse(sway_layer, layer, link) { | ||
68 | double _sx = ox - sway_layer->geo.x; | ||
69 | double _sy = oy - sway_layer->geo.y; | ||
70 | struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( | ||
71 | sway_layer->layer_surface, _sx, _sy, sx, sy); | ||
72 | if (sub && surface_is_xdg_popup(sub)) { | ||
73 | return sub; | ||
74 | } | ||
75 | } | ||
76 | return NULL; | ||
77 | } | ||
78 | |||
79 | /** | 40 | /** |
80 | * Returns the node at the cursor's position. If there is a surface at that | 41 | * Returns the node at the cursor's position. If there is a surface at that |
81 | * location, it is stored in **surface (it may not be a view). | 42 | * location, it is stored in **surface (it may not be a view). |
@@ -83,116 +44,101 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, | |||
83 | struct sway_node *node_at_coords( | 44 | struct sway_node *node_at_coords( |
84 | struct sway_seat *seat, double lx, double ly, | 45 | struct sway_seat *seat, double lx, double ly, |
85 | struct wlr_surface **surface, double *sx, double *sy) { | 46 | struct wlr_surface **surface, double *sx, double *sy) { |
86 | // check for unmanaged views first | 47 | struct wlr_scene_node *scene_node = NULL; |
87 | #if HAVE_XWAYLAND | 48 | |
88 | struct wl_list *unmanaged = &root->xwayland_unmanaged; | 49 | struct wlr_scene_node *node; |
89 | struct sway_xwayland_unmanaged *unmanaged_surface; | 50 | wl_list_for_each_reverse(node, &root->layer_tree->children, link) { |
90 | wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { | 51 | struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node); |
91 | struct wlr_xwayland_surface *xsurface = | 52 | |
92 | unmanaged_surface->wlr_xwayland_surface; | 53 | bool non_interactive = scene_descriptor_try_get(&layer->node, |
93 | 54 | SWAY_SCENE_DESC_NON_INTERACTIVE); | |
94 | double _sx = lx - unmanaged_surface->lx; | 55 | if (non_interactive) { |
95 | double _sy = ly - unmanaged_surface->ly; | 56 | continue; |
96 | if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { | 57 | } |
97 | *surface = xsurface->surface; | 58 | |
98 | *sx = _sx; | 59 | scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy); |
99 | *sy = _sy; | 60 | if (scene_node) { |
100 | return NULL; | 61 | break; |
101 | } | 62 | } |
102 | } | 63 | } |
64 | |||
65 | if (scene_node) { | ||
66 | // determine what wlr_surface we clicked on | ||
67 | if (scene_node->type == WLR_SCENE_NODE_BUFFER) { | ||
68 | struct wlr_scene_buffer *scene_buffer = | ||
69 | wlr_scene_buffer_from_node(scene_node); | ||
70 | struct wlr_scene_surface *scene_surface = | ||
71 | wlr_scene_surface_try_from_buffer(scene_buffer); | ||
72 | |||
73 | if (scene_surface) { | ||
74 | *surface = scene_surface->surface; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | // determine what container we clicked on | ||
79 | struct wlr_scene_node *current = scene_node; | ||
80 | while (true) { | ||
81 | struct sway_container *con = scene_descriptor_try_get(current, | ||
82 | SWAY_SCENE_DESC_CONTAINER); | ||
83 | |||
84 | if (!con) { | ||
85 | struct sway_view *view = scene_descriptor_try_get(current, | ||
86 | SWAY_SCENE_DESC_VIEW); | ||
87 | if (view) { | ||
88 | con = view->container; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | if (!con) { | ||
93 | struct sway_popup_desc *popup = | ||
94 | scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP); | ||
95 | if (popup && popup->view) { | ||
96 | con = popup->view->container; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | if (con && (!con->view || con->view->surface)) { | ||
101 | return &con->node; | ||
102 | } | ||
103 | |||
104 | if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) { | ||
105 | // We don't want to feed through the current workspace on | ||
106 | // layer shells | ||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | #if HAVE_XWAYLAND | ||
111 | if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) { | ||
112 | return NULL; | ||
113 | } | ||
103 | #endif | 114 | #endif |
104 | // find the output the cursor is on | 115 | |
116 | if (!current->parent) { | ||
117 | break; | ||
118 | } | ||
119 | |||
120 | current = ¤t->parent->node; | ||
121 | } | ||
122 | } | ||
123 | |||
124 | // if we aren't on a container, determine what workspace we are on | ||
105 | struct wlr_output *wlr_output = wlr_output_layout_output_at( | 125 | struct wlr_output *wlr_output = wlr_output_layout_output_at( |
106 | root->output_layout, lx, ly); | 126 | root->output_layout, lx, ly); |
107 | if (wlr_output == NULL) { | 127 | if (wlr_output == NULL) { |
108 | return NULL; | 128 | return NULL; |
109 | } | 129 | } |
130 | |||
110 | struct sway_output *output = wlr_output->data; | 131 | struct sway_output *output = wlr_output->data; |
111 | if (!output || !output->enabled) { | 132 | if (!output || !output->enabled) { |
112 | // output is being destroyed or is being enabled | 133 | // output is being destroyed or is being enabled |
113 | return NULL; | 134 | return NULL; |
114 | } | 135 | } |
115 | double ox = lx, oy = ly; | ||
116 | wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); | ||
117 | |||
118 | if (root->fullscreen_global) { | ||
119 | // Try fullscreen container | ||
120 | struct sway_container *con = tiling_container_at( | ||
121 | &root->fullscreen_global->node, lx, ly, surface, sx, sy); | ||
122 | if (con) { | ||
123 | return &con->node; | ||
124 | } | ||
125 | return NULL; | ||
126 | } | ||
127 | 136 | ||
128 | // find the focused workspace on the output for this seat | ||
129 | struct sway_workspace *ws = output_get_active_workspace(output); | 137 | struct sway_workspace *ws = output_get_active_workspace(output); |
130 | if (!ws) { | 138 | if (!ws) { |
131 | return NULL; | 139 | return NULL; |
132 | } | 140 | } |
133 | 141 | ||
134 | if ((*surface = layer_surface_at(output, | ||
135 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
136 | ox, oy, sx, sy))) { | ||
137 | return NULL; | ||
138 | } | ||
139 | if (ws->fullscreen) { | ||
140 | // Try transient containers | ||
141 | for (int i = 0; i < ws->floating->length; ++i) { | ||
142 | struct sway_container *floater = ws->floating->items[i]; | ||
143 | if (container_is_transient_for(floater, ws->fullscreen)) { | ||
144 | struct sway_container *con = tiling_container_at( | ||
145 | &floater->node, lx, ly, surface, sx, sy); | ||
146 | if (con) { | ||
147 | return &con->node; | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | // Try fullscreen container | ||
152 | struct sway_container *con = | ||
153 | tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); | ||
154 | if (con) { | ||
155 | return &con->node; | ||
156 | } | ||
157 | return NULL; | ||
158 | } | ||
159 | if ((*surface = layer_surface_popup_at(output, | ||
160 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
161 | ox, oy, sx, sy))) { | ||
162 | return NULL; | ||
163 | } | ||
164 | if ((*surface = layer_surface_popup_at(output, | ||
165 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
166 | ox, oy, sx, sy))) { | ||
167 | return NULL; | ||
168 | } | ||
169 | if ((*surface = layer_surface_popup_at(output, | ||
170 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
171 | ox, oy, sx, sy))) { | ||
172 | return NULL; | ||
173 | } | ||
174 | if ((*surface = layer_surface_at(output, | ||
175 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
176 | ox, oy, sx, sy))) { | ||
177 | return NULL; | ||
178 | } | ||
179 | |||
180 | struct sway_container *c; | ||
181 | if ((c = container_at(ws, lx, ly, surface, sx, sy))) { | ||
182 | return &c->node; | ||
183 | } | ||
184 | |||
185 | if ((*surface = layer_surface_at(output, | ||
186 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
187 | ox, oy, sx, sy))) { | ||
188 | return NULL; | ||
189 | } | ||
190 | if ((*surface = layer_surface_at(output, | ||
191 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
192 | ox, oy, sx, sy))) { | ||
193 | return NULL; | ||
194 | } | ||
195 | |||
196 | return &ws->node; | 142 | return &ws->node; |
197 | } | 143 | } |
198 | 144 | ||
@@ -218,7 +164,7 @@ void cursor_update_image(struct sway_cursor *cursor, | |||
218 | // Try a node's resize edge | 164 | // Try a node's resize edge |
219 | enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor); | 165 | enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor); |
220 | if (edge == WLR_EDGE_NONE) { | 166 | if (edge == WLR_EDGE_NONE) { |
221 | cursor_set_image(cursor, "left_ptr", NULL); | 167 | cursor_set_image(cursor, "default", NULL); |
222 | } else if (container_is_floating(node->sway_container)) { | 168 | } else if (container_is_floating(node->sway_container)) { |
223 | cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); | 169 | cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); |
224 | } else { | 170 | } else { |
@@ -229,12 +175,12 @@ void cursor_update_image(struct sway_cursor *cursor, | |||
229 | } | 175 | } |
230 | } | 176 | } |
231 | } else { | 177 | } else { |
232 | cursor_set_image(cursor, "left_ptr", NULL); | 178 | cursor_set_image(cursor, "default", NULL); |
233 | } | 179 | } |
234 | } | 180 | } |
235 | 181 | ||
236 | static void cursor_hide(struct sway_cursor *cursor) { | 182 | static void cursor_hide(struct sway_cursor *cursor) { |
237 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | 183 | wlr_cursor_unset_image(cursor->cursor); |
238 | cursor->hidden = true; | 184 | cursor->hidden = true; |
239 | wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); | 185 | wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); |
240 | } | 186 | } |
@@ -297,7 +243,7 @@ static enum sway_input_idle_source idle_source_from_device( | |||
297 | return IDLE_SOURCE_POINTER; | 243 | return IDLE_SOURCE_POINTER; |
298 | case WLR_INPUT_DEVICE_TOUCH: | 244 | case WLR_INPUT_DEVICE_TOUCH: |
299 | return IDLE_SOURCE_TOUCH; | 245 | return IDLE_SOURCE_TOUCH; |
300 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 246 | case WLR_INPUT_DEVICE_TABLET: |
301 | return IDLE_SOURCE_TABLET_TOOL; | 247 | return IDLE_SOURCE_TABLET_TOOL; |
302 | case WLR_INPUT_DEVICE_TABLET_PAD: | 248 | case WLR_INPUT_DEVICE_TABLET_PAD: |
303 | return IDLE_SOURCE_TABLET_PAD; | 249 | return IDLE_SOURCE_TABLET_PAD; |
@@ -346,7 +292,7 @@ void cursor_unhide(struct sway_cursor *cursor) { | |||
346 | wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); | 292 | wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); |
347 | } | 293 | } |
348 | 294 | ||
349 | static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | 295 | void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, |
350 | struct wlr_input_device *device, double dx, double dy, | 296 | struct wlr_input_device *device, double dx, double dy, |
351 | double dx_unaccel, double dy_unaccel) { | 297 | double dx_unaccel, double dy_unaccel) { |
352 | wlr_relative_pointer_manager_v1_send_relative_motion( | 298 | wlr_relative_pointer_manager_v1_send_relative_motion( |
@@ -383,33 +329,34 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
383 | static void handle_pointer_motion_relative( | 329 | static void handle_pointer_motion_relative( |
384 | struct wl_listener *listener, void *data) { | 330 | struct wl_listener *listener, void *data) { |
385 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); | 331 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); |
386 | struct wlr_event_pointer_motion *e = data; | 332 | struct wlr_pointer_motion_event *e = data; |
387 | cursor_handle_activity_from_device(cursor, e->device); | 333 | cursor_handle_activity_from_device(cursor, &e->pointer->base); |
388 | 334 | ||
389 | pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, | 335 | pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x, |
390 | e->unaccel_dx, e->unaccel_dy); | 336 | e->delta_y, e->unaccel_dx, e->unaccel_dy); |
391 | } | 337 | } |
392 | 338 | ||
393 | static void handle_pointer_motion_absolute( | 339 | static void handle_pointer_motion_absolute( |
394 | struct wl_listener *listener, void *data) { | 340 | struct wl_listener *listener, void *data) { |
395 | struct sway_cursor *cursor = | 341 | struct sway_cursor *cursor = |
396 | wl_container_of(listener, cursor, motion_absolute); | 342 | wl_container_of(listener, cursor, motion_absolute); |
397 | struct wlr_event_pointer_motion_absolute *event = data; | 343 | struct wlr_pointer_motion_absolute_event *event = data; |
398 | cursor_handle_activity_from_device(cursor, event->device); | 344 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
399 | 345 | ||
400 | double lx, ly; | 346 | double lx, ly; |
401 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 347 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base, |
402 | event->x, event->y, &lx, &ly); | 348 | event->x, event->y, &lx, &ly); |
403 | 349 | ||
404 | double dx = lx - cursor->cursor->x; | 350 | double dx = lx - cursor->cursor->x; |
405 | double dy = ly - cursor->cursor->y; | 351 | double dy = ly - cursor->cursor->y; |
406 | 352 | ||
407 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | 353 | pointer_motion(cursor, event->time_msec, &event->pointer->base, dx, dy, |
354 | dx, dy); | ||
408 | } | 355 | } |
409 | 356 | ||
410 | void dispatch_cursor_button(struct sway_cursor *cursor, | 357 | void dispatch_cursor_button(struct sway_cursor *cursor, |
411 | struct wlr_input_device *device, uint32_t time_msec, uint32_t button, | 358 | struct wlr_input_device *device, uint32_t time_msec, uint32_t button, |
412 | enum wlr_button_state state) { | 359 | enum wl_pointer_button_state state) { |
413 | if (time_msec == 0) { | 360 | if (time_msec == 0) { |
414 | time_msec = get_current_time_msec(); | 361 | time_msec = get_current_time_msec(); |
415 | } | 362 | } |
@@ -419,9 +366,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
419 | 366 | ||
420 | static void handle_pointer_button(struct wl_listener *listener, void *data) { | 367 | static void handle_pointer_button(struct wl_listener *listener, void *data) { |
421 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); | 368 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); |
422 | struct wlr_event_pointer_button *event = data; | 369 | struct wlr_pointer_button_event *event = data; |
423 | 370 | ||
424 | if (event->state == WLR_BUTTON_PRESSED) { | 371 | if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) { |
425 | cursor->pressed_button_count++; | 372 | cursor->pressed_button_count++; |
426 | } else { | 373 | } else { |
427 | if (cursor->pressed_button_count > 0) { | 374 | if (cursor->pressed_button_count > 0) { |
@@ -431,20 +378,20 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) { | |||
431 | } | 378 | } |
432 | } | 379 | } |
433 | 380 | ||
434 | cursor_handle_activity_from_device(cursor, event->device); | 381 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
435 | dispatch_cursor_button(cursor, event->device, | 382 | dispatch_cursor_button(cursor, &event->pointer->base, |
436 | event->time_msec, event->button, event->state); | 383 | event->time_msec, event->button, event->state); |
437 | } | 384 | } |
438 | 385 | ||
439 | void dispatch_cursor_axis(struct sway_cursor *cursor, | 386 | void dispatch_cursor_axis(struct sway_cursor *cursor, |
440 | struct wlr_event_pointer_axis *event) { | 387 | struct wlr_pointer_axis_event *event) { |
441 | seatop_pointer_axis(cursor->seat, event); | 388 | seatop_pointer_axis(cursor->seat, event); |
442 | } | 389 | } |
443 | 390 | ||
444 | static void handle_pointer_axis(struct wl_listener *listener, void *data) { | 391 | static void handle_pointer_axis(struct wl_listener *listener, void *data) { |
445 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); | 392 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); |
446 | struct wlr_event_pointer_axis *event = data; | 393 | struct wlr_pointer_axis_event *event = data; |
447 | cursor_handle_activity_from_device(cursor, event->device); | 394 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
448 | dispatch_cursor_axis(cursor, event); | 395 | dispatch_cursor_axis(cursor, event); |
449 | } | 396 | } |
450 | 397 | ||
@@ -455,93 +402,76 @@ static void handle_pointer_frame(struct wl_listener *listener, void *data) { | |||
455 | 402 | ||
456 | static void handle_touch_down(struct wl_listener *listener, void *data) { | 403 | static void handle_touch_down(struct wl_listener *listener, void *data) { |
457 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); | 404 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); |
458 | struct wlr_event_touch_down *event = data; | 405 | struct wlr_touch_down_event *event = data; |
459 | cursor_handle_activity_from_device(cursor, event->device); | 406 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
460 | cursor_hide(cursor); | 407 | cursor_hide(cursor); |
461 | 408 | ||
462 | struct sway_seat *seat = cursor->seat; | 409 | struct sway_seat *seat = cursor->seat; |
463 | struct wlr_seat *wlr_seat = seat->wlr_seat; | ||
464 | struct wlr_surface *surface = NULL; | ||
465 | 410 | ||
466 | double lx, ly; | 411 | double lx, ly; |
467 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 412 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, |
468 | event->x, event->y, &lx, &ly); | 413 | event->x, event->y, &lx, &ly); |
469 | double sx, sy; | ||
470 | struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy); | ||
471 | 414 | ||
472 | seat->touch_id = event->touch_id; | 415 | seat->touch_id = event->touch_id; |
473 | seat->touch_x = lx; | 416 | seat->touch_x = lx; |
474 | seat->touch_y = ly; | 417 | seat->touch_y = ly; |
475 | 418 | ||
476 | if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { | 419 | seatop_touch_down(seat, event, lx, ly); |
477 | if (seat_is_input_allowed(seat, surface)) { | 420 | } |
478 | wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, | 421 | |
479 | event->touch_id, sx, sy); | 422 | static void handle_touch_up(struct wl_listener *listener, void *data) { |
423 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); | ||
424 | struct wlr_touch_up_event *event = data; | ||
425 | cursor_handle_activity_from_device(cursor, &event->touch->base); | ||
480 | 426 | ||
481 | if (focused_node) { | 427 | struct sway_seat *seat = cursor->seat; |
482 | seat_set_focus(seat, focused_node); | 428 | |
483 | } | 429 | if (cursor->simulating_pointer_from_touch) { |
430 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { | ||
431 | cursor->pointer_touch_up = true; | ||
432 | dispatch_cursor_button(cursor, &event->touch->base, | ||
433 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); | ||
484 | } | 434 | } |
485 | } else if (!cursor->simulating_pointer_from_touch && | 435 | } else { |
486 | (!surface || seat_is_input_allowed(seat, surface))) { | 436 | seatop_touch_up(seat, event); |
487 | // Fallback to cursor simulation. | ||
488 | // The pointer_touch_id state is needed, so drags are not aborted when over | ||
489 | // a surface supporting touch and multi touch events don't interfere. | ||
490 | cursor->simulating_pointer_from_touch = true; | ||
491 | cursor->pointer_touch_id = seat->touch_id; | ||
492 | double dx, dy; | ||
493 | dx = lx - cursor->cursor->x; | ||
494 | dy = ly - cursor->cursor->y; | ||
495 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | ||
496 | dispatch_cursor_button(cursor, event->device, event->time_msec, | ||
497 | BTN_LEFT, WLR_BUTTON_PRESSED); | ||
498 | } | 437 | } |
499 | } | 438 | } |
500 | 439 | ||
501 | static void handle_touch_up(struct wl_listener *listener, void *data) { | 440 | static void handle_touch_cancel(struct wl_listener *listener, void *data) { |
502 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); | 441 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel); |
503 | struct wlr_event_touch_up *event = data; | 442 | struct wlr_touch_cancel_event *event = data; |
504 | cursor_handle_activity_from_device(cursor, event->device); | 443 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
505 | 444 | ||
506 | struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; | 445 | struct sway_seat *seat = cursor->seat; |
507 | 446 | ||
508 | if (cursor->simulating_pointer_from_touch) { | 447 | if (cursor->simulating_pointer_from_touch) { |
509 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { | 448 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { |
510 | cursor->pointer_touch_up = true; | 449 | cursor->pointer_touch_up = true; |
511 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 450 | dispatch_cursor_button(cursor, &event->touch->base, |
512 | BTN_LEFT, WLR_BUTTON_RELEASED); | 451 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); |
513 | } | 452 | } |
514 | } else { | 453 | } else { |
515 | wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); | 454 | seatop_touch_cancel(seat, event); |
516 | } | 455 | } |
517 | } | 456 | } |
518 | 457 | ||
519 | static void handle_touch_motion(struct wl_listener *listener, void *data) { | 458 | static void handle_touch_motion(struct wl_listener *listener, void *data) { |
520 | struct sway_cursor *cursor = | 459 | struct sway_cursor *cursor = |
521 | wl_container_of(listener, cursor, touch_motion); | 460 | wl_container_of(listener, cursor, touch_motion); |
522 | struct wlr_event_touch_motion *event = data; | 461 | struct wlr_touch_motion_event *event = data; |
523 | cursor_handle_activity_from_device(cursor, event->device); | 462 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
524 | 463 | ||
525 | struct sway_seat *seat = cursor->seat; | 464 | struct sway_seat *seat = cursor->seat; |
526 | struct wlr_seat *wlr_seat = seat->wlr_seat; | ||
527 | struct wlr_surface *surface = NULL; | ||
528 | 465 | ||
529 | double lx, ly; | 466 | double lx, ly; |
530 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 467 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, |
531 | event->x, event->y, &lx, &ly); | 468 | event->x, event->y, &lx, &ly); |
532 | double sx, sy; | ||
533 | node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); | ||
534 | 469 | ||
535 | if (seat->touch_id == event->touch_id) { | 470 | if (seat->touch_id == event->touch_id) { |
536 | seat->touch_x = lx; | 471 | seat->touch_x = lx; |
537 | seat->touch_y = ly; | 472 | seat->touch_y = ly; |
538 | 473 | ||
539 | struct sway_drag_icon *drag_icon; | 474 | drag_icons_update_position(seat); |
540 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
541 | if (drag_icon->seat == seat) { | ||
542 | drag_icon_update_position(drag_icon); | ||
543 | } | ||
544 | } | ||
545 | } | 475 | } |
546 | 476 | ||
547 | if (cursor->simulating_pointer_from_touch) { | 477 | if (cursor->simulating_pointer_from_touch) { |
@@ -549,11 +479,11 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { | |||
549 | double dx, dy; | 479 | double dx, dy; |
550 | dx = lx - cursor->cursor->x; | 480 | dx = lx - cursor->cursor->x; |
551 | dy = ly - cursor->cursor->y; | 481 | dy = ly - cursor->cursor->y; |
552 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | 482 | pointer_motion(cursor, event->time_msec, &event->touch->base, |
483 | dx, dy, dx, dy); | ||
553 | } | 484 | } |
554 | } else if (surface) { | 485 | } else { |
555 | wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, | 486 | seatop_touch_motion(seat, event, lx, ly); |
556 | event->touch_id, sx, sy); | ||
557 | } | 487 | } |
558 | } | 488 | } |
559 | 489 | ||
@@ -588,14 +518,15 @@ static void apply_mapping_from_region(struct wlr_input_device *device, | |||
588 | double x1 = region->x1, x2 = region->x2; | 518 | double x1 = region->x1, x2 = region->x2; |
589 | double y1 = region->y1, y2 = region->y2; | 519 | double y1 = region->y1, y2 = region->y2; |
590 | 520 | ||
591 | if (region->mm) { | 521 | if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) { |
592 | if (device->width_mm == 0 || device->height_mm == 0) { | 522 | struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); |
523 | if (tablet->width_mm == 0 || tablet->height_mm == 0) { | ||
593 | return; | 524 | return; |
594 | } | 525 | } |
595 | x1 /= device->width_mm; | 526 | x1 /= tablet->width_mm; |
596 | x2 /= device->width_mm; | 527 | x2 /= tablet->width_mm; |
597 | y1 /= device->height_mm; | 528 | y1 /= tablet->height_mm; |
598 | y2 /= device->height_mm; | 529 | y2 /= tablet->height_mm; |
599 | } | 530 | } |
600 | 531 | ||
601 | *x = apply_mapping_from_coord(x1, x2, *x); | 532 | *x = apply_mapping_from_coord(x1, x2, *x); |
@@ -657,8 +588,8 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor, | |||
657 | 588 | ||
658 | static void handle_tool_axis(struct wl_listener *listener, void *data) { | 589 | static void handle_tool_axis(struct wl_listener *listener, void *data) { |
659 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); | 590 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); |
660 | struct wlr_event_tablet_tool_axis *event = data; | 591 | struct wlr_tablet_tool_axis_event *event = data; |
661 | cursor_handle_activity_from_device(cursor, event->device); | 592 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
662 | 593 | ||
663 | struct sway_tablet_tool *sway_tool = event->tool->data; | 594 | struct sway_tablet_tool *sway_tool = event->tool->data; |
664 | if (!sway_tool) { | 595 | if (!sway_tool) { |
@@ -713,8 +644,8 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { | |||
713 | 644 | ||
714 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | 645 | static void handle_tool_tip(struct wl_listener *listener, void *data) { |
715 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); | 646 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); |
716 | struct wlr_event_tablet_tool_tip *event = data; | 647 | struct wlr_tablet_tool_tip_event *event = data; |
717 | cursor_handle_activity_from_device(cursor, event->device); | 648 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
718 | 649 | ||
719 | struct sway_tablet_tool *sway_tool = event->tool->data; | 650 | struct sway_tablet_tool *sway_tool = event->tool->data; |
720 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; | 651 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; |
@@ -729,8 +660,8 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { | |||
729 | if (cursor->simulating_pointer_from_tool_tip && | 660 | if (cursor->simulating_pointer_from_tool_tip && |
730 | event->state == WLR_TABLET_TOOL_TIP_UP) { | 661 | event->state == WLR_TABLET_TOOL_TIP_UP) { |
731 | cursor->simulating_pointer_from_tool_tip = false; | 662 | cursor->simulating_pointer_from_tool_tip = false; |
732 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 663 | dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, |
733 | BTN_LEFT, WLR_BUTTON_RELEASED); | 664 | BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); |
734 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 665 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
735 | } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { | 666 | } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { |
736 | // If we started holding the tool tip down on a surface that accepts | 667 | // If we started holding the tool tip down on a surface that accepts |
@@ -741,8 +672,8 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { | |||
741 | WLR_TABLET_TOOL_TIP_UP); | 672 | WLR_TABLET_TOOL_TIP_UP); |
742 | } else { | 673 | } else { |
743 | cursor->simulating_pointer_from_tool_tip = true; | 674 | cursor->simulating_pointer_from_tool_tip = true; |
744 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 675 | dispatch_cursor_button(cursor, &event->tablet->base, |
745 | BTN_LEFT, WLR_BUTTON_PRESSED); | 676 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); |
746 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 677 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
747 | } | 678 | } |
748 | } else { | 679 | } else { |
@@ -764,12 +695,13 @@ static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor, | |||
764 | static void handle_tool_proximity(struct wl_listener *listener, void *data) { | 695 | static void handle_tool_proximity(struct wl_listener *listener, void *data) { |
765 | struct sway_cursor *cursor = | 696 | struct sway_cursor *cursor = |
766 | wl_container_of(listener, cursor, tool_proximity); | 697 | wl_container_of(listener, cursor, tool_proximity); |
767 | struct wlr_event_tablet_tool_proximity *event = data; | 698 | struct wlr_tablet_tool_proximity_event *event = data; |
768 | cursor_handle_activity_from_device(cursor, event->device); | 699 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
769 | 700 | ||
770 | struct wlr_tablet_tool *tool = event->tool; | 701 | struct wlr_tablet_tool *tool = event->tool; |
771 | if (!tool->data) { | 702 | if (!tool->data) { |
772 | struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); | 703 | struct sway_tablet *tablet = get_tablet_for_device(cursor, |
704 | &event->tablet->base); | ||
773 | if (!tablet) { | 705 | if (!tablet) { |
774 | sway_log(SWAY_ERROR, "no tablet for tablet tool"); | 706 | sway_log(SWAY_ERROR, "no tablet for tablet tool"); |
775 | return; | 707 | return; |
@@ -794,8 +726,8 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) { | |||
794 | 726 | ||
795 | static void handle_tool_button(struct wl_listener *listener, void *data) { | 727 | static void handle_tool_button(struct wl_listener *listener, void *data) { |
796 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); | 728 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); |
797 | struct wlr_event_tablet_tool_button *event = data; | 729 | struct wlr_tablet_tool_button_event *event = data; |
798 | cursor_handle_activity_from_device(cursor, event->device); | 730 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
799 | 731 | ||
800 | struct sway_tablet_tool *sway_tool = event->tool->data; | 732 | struct sway_tablet_tool *sway_tool = event->tool->data; |
801 | if (!sway_tool) { | 733 | if (!sway_tool) { |
@@ -810,31 +742,71 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { | |||
810 | node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, | 742 | node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, |
811 | &surface, &sx, &sy); | 743 | &surface, &sx, &sy); |
812 | 744 | ||
813 | if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { | 745 | // TODO: floating resize should support graphics tablet events |
746 | struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat); | ||
747 | uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; | ||
748 | bool mod_pressed = modifiers & config->floating_mod; | ||
749 | |||
750 | bool surface_supports_tablet_events = | ||
751 | surface && wlr_surface_accepts_tablet_v2(tablet_v2, surface); | ||
752 | |||
753 | // Simulate pointer when: | ||
754 | // 1. The modifier key is pressed, OR | ||
755 | // 2. The surface under the cursor does not support tablet events. | ||
756 | bool should_simulate_pointer = mod_pressed || !surface_supports_tablet_events; | ||
757 | |||
758 | // Similar to tool tip, we need to selectively simulate mouse events, but we | ||
759 | // want to make sure that it is always consistent. Because all tool buttons | ||
760 | // currently map to BTN_RIGHT, we need to keep count of how many tool | ||
761 | // buttons are currently pressed down so we can send consistent events. | ||
762 | // | ||
763 | // The logic follows: | ||
764 | // - If we are already simulating the pointer, we should continue to do so | ||
765 | // until at least no tool button is held down. | ||
766 | // - If we should simulate the pointer and no tool button is currently held | ||
767 | // down, begin simulating the pointer. | ||
768 | // - If neither of the above are true, send the tablet events. | ||
769 | if ((cursor->tool_buttons > 0 && cursor->simulating_pointer_from_tool_button) | ||
770 | || (cursor->tool_buttons == 0 && should_simulate_pointer)) { | ||
771 | cursor->simulating_pointer_from_tool_button = true; | ||
772 | |||
814 | // TODO: the user may want to configure which tool buttons are mapped to | 773 | // TODO: the user may want to configure which tool buttons are mapped to |
815 | // which simulated pointer buttons | 774 | // which simulated pointer buttons |
816 | switch (event->state) { | 775 | switch (event->state) { |
817 | case WLR_BUTTON_PRESSED: | 776 | case WLR_BUTTON_PRESSED: |
818 | if (cursor->tool_buttons == 0) { | 777 | if (cursor->tool_buttons == 0) { |
819 | dispatch_cursor_button(cursor, event->device, | 778 | dispatch_cursor_button(cursor, &event->tablet->base, |
820 | event->time_msec, BTN_RIGHT, event->state); | 779 | event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED); |
821 | } | 780 | } |
822 | cursor->tool_buttons++; | ||
823 | break; | 781 | break; |
824 | case WLR_BUTTON_RELEASED: | 782 | case WLR_BUTTON_RELEASED: |
825 | if (cursor->tool_buttons == 1) { | 783 | if (cursor->tool_buttons <= 1) { |
826 | dispatch_cursor_button(cursor, event->device, | 784 | dispatch_cursor_button(cursor, &event->tablet->base, |
827 | event->time_msec, BTN_RIGHT, event->state); | 785 | event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED); |
828 | } | 786 | } |
829 | cursor->tool_buttons--; | ||
830 | break; | 787 | break; |
831 | } | 788 | } |
832 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 789 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
833 | return; | 790 | } else { |
791 | cursor->simulating_pointer_from_tool_button = false; | ||
792 | |||
793 | wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, | ||
794 | event->button, (enum zwp_tablet_pad_v2_button_state)event->state); | ||
834 | } | 795 | } |
835 | 796 | ||
836 | wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, | 797 | // Update tool button count. |
837 | event->button, (enum zwp_tablet_pad_v2_button_state)event->state); | 798 | switch (event->state) { |
799 | case WLR_BUTTON_PRESSED: | ||
800 | cursor->tool_buttons++; | ||
801 | break; | ||
802 | case WLR_BUTTON_RELEASED: | ||
803 | if (cursor->tool_buttons == 0) { | ||
804 | sway_log(SWAY_ERROR, "inconsistent tablet tool button events"); | ||
805 | } else { | ||
806 | cursor->tool_buttons--; | ||
807 | } | ||
808 | break; | ||
809 | } | ||
838 | } | 810 | } |
839 | 811 | ||
840 | static void check_constraint_region(struct sway_cursor *cursor) { | 812 | static void check_constraint_region(struct sway_cursor *cursor) { |
@@ -920,59 +892,68 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener, | |||
920 | event->hotspot_y, focused_client); | 892 | event->hotspot_y, focused_client); |
921 | } | 893 | } |
922 | 894 | ||
895 | static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) { | ||
896 | struct sway_cursor *cursor = wl_container_of( | ||
897 | listener, cursor, hold_begin); | ||
898 | struct wlr_pointer_hold_begin_event *event = data; | ||
899 | cursor_handle_activity_from_device(cursor, &event->pointer->base); | ||
900 | seatop_hold_begin(cursor->seat, event); | ||
901 | } | ||
902 | |||
903 | static void handle_pointer_hold_end(struct wl_listener *listener, void *data) { | ||
904 | struct sway_cursor *cursor = wl_container_of( | ||
905 | listener, cursor, hold_end); | ||
906 | struct wlr_pointer_hold_end_event *event = data; | ||
907 | cursor_handle_activity_from_device(cursor, &event->pointer->base); | ||
908 | seatop_hold_end(cursor->seat, event); | ||
909 | } | ||
910 | |||
923 | static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { | 911 | static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { |
924 | struct sway_cursor *cursor = wl_container_of( | 912 | struct sway_cursor *cursor = wl_container_of( |
925 | listener, cursor, pinch_begin); | 913 | listener, cursor, pinch_begin); |
926 | struct wlr_event_pointer_pinch_begin *event = data; | 914 | struct wlr_pointer_pinch_begin_event *event = data; |
927 | wlr_pointer_gestures_v1_send_pinch_begin( | 915 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
928 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 916 | seatop_pinch_begin(cursor->seat, event); |
929 | event->time_msec, event->fingers); | ||
930 | } | 917 | } |
931 | 918 | ||
932 | static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { | 919 | static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { |
933 | struct sway_cursor *cursor = wl_container_of( | 920 | struct sway_cursor *cursor = wl_container_of( |
934 | listener, cursor, pinch_update); | 921 | listener, cursor, pinch_update); |
935 | struct wlr_event_pointer_pinch_update *event = data; | 922 | struct wlr_pointer_pinch_update_event *event = data; |
936 | wlr_pointer_gestures_v1_send_pinch_update( | 923 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
937 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 924 | seatop_pinch_update(cursor->seat, event); |
938 | event->time_msec, event->dx, event->dy, | ||
939 | event->scale, event->rotation); | ||
940 | } | 925 | } |
941 | 926 | ||
942 | static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { | 927 | static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { |
943 | struct sway_cursor *cursor = wl_container_of( | 928 | struct sway_cursor *cursor = wl_container_of( |
944 | listener, cursor, pinch_end); | 929 | listener, cursor, pinch_end); |
945 | struct wlr_event_pointer_pinch_end *event = data; | 930 | struct wlr_pointer_pinch_end_event *event = data; |
946 | wlr_pointer_gestures_v1_send_pinch_end( | 931 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
947 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 932 | seatop_pinch_end(cursor->seat, event); |
948 | event->time_msec, event->cancelled); | ||
949 | } | 933 | } |
950 | 934 | ||
951 | static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { | 935 | static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { |
952 | struct sway_cursor *cursor = wl_container_of( | 936 | struct sway_cursor *cursor = wl_container_of( |
953 | listener, cursor, swipe_begin); | 937 | listener, cursor, swipe_begin); |
954 | struct wlr_event_pointer_swipe_begin *event = data; | 938 | struct wlr_pointer_swipe_begin_event *event = data; |
955 | wlr_pointer_gestures_v1_send_swipe_begin( | 939 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
956 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 940 | seatop_swipe_begin(cursor->seat, event); |
957 | event->time_msec, event->fingers); | ||
958 | } | 941 | } |
959 | 942 | ||
960 | static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { | 943 | static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { |
961 | struct sway_cursor *cursor = wl_container_of( | 944 | struct sway_cursor *cursor = wl_container_of( |
962 | listener, cursor, swipe_update); | 945 | listener, cursor, swipe_update); |
963 | struct wlr_event_pointer_swipe_update *event = data; | 946 | struct wlr_pointer_swipe_update_event *event = data; |
964 | wlr_pointer_gestures_v1_send_swipe_update( | 947 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
965 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 948 | seatop_swipe_update(cursor->seat, event); |
966 | event->time_msec, event->dx, event->dy); | ||
967 | } | 949 | } |
968 | 950 | ||
969 | static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { | 951 | static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { |
970 | struct sway_cursor *cursor = wl_container_of( | 952 | struct sway_cursor *cursor = wl_container_of( |
971 | listener, cursor, swipe_end); | 953 | listener, cursor, swipe_end); |
972 | struct wlr_event_pointer_swipe_end *event = data; | 954 | struct wlr_pointer_swipe_end_event *event = data; |
973 | wlr_pointer_gestures_v1_send_swipe_end( | 955 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
974 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 956 | seatop_swipe_end(cursor->seat, event); |
975 | event->time_msec, event->cancelled); | ||
976 | } | 957 | } |
977 | 958 | ||
978 | static void handle_image_surface_destroy(struct wl_listener *listener, | 959 | static void handle_image_surface_destroy(struct wl_listener *listener, |
@@ -1011,10 +992,9 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image, | |||
1011 | } | 992 | } |
1012 | 993 | ||
1013 | if (!image) { | 994 | if (!image) { |
1014 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | 995 | wlr_cursor_unset_image(cursor->cursor); |
1015 | } else if (!current_image || strcmp(current_image, image) != 0) { | 996 | } else if (!current_image || strcmp(current_image, image) != 0) { |
1016 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, | 997 | wlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image); |
1017 | cursor->cursor); | ||
1018 | } | 998 | } |
1019 | } | 999 | } |
1020 | 1000 | ||
@@ -1046,6 +1026,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { | |||
1046 | wl_event_source_remove(cursor->hide_source); | 1026 | wl_event_source_remove(cursor->hide_source); |
1047 | 1027 | ||
1048 | wl_list_remove(&cursor->image_surface_destroy.link); | 1028 | wl_list_remove(&cursor->image_surface_destroy.link); |
1029 | wl_list_remove(&cursor->hold_begin.link); | ||
1030 | wl_list_remove(&cursor->hold_end.link); | ||
1049 | wl_list_remove(&cursor->pinch_begin.link); | 1031 | wl_list_remove(&cursor->pinch_begin.link); |
1050 | wl_list_remove(&cursor->pinch_update.link); | 1032 | wl_list_remove(&cursor->pinch_update.link); |
1051 | wl_list_remove(&cursor->pinch_end.link); | 1033 | wl_list_remove(&cursor->pinch_end.link); |
@@ -1059,6 +1041,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { | |||
1059 | wl_list_remove(&cursor->frame.link); | 1041 | wl_list_remove(&cursor->frame.link); |
1060 | wl_list_remove(&cursor->touch_down.link); | 1042 | wl_list_remove(&cursor->touch_down.link); |
1061 | wl_list_remove(&cursor->touch_up.link); | 1043 | wl_list_remove(&cursor->touch_up.link); |
1044 | wl_list_remove(&cursor->touch_cancel.link); | ||
1062 | wl_list_remove(&cursor->touch_motion.link); | 1045 | wl_list_remove(&cursor->touch_motion.link); |
1063 | wl_list_remove(&cursor->touch_frame.link); | 1046 | wl_list_remove(&cursor->touch_frame.link); |
1064 | wl_list_remove(&cursor->tool_axis.link); | 1047 | wl_list_remove(&cursor->tool_axis.link); |
@@ -1095,19 +1078,24 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
1095 | wl_list_init(&cursor->image_surface_destroy.link); | 1078 | wl_list_init(&cursor->image_surface_destroy.link); |
1096 | cursor->image_surface_destroy.notify = handle_image_surface_destroy; | 1079 | cursor->image_surface_destroy.notify = handle_image_surface_destroy; |
1097 | 1080 | ||
1098 | cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); | 1081 | wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin); |
1099 | cursor->pinch_begin.notify = handle_pointer_pinch_begin; | 1082 | cursor->hold_begin.notify = handle_pointer_hold_begin; |
1083 | wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end); | ||
1084 | cursor->hold_end.notify = handle_pointer_hold_end; | ||
1085 | |||
1100 | wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); | 1086 | wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); |
1101 | cursor->pinch_update.notify = handle_pointer_pinch_update; | 1087 | cursor->pinch_begin.notify = handle_pointer_pinch_begin; |
1102 | wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); | 1088 | wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); |
1103 | cursor->pinch_end.notify = handle_pointer_pinch_end; | 1089 | cursor->pinch_update.notify = handle_pointer_pinch_update; |
1104 | wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); | 1090 | wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); |
1105 | cursor->swipe_begin.notify = handle_pointer_swipe_begin; | 1091 | cursor->pinch_end.notify = handle_pointer_pinch_end; |
1092 | |||
1106 | wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); | 1093 | wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); |
1107 | cursor->swipe_update.notify = handle_pointer_swipe_update; | 1094 | cursor->swipe_begin.notify = handle_pointer_swipe_begin; |
1108 | wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); | 1095 | wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); |
1109 | cursor->swipe_end.notify = handle_pointer_swipe_end; | 1096 | cursor->swipe_update.notify = handle_pointer_swipe_update; |
1110 | wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); | 1097 | wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); |
1098 | cursor->swipe_end.notify = handle_pointer_swipe_end; | ||
1111 | 1099 | ||
1112 | // input events | 1100 | // input events |
1113 | wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); | 1101 | wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); |
@@ -1132,6 +1120,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
1132 | wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); | 1120 | wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); |
1133 | cursor->touch_up.notify = handle_touch_up; | 1121 | cursor->touch_up.notify = handle_touch_up; |
1134 | 1122 | ||
1123 | wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel); | ||
1124 | cursor->touch_cancel.notify = handle_touch_cancel; | ||
1125 | |||
1135 | wl_signal_add(&wlr_cursor->events.touch_motion, | 1126 | wl_signal_add(&wlr_cursor->events.touch_motion, |
1136 | &cursor->touch_motion); | 1127 | &cursor->touch_motion); |
1137 | cursor->touch_motion.notify = handle_touch_motion; | 1128 | cursor->touch_motion.notify = handle_touch_motion; |
@@ -1224,11 +1215,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) { | |||
1224 | // Get event code from name | 1215 | // Get event code from name |
1225 | int code = libevdev_event_code_from_name(EV_KEY, name); | 1216 | int code = libevdev_event_code_from_name(EV_KEY, name); |
1226 | if (code == -1) { | 1217 | if (code == -1) { |
1227 | size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; | 1218 | *error = format_str("Unknown event %s", name); |
1228 | *error = malloc(len); | ||
1229 | if (*error) { | ||
1230 | snprintf(*error, len, "Unknown event %s", name); | ||
1231 | } | ||
1232 | return 0; | 1219 | return 0; |
1233 | } | 1220 | } |
1234 | return code; | 1221 | return code; |
@@ -1250,13 +1237,8 @@ uint32_t get_mouse_bindcode(const char *name, char **error) { | |||
1250 | } | 1237 | } |
1251 | const char *event = libevdev_event_code_get_name(EV_KEY, code); | 1238 | const char *event = libevdev_event_code_get_name(EV_KEY, code); |
1252 | if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { | 1239 | if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { |
1253 | size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", | 1240 | *error = format_str("Event code %d (%s) is not a button", |
1254 | code, event ? event : "(null)") + 1; | 1241 | code, event ? event : "(null)"); |
1255 | *error = malloc(len); | ||
1256 | if (*error) { | ||
1257 | snprintf(*error, len, "Event code %d (%s) is not a button", | ||
1258 | code, event ? event : "(null)"); | ||
1259 | } | ||
1260 | return 0; | 1242 | return 0; |
1261 | } | 1243 | } |
1262 | return code; | 1244 | return code; |
@@ -1289,12 +1271,15 @@ const char *get_mouse_button_name(uint32_t button) { | |||
1289 | static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { | 1271 | static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { |
1290 | struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; | 1272 | struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; |
1291 | 1273 | ||
1292 | if (constraint->current.committed & | 1274 | if (constraint->current.cursor_hint.enabled) { |
1293 | WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { | ||
1294 | double sx = constraint->current.cursor_hint.x; | 1275 | double sx = constraint->current.cursor_hint.x; |
1295 | double sy = constraint->current.cursor_hint.y; | 1276 | double sy = constraint->current.cursor_hint.y; |
1296 | 1277 | ||
1297 | struct sway_view *view = view_from_wlr_surface(constraint->surface); | 1278 | struct sway_view *view = view_from_wlr_surface(constraint->surface); |
1279 | if (!view) { | ||
1280 | return; | ||
1281 | } | ||
1282 | |||
1298 | struct sway_container *con = view->container; | 1283 | struct sway_container *con = view->container; |
1299 | 1284 | ||
1300 | double lx = sx + con->pending.content_x - view->geometry.x; | 1285 | double lx = sx + con->pending.content_x - view->geometry.x; |
@@ -1345,12 +1330,9 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) { | |||
1345 | sway_constraint->destroy.notify = handle_constraint_destroy; | 1330 | sway_constraint->destroy.notify = handle_constraint_destroy; |
1346 | wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); | 1331 | wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); |
1347 | 1332 | ||
1348 | struct sway_node *focus = seat_get_focus(seat); | 1333 | struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; |
1349 | if (focus && focus->type == N_CONTAINER && focus->sway_container->view) { | 1334 | if (surface && surface == constraint->surface) { |
1350 | struct wlr_surface *surface = focus->sway_container->view->surface; | 1335 | sway_cursor_constrain(seat->cursor, constraint); |
1351 | if (surface == constraint->surface) { | ||
1352 | sway_cursor_constrain(seat->cursor, constraint); | ||
1353 | } | ||
1354 | } | 1336 | } |
1355 | } | 1337 | } |
1356 | 1338 | ||
@@ -1408,3 +1390,26 @@ void sway_cursor_constrain(struct sway_cursor *cursor, | |||
1408 | wl_signal_add(&constraint->surface->events.commit, | 1390 | wl_signal_add(&constraint->surface->events.commit, |
1409 | &cursor->constraint_commit); | 1391 | &cursor->constraint_commit); |
1410 | } | 1392 | } |
1393 | |||
1394 | void handle_request_set_cursor_shape(struct wl_listener *listener, void *data) { | ||
1395 | const struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; | ||
1396 | struct sway_seat *seat = event->seat_client->seat->data; | ||
1397 | |||
1398 | if (!seatop_allows_set_cursor(seat)) { | ||
1399 | return; | ||
1400 | } | ||
1401 | |||
1402 | struct wl_client *focused_client = NULL; | ||
1403 | struct wlr_surface *focused_surface = seat->wlr_seat->pointer_state.focused_surface; | ||
1404 | if (focused_surface != NULL) { | ||
1405 | focused_client = wl_resource_get_client(focused_surface->resource); | ||
1406 | } | ||
1407 | |||
1408 | // TODO: check cursor mode | ||
1409 | if (focused_client == NULL || event->seat_client->client != focused_client) { | ||
1410 | sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client"); | ||
1411 | return; | ||
1412 | } | ||
1413 | |||
1414 | cursor_set_image(seat->cursor, wlr_cursor_shape_v1_name(event->shape), focused_client); | ||
1415 | } | ||
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index f04a8ce0..248ca34e 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c | |||
@@ -1,12 +1,12 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <math.h> | 4 | #include <math.h> |
5 | #include <assert.h> | ||
6 | #include <wlr/config.h> | ||
6 | #include <wlr/backend/libinput.h> | 7 | #include <wlr/backend/libinput.h> |
7 | #include <wlr/types/wlr_cursor.h> | 8 | #include <wlr/types/wlr_cursor.h> |
8 | #include <wlr/types/wlr_keyboard_group.h> | 9 | #include <wlr/types/wlr_keyboard_group.h> |
9 | #include <wlr/types/wlr_input_inhibitor.h> | ||
10 | #include <wlr/types/wlr_virtual_keyboard_v1.h> | 10 | #include <wlr/types/wlr_virtual_keyboard_v1.h> |
11 | #include <wlr/types/wlr_virtual_pointer_v1.h> | 11 | #include <wlr/types/wlr_virtual_pointer_v1.h> |
12 | #include "sway/config.h" | 12 | #include "sway/config.h" |
@@ -22,6 +22,10 @@ | |||
22 | #include "list.h" | 22 | #include "list.h" |
23 | #include "log.h" | 23 | #include "log.h" |
24 | 24 | ||
25 | #if WLR_HAS_LIBINPUT_BACKEND | ||
26 | #include <wlr/backend/libinput.h> | ||
27 | #endif | ||
28 | |||
25 | #define DEFAULT_SEAT "seat0" | 29 | #define DEFAULT_SEAT "seat0" |
26 | 30 | ||
27 | struct input_config *current_input_config = NULL; | 31 | struct input_config *current_input_config = NULL; |
@@ -63,8 +67,15 @@ struct sway_seat *input_manager_sway_seat_from_wlr_seat(struct wlr_seat *wlr_sea | |||
63 | } | 67 | } |
64 | 68 | ||
65 | char *input_device_get_identifier(struct wlr_input_device *device) { | 69 | char *input_device_get_identifier(struct wlr_input_device *device) { |
66 | int vendor = device->vendor; | 70 | int vendor = 0, product = 0; |
67 | int product = device->product; | 71 | #if WLR_HAS_LIBINPUT_BACKEND |
72 | if (wlr_input_device_is_libinput(device)) { | ||
73 | struct libinput_device *libinput_dev = wlr_libinput_get_device_handle(device); | ||
74 | vendor = libinput_device_get_id_vendor(libinput_dev); | ||
75 | product = libinput_device_get_id_product(libinput_dev); | ||
76 | } | ||
77 | #endif | ||
78 | |||
68 | char *name = strdup(device->name ? device->name : ""); | 79 | char *name = strdup(device->name ? device->name : ""); |
69 | strip_whitespace(name); | 80 | strip_whitespace(name); |
70 | 81 | ||
@@ -76,20 +87,13 @@ char *input_device_get_identifier(struct wlr_input_device *device) { | |||
76 | } | 87 | } |
77 | } | 88 | } |
78 | 89 | ||
79 | const char *fmt = "%d:%d:%s"; | 90 | char *identifier = format_str("%d:%d:%s", vendor, product, name); |
80 | int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; | ||
81 | char *identifier = malloc(len); | ||
82 | if (!identifier) { | ||
83 | sway_log(SWAY_ERROR, "Unable to allocate unique input device name"); | ||
84 | return NULL; | ||
85 | } | ||
86 | |||
87 | snprintf(identifier, len, fmt, vendor, product, name); | ||
88 | free(name); | 91 | free(name); |
89 | return identifier; | 92 | return identifier; |
90 | } | 93 | } |
91 | 94 | ||
92 | static bool device_is_touchpad(struct sway_input_device *device) { | 95 | static bool device_is_touchpad(struct sway_input_device *device) { |
96 | #if WLR_HAS_LIBINPUT_BACKEND | ||
93 | if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || | 97 | if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || |
94 | !wlr_input_device_is_libinput(device->wlr_device)) { | 98 | !wlr_input_device_is_libinput(device->wlr_device)) { |
95 | return false; | 99 | return false; |
@@ -99,6 +103,9 @@ static bool device_is_touchpad(struct sway_input_device *device) { | |||
99 | wlr_libinput_get_device_handle(device->wlr_device); | 103 | wlr_libinput_get_device_handle(device->wlr_device); |
100 | 104 | ||
101 | return libinput_device_config_tap_get_finger_count(libinput_device) > 0; | 105 | return libinput_device_config_tap_get_finger_count(libinput_device) > 0; |
106 | #else | ||
107 | return false; | ||
108 | #endif | ||
102 | } | 109 | } |
103 | 110 | ||
104 | const char *input_device_get_type(struct sway_input_device *device) { | 111 | const char *input_device_get_type(struct sway_input_device *device) { |
@@ -113,7 +120,7 @@ const char *input_device_get_type(struct sway_input_device *device) { | |||
113 | return "keyboard"; | 120 | return "keyboard"; |
114 | case WLR_INPUT_DEVICE_TOUCH: | 121 | case WLR_INPUT_DEVICE_TOUCH: |
115 | return "touch"; | 122 | return "touch"; |
116 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 123 | case WLR_INPUT_DEVICE_TABLET: |
117 | return "tablet_tool"; | 124 | return "tablet_tool"; |
118 | case WLR_INPUT_DEVICE_TABLET_PAD: | 125 | case WLR_INPUT_DEVICE_TABLET_PAD: |
119 | return "tablet_pad"; | 126 | return "tablet_pad"; |
@@ -236,7 +243,11 @@ static void handle_new_input(struct wl_listener *listener, void *data) { | |||
236 | 243 | ||
237 | apply_input_type_config(input_device); | 244 | apply_input_type_config(input_device); |
238 | 245 | ||
239 | sway_input_configure_libinput_device(input_device); | 246 | #if WLR_HAS_LIBINPUT_BACKEND |
247 | bool config_changed = sway_input_configure_libinput_device(input_device); | ||
248 | #else | ||
249 | bool config_changed = false; | ||
250 | #endif | ||
240 | 251 | ||
241 | wl_signal_add(&device->events.destroy, &input_device->device_destroy); | 252 | wl_signal_add(&device->events.destroy, &input_device->device_destroy); |
242 | input_device->device_destroy.notify = handle_device_destroy; | 253 | input_device->device_destroy.notify = handle_device_destroy; |
@@ -274,29 +285,9 @@ static void handle_new_input(struct wl_listener *listener, void *data) { | |||
274 | } | 285 | } |
275 | 286 | ||
276 | ipc_event_input("added", input_device); | 287 | ipc_event_input("added", input_device); |
277 | } | ||
278 | 288 | ||
279 | static void handle_inhibit_activate(struct wl_listener *listener, void *data) { | 289 | if (config_changed) { |
280 | struct sway_input_manager *input_manager = wl_container_of( | 290 | ipc_event_input("libinput_config", input_device); |
281 | listener, input_manager, inhibit_activate); | ||
282 | struct sway_seat *seat; | ||
283 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
284 | seat_set_exclusive_client(seat, input_manager->inhibit->active_client); | ||
285 | } | ||
286 | } | ||
287 | |||
288 | static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) { | ||
289 | struct sway_input_manager *input_manager = wl_container_of( | ||
290 | listener, input_manager, inhibit_deactivate); | ||
291 | struct sway_seat *seat; | ||
292 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
293 | seat_set_exclusive_client(seat, NULL); | ||
294 | struct sway_node *previous = seat_get_focus(seat); | ||
295 | if (previous) { | ||
296 | // Hack to get seat to re-focus the return value of get_focus | ||
297 | seat_set_focus(seat, NULL); | ||
298 | seat_set_focus(seat, previous); | ||
299 | } | ||
300 | } | 291 | } |
301 | } | 292 | } |
302 | 293 | ||
@@ -377,7 +368,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) { | |||
377 | struct sway_input_manager *input_manager = | 368 | struct sway_input_manager *input_manager = |
378 | wl_container_of(listener, input_manager, virtual_keyboard_new); | 369 | wl_container_of(listener, input_manager, virtual_keyboard_new); |
379 | struct wlr_virtual_keyboard_v1 *keyboard = data; | 370 | struct wlr_virtual_keyboard_v1 *keyboard = data; |
380 | struct wlr_input_device *device = &keyboard->input_device; | 371 | struct wlr_input_device *device = &keyboard->keyboard.base; |
381 | 372 | ||
382 | // TODO: Amend protocol to allow NULL seat | 373 | // TODO: Amend protocol to allow NULL seat |
383 | struct sway_seat *seat = keyboard->seat ? | 374 | struct sway_seat *seat = keyboard->seat ? |
@@ -410,7 +401,7 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) { | |||
410 | wl_container_of(listener, input_manager, virtual_pointer_new); | 401 | wl_container_of(listener, input_manager, virtual_pointer_new); |
411 | struct wlr_virtual_pointer_v1_new_pointer_event *event = data; | 402 | struct wlr_virtual_pointer_v1_new_pointer_event *event = data; |
412 | struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; | 403 | struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; |
413 | struct wlr_input_device *device = &pointer->input_device; | 404 | struct wlr_input_device *device = &pointer->pointer.base; |
414 | 405 | ||
415 | struct sway_seat *seat = event->suggested_seat ? | 406 | struct sway_seat *seat = event->suggested_seat ? |
416 | input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : | 407 | input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : |
@@ -442,6 +433,20 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) { | |||
442 | } | 433 | } |
443 | } | 434 | } |
444 | 435 | ||
436 | static void handle_transient_seat_manager_create_seat( | ||
437 | struct wl_listener *listener, void *data) { | ||
438 | struct wlr_transient_seat_v1 *transient_seat = data; | ||
439 | static uint64_t i; | ||
440 | char name[256]; | ||
441 | snprintf(name, sizeof(name), "transient-%"PRIx64, i++); | ||
442 | struct sway_seat *seat = seat_create(name); | ||
443 | if (seat && seat->wlr_seat) { | ||
444 | wlr_transient_seat_v1_ready(transient_seat, seat->wlr_seat); | ||
445 | } else { | ||
446 | wlr_transient_seat_v1_deny(transient_seat); | ||
447 | } | ||
448 | } | ||
449 | |||
445 | struct sway_input_manager *input_manager_create(struct sway_server *server) { | 450 | struct sway_input_manager *input_manager_create(struct sway_server *server) { |
446 | struct sway_input_manager *input = | 451 | struct sway_input_manager *input = |
447 | calloc(1, sizeof(struct sway_input_manager)); | 452 | calloc(1, sizeof(struct sway_input_manager)); |
@@ -468,14 +473,6 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { | |||
468 | &input->virtual_pointer_new); | 473 | &input->virtual_pointer_new); |
469 | input->virtual_pointer_new.notify = handle_virtual_pointer; | 474 | input->virtual_pointer_new.notify = handle_virtual_pointer; |
470 | 475 | ||
471 | input->inhibit = wlr_input_inhibit_manager_create(server->wl_display); | ||
472 | input->inhibit_activate.notify = handle_inhibit_activate; | ||
473 | wl_signal_add(&input->inhibit->events.activate, | ||
474 | &input->inhibit_activate); | ||
475 | input->inhibit_deactivate.notify = handle_inhibit_deactivate; | ||
476 | wl_signal_add(&input->inhibit->events.deactivate, | ||
477 | &input->inhibit_deactivate); | ||
478 | |||
479 | input->keyboard_shortcuts_inhibit = | 476 | input->keyboard_shortcuts_inhibit = |
480 | wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); | 477 | wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); |
481 | input->keyboard_shortcuts_inhibit_new_inhibitor.notify = | 478 | input->keyboard_shortcuts_inhibit_new_inhibitor.notify = |
@@ -483,6 +480,17 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { | |||
483 | wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, | 480 | wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, |
484 | &input->keyboard_shortcuts_inhibit_new_inhibitor); | 481 | &input->keyboard_shortcuts_inhibit_new_inhibitor); |
485 | 482 | ||
483 | input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display); | ||
484 | |||
485 | input->transient_seat_manager = | ||
486 | wlr_transient_seat_manager_v1_create(server->wl_display); | ||
487 | assert(input->transient_seat_manager); | ||
488 | |||
489 | input->transient_seat_create.notify = | ||
490 | handle_transient_seat_manager_create_seat; | ||
491 | wl_signal_add(&input->transient_seat_manager->events.create_seat, | ||
492 | &input->transient_seat_create); | ||
493 | |||
486 | return input; | 494 | return input; |
487 | } | 495 | } |
488 | 496 | ||
@@ -520,21 +528,50 @@ static void retranslate_keysyms(struct input_config *input_config) { | |||
520 | return; | 528 | return; |
521 | } | 529 | } |
522 | } | 530 | } |
531 | |||
532 | for (int i = 0; i < config->input_type_configs->length; ++i) { | ||
533 | struct input_config *ic = config->input_type_configs->items[i]; | ||
534 | if (ic->xkb_layout || ic->xkb_file) { | ||
535 | // this is the first config with xkb_layout or xkb_file | ||
536 | if (ic->identifier == input_config->identifier) { | ||
537 | translate_keysyms(ic); | ||
538 | } | ||
539 | |||
540 | return; | ||
541 | } | ||
542 | } | ||
523 | } | 543 | } |
524 | 544 | ||
525 | static void input_manager_configure_input( | 545 | static void input_manager_configure_input( |
526 | struct sway_input_device *input_device) { | 546 | struct sway_input_device *input_device) { |
527 | sway_input_configure_libinput_device(input_device); | 547 | #if WLR_HAS_LIBINPUT_BACKEND |
548 | bool config_changed = sway_input_configure_libinput_device(input_device); | ||
549 | #else | ||
550 | bool config_changed = false; | ||
551 | #endif | ||
528 | struct sway_seat *seat = NULL; | 552 | struct sway_seat *seat = NULL; |
529 | wl_list_for_each(seat, &server.input->seats, link) { | 553 | wl_list_for_each(seat, &server.input->seats, link) { |
530 | seat_configure_device(seat, input_device); | 554 | seat_configure_device(seat, input_device); |
531 | } | 555 | } |
556 | if (config_changed) { | ||
557 | ipc_event_input("libinput_config", input_device); | ||
558 | } | ||
532 | } | 559 | } |
533 | 560 | ||
534 | void input_manager_configure_all_inputs(void) { | 561 | void input_manager_configure_all_input_mappings(void) { |
535 | struct sway_input_device *input_device = NULL; | 562 | struct sway_input_device *input_device; |
536 | wl_list_for_each(input_device, &server.input->devices, link) { | 563 | wl_list_for_each(input_device, &server.input->devices, link) { |
537 | input_manager_configure_input(input_device); | 564 | struct sway_seat *seat; |
565 | wl_list_for_each(seat, &server.input->seats, link) { | ||
566 | seat_configure_device_mapping(seat, input_device); | ||
567 | } | ||
568 | |||
569 | #if WLR_HAS_LIBINPUT_BACKEND | ||
570 | // Input devices mapped to unavailable outputs get their libinput | ||
571 | // send_events setting switched to false. We need to re-enable this | ||
572 | // when the output appears. | ||
573 | sway_input_configure_libinput_device_send_events(input_device); | ||
574 | #endif | ||
538 | } | 575 | } |
539 | } | 576 | } |
540 | 577 | ||
@@ -556,7 +593,9 @@ void input_manager_apply_input_config(struct input_config *input_config) { | |||
556 | } | 593 | } |
557 | 594 | ||
558 | void input_manager_reset_input(struct sway_input_device *input_device) { | 595 | void input_manager_reset_input(struct sway_input_device *input_device) { |
596 | #if WLR_HAS_LIBINPUT_BACKEND | ||
559 | sway_input_reset_libinput_device(input_device); | 597 | sway_input_reset_libinput_device(input_device); |
598 | #endif | ||
560 | struct sway_seat *seat = NULL; | 599 | struct sway_seat *seat = NULL; |
561 | wl_list_for_each(seat, &server.input->seats, link) { | 600 | wl_list_for_each(seat, &server.input->seats, link) { |
562 | seat_reset_device(seat, input_device); | 601 | seat_reset_device(seat, input_device); |
@@ -564,6 +603,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) { | |||
564 | } | 603 | } |
565 | 604 | ||
566 | void input_manager_reset_all_inputs(void) { | 605 | void input_manager_reset_all_inputs(void) { |
606 | // Set the active keyboard to NULL to avoid spamming configuration updates | ||
607 | // for all keyboard devices. | ||
608 | struct sway_seat *seat; | ||
609 | wl_list_for_each(seat, &server.input->seats, link) { | ||
610 | wlr_seat_set_keyboard(seat->wlr_seat, NULL); | ||
611 | } | ||
612 | |||
567 | struct sway_input_device *input_device = NULL; | 613 | struct sway_input_device *input_device = NULL; |
568 | wl_list_for_each(input_device, &server.input->devices, link) { | 614 | wl_list_for_each(input_device, &server.input->devices, link) { |
569 | input_manager_reset_input(input_device); | 615 | input_manager_reset_input(input_device); |
@@ -572,7 +618,6 @@ void input_manager_reset_all_inputs(void) { | |||
572 | // If there is at least one keyboard using the default keymap, repeat delay, | 618 | // If there is at least one keyboard using the default keymap, repeat delay, |
573 | // and repeat rate, then it is possible that there is a keyboard group that | 619 | // and repeat rate, then it is possible that there is a keyboard group that |
574 | // need their keyboard disarmed. | 620 | // need their keyboard disarmed. |
575 | struct sway_seat *seat; | ||
576 | wl_list_for_each(seat, &server.input->seats, link) { | 621 | wl_list_for_each(seat, &server.input->seats, link) { |
577 | struct sway_keyboard_group *group; | 622 | struct sway_keyboard_group *group; |
578 | wl_list_for_each(group, &seat->keyboard_groups, link) { | 623 | wl_list_for_each(group, &seat->keyboard_groups, link) { |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index f258ac7d..f74d0658 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -1,10 +1,9 @@ | |||
1 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include <limits.h> | 2 | #include <limits.h> |
3 | #include <strings.h> | 3 | #include <strings.h> |
4 | #include <wlr/config.h> | ||
4 | #include <wlr/backend/multi.h> | 5 | #include <wlr/backend/multi.h> |
5 | #include <wlr/backend/session.h> | ||
6 | #include <wlr/interfaces/wlr_keyboard.h> | 6 | #include <wlr/interfaces/wlr_keyboard.h> |
7 | #include <wlr/types/wlr_idle.h> | ||
8 | #include <wlr/types/wlr_keyboard.h> | 7 | #include <wlr/types/wlr_keyboard.h> |
9 | #include <wlr/types/wlr_keyboard_group.h> | 8 | #include <wlr/types/wlr_keyboard_group.h> |
10 | #include <xkbcommon/xkbcommon-names.h> | 9 | #include <xkbcommon/xkbcommon-names.h> |
@@ -16,6 +15,10 @@ | |||
16 | #include "sway/ipc-server.h" | 15 | #include "sway/ipc-server.h" |
17 | #include "log.h" | 16 | #include "log.h" |
18 | 17 | ||
18 | #if WLR_HAS_SESSION | ||
19 | #include <wlr/backend/session.h> | ||
20 | #endif | ||
21 | |||
19 | static struct modifier_key { | 22 | static struct modifier_key { |
20 | char *name; | 23 | char *name; |
21 | uint32_t mod; | 24 | uint32_t mod; |
@@ -29,6 +32,7 @@ static struct modifier_key { | |||
29 | { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, | 32 | { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, |
30 | { "Mod3", WLR_MODIFIER_MOD3 }, | 33 | { "Mod3", WLR_MODIFIER_MOD3 }, |
31 | { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, | 34 | { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, |
35 | { "Super", WLR_MODIFIER_LOGO }, | ||
32 | { "Mod5", WLR_MODIFIER_MOD5 }, | 36 | { "Mod5", WLR_MODIFIER_MOD5 }, |
33 | }; | 37 | }; |
34 | 38 | ||
@@ -264,14 +268,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | |||
264 | xkb_keysym_t keysym = pressed_keysyms[i]; | 268 | xkb_keysym_t keysym = pressed_keysyms[i]; |
265 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && | 269 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && |
266 | keysym <= XKB_KEY_XF86Switch_VT_12) { | 270 | keysym <= XKB_KEY_XF86Switch_VT_12) { |
267 | if (wlr_backend_is_multi(server.backend)) { | 271 | #if WLR_HAS_SESSION |
268 | struct wlr_session *session = | 272 | if (server.session) { |
269 | wlr_backend_get_session(server.backend); | 273 | unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; |
270 | if (session) { | 274 | wlr_session_change_vt(server.session, vt); |
271 | unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; | ||
272 | wlr_session_change_vt(session, vt); | ||
273 | } | ||
274 | } | 275 | } |
276 | #endif | ||
275 | return true; | 277 | return true; |
276 | } | 278 | } |
277 | } | 279 | } |
@@ -291,14 +293,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | |||
291 | static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, | 293 | static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, |
292 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | 294 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, |
293 | uint32_t *modifiers) { | 295 | uint32_t *modifiers) { |
294 | struct wlr_input_device *device = | 296 | *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
295 | keyboard->seat_device->input_device->wlr_device; | ||
296 | *modifiers = wlr_keyboard_get_modifiers(device->keyboard); | ||
297 | xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( | 297 | xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( |
298 | device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); | 298 | keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); |
299 | *modifiers = *modifiers & ~consumed; | 299 | *modifiers = *modifiers & ~consumed; |
300 | 300 | ||
301 | return xkb_state_key_get_syms(device->keyboard->xkb_state, | 301 | return xkb_state_key_get_syms(keyboard->wlr->xkb_state, |
302 | keycode, keysyms); | 302 | keycode, keysyms); |
303 | } | 303 | } |
304 | 304 | ||
@@ -314,13 +314,11 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, | |||
314 | static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, | 314 | static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, |
315 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | 315 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, |
316 | uint32_t *modifiers) { | 316 | uint32_t *modifiers) { |
317 | struct wlr_input_device *device = | 317 | *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
318 | keyboard->seat_device->input_device->wlr_device; | ||
319 | *modifiers = wlr_keyboard_get_modifiers(device->keyboard); | ||
320 | 318 | ||
321 | xkb_layout_index_t layout_index = xkb_state_key_get_layout( | 319 | xkb_layout_index_t layout_index = xkb_state_key_get_layout( |
322 | device->keyboard->xkb_state, keycode); | 320 | keyboard->wlr->xkb_state, keycode); |
323 | return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, | 321 | return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap, |
324 | keycode, layout_index, 0, keysyms); | 322 | keycode, layout_index, 0, keysyms); |
325 | } | 323 | } |
326 | 324 | ||
@@ -360,8 +358,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard, | |||
360 | keyinfo->keycode, &keyinfo->translated_keysyms, | 358 | keyinfo->keycode, &keyinfo->translated_keysyms, |
361 | &keyinfo->translated_modifiers); | 359 | &keyinfo->translated_modifiers); |
362 | 360 | ||
363 | keyinfo->code_modifiers = wlr_keyboard_get_modifiers( | 361 | keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
364 | keyboard->seat_device->input_device->wlr_device->keyboard); | ||
365 | 362 | ||
366 | // Update shortcut model keyinfo | 363 | // Update shortcut model keyinfo |
367 | update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, | 364 | update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, |
@@ -401,15 +398,15 @@ static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab( | |||
401 | } | 398 | } |
402 | 399 | ||
403 | static void handle_key_event(struct sway_keyboard *keyboard, | 400 | static void handle_key_event(struct sway_keyboard *keyboard, |
404 | struct wlr_event_keyboard_key *event) { | 401 | struct wlr_keyboard_key_event *event) { |
405 | struct sway_seat *seat = keyboard->seat_device->sway_seat; | 402 | struct sway_seat *seat = keyboard->seat_device->sway_seat; |
406 | struct wlr_seat *wlr_seat = seat->wlr_seat; | 403 | struct wlr_seat *wlr_seat = seat->wlr_seat; |
407 | struct wlr_input_device *wlr_device = | 404 | struct wlr_input_device *wlr_device = |
408 | keyboard->seat_device->input_device->wlr_device; | 405 | keyboard->seat_device->input_device->wlr_device; |
409 | char *device_identifier = input_device_get_identifier(wlr_device); | 406 | char *device_identifier = input_device_get_identifier(wlr_device); |
410 | bool exact_identifier = wlr_device->keyboard->group != NULL; | 407 | bool exact_identifier = keyboard->wlr->group != NULL; |
411 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); | 408 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); |
412 | bool input_inhibited = seat->exclusive_client != NULL; | 409 | bool locked = server.session_lock.lock; |
413 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = | 410 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = |
414 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); | 411 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); |
415 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; | 412 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; |
@@ -427,17 +424,17 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
427 | struct sway_binding *binding_released = NULL; | 424 | struct sway_binding *binding_released = NULL; |
428 | get_active_binding(&keyboard->state_keycodes, | 425 | get_active_binding(&keyboard->state_keycodes, |
429 | config->current_mode->keycode_bindings, &binding_released, | 426 | config->current_mode->keycode_bindings, &binding_released, |
430 | keyinfo.code_modifiers, true, input_inhibited, | 427 | keyinfo.code_modifiers, true, locked, |
431 | shortcuts_inhibited, device_identifier, | 428 | shortcuts_inhibited, device_identifier, |
432 | exact_identifier, keyboard->effective_layout); | 429 | exact_identifier, keyboard->effective_layout); |
433 | get_active_binding(&keyboard->state_keysyms_raw, | 430 | get_active_binding(&keyboard->state_keysyms_raw, |
434 | config->current_mode->keysym_bindings, &binding_released, | 431 | config->current_mode->keysym_bindings, &binding_released, |
435 | keyinfo.raw_modifiers, true, input_inhibited, | 432 | keyinfo.raw_modifiers, true, locked, |
436 | shortcuts_inhibited, device_identifier, | 433 | shortcuts_inhibited, device_identifier, |
437 | exact_identifier, keyboard->effective_layout); | 434 | exact_identifier, keyboard->effective_layout); |
438 | get_active_binding(&keyboard->state_keysyms_translated, | 435 | get_active_binding(&keyboard->state_keysyms_translated, |
439 | config->current_mode->keysym_bindings, &binding_released, | 436 | config->current_mode->keysym_bindings, &binding_released, |
440 | keyinfo.translated_modifiers, true, input_inhibited, | 437 | keyinfo.translated_modifiers, true, locked, |
441 | shortcuts_inhibited, device_identifier, | 438 | shortcuts_inhibited, device_identifier, |
442 | exact_identifier, keyboard->effective_layout); | 439 | exact_identifier, keyboard->effective_layout); |
443 | 440 | ||
@@ -459,17 +456,17 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
459 | if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { | 456 | if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { |
460 | get_active_binding(&keyboard->state_keycodes, | 457 | get_active_binding(&keyboard->state_keycodes, |
461 | config->current_mode->keycode_bindings, &binding, | 458 | config->current_mode->keycode_bindings, &binding, |
462 | keyinfo.code_modifiers, false, input_inhibited, | 459 | keyinfo.code_modifiers, false, locked, |
463 | shortcuts_inhibited, device_identifier, | 460 | shortcuts_inhibited, device_identifier, |
464 | exact_identifier, keyboard->effective_layout); | 461 | exact_identifier, keyboard->effective_layout); |
465 | get_active_binding(&keyboard->state_keysyms_raw, | 462 | get_active_binding(&keyboard->state_keysyms_raw, |
466 | config->current_mode->keysym_bindings, &binding, | 463 | config->current_mode->keysym_bindings, &binding, |
467 | keyinfo.raw_modifiers, false, input_inhibited, | 464 | keyinfo.raw_modifiers, false, locked, |
468 | shortcuts_inhibited, device_identifier, | 465 | shortcuts_inhibited, device_identifier, |
469 | exact_identifier, keyboard->effective_layout); | 466 | exact_identifier, keyboard->effective_layout); |
470 | get_active_binding(&keyboard->state_keysyms_translated, | 467 | get_active_binding(&keyboard->state_keysyms_translated, |
471 | config->current_mode->keysym_bindings, &binding, | 468 | config->current_mode->keysym_bindings, &binding, |
472 | keyinfo.translated_modifiers, false, input_inhibited, | 469 | keyinfo.translated_modifiers, false, locked, |
473 | shortcuts_inhibited, device_identifier, | 470 | shortcuts_inhibited, device_identifier, |
474 | exact_identifier, keyboard->effective_layout); | 471 | exact_identifier, keyboard->effective_layout); |
475 | } | 472 | } |
@@ -477,10 +474,10 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
477 | // Set up (or clear) keyboard repeat for a pressed binding. Since the | 474 | // Set up (or clear) keyboard repeat for a pressed binding. Since the |
478 | // binding may remove the keyboard, the timer needs to be updated first | 475 | // binding may remove the keyboard, the timer needs to be updated first |
479 | if (binding && !(binding->flags & BINDING_NOREPEAT) && | 476 | if (binding && !(binding->flags & BINDING_NOREPEAT) && |
480 | wlr_device->keyboard->repeat_info.delay > 0) { | 477 | keyboard->wlr->repeat_info.delay > 0) { |
481 | keyboard->repeat_binding = binding; | 478 | keyboard->repeat_binding = binding; |
482 | if (wl_event_source_timer_update(keyboard->key_repeat_source, | 479 | if (wl_event_source_timer_update(keyboard->key_repeat_source, |
483 | wlr_device->keyboard->repeat_info.delay) < 0) { | 480 | keyboard->wlr->repeat_info.delay) < 0) { |
484 | sway_log(SWAY_DEBUG, "failed to set key repeat timer"); | 481 | sway_log(SWAY_DEBUG, "failed to set key repeat timer"); |
485 | } | 482 | } |
486 | } else if (keyboard->repeat_binding) { | 483 | } else if (keyboard->repeat_binding) { |
@@ -492,7 +489,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
492 | handled = true; | 489 | handled = true; |
493 | } | 490 | } |
494 | 491 | ||
495 | if (!handled && wlr_device->keyboard->group) { | 492 | if (!handled && keyboard->wlr->group) { |
496 | // Only handle device specific bindings for keyboards in a group | 493 | // Only handle device specific bindings for keyboards in a group |
497 | free(device_identifier); | 494 | free(device_identifier); |
498 | return; | 495 | return; |
@@ -517,7 +514,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
517 | &keyboard->state_pressed_sent, event->keycode, | 514 | &keyboard->state_pressed_sent, event->keycode, |
518 | event->state, keyinfo.keycode, 0); | 515 | event->state, keyinfo.keycode, 0); |
519 | if (pressed_sent) { | 516 | if (pressed_sent) { |
520 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | 517 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); |
521 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | 518 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, |
522 | event->keycode, event->state); | 519 | event->keycode, event->state); |
523 | handled = true; | 520 | handled = true; |
@@ -528,8 +525,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
528 | struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); | 525 | struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); |
529 | 526 | ||
530 | if (kb_grab) { | 527 | if (kb_grab) { |
531 | wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, | 528 | wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); |
532 | wlr_device->keyboard); | ||
533 | wlr_input_method_keyboard_grab_v2_send_key(kb_grab, | 529 | wlr_input_method_keyboard_grab_v2_send_key(kb_grab, |
534 | event->time_msec, event->keycode, event->state); | 530 | event->time_msec, event->keycode, event->state); |
535 | handled = true; | 531 | handled = true; |
@@ -542,7 +538,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
542 | update_shortcut_state( | 538 | update_shortcut_state( |
543 | &keyboard->state_pressed_sent, event->keycode, event->state, | 539 | &keyboard->state_pressed_sent, event->keycode, event->state, |
544 | keyinfo.keycode, 0); | 540 | keyinfo.keycode, 0); |
545 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | 541 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); |
546 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | 542 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, |
547 | event->keycode, event->state); | 543 | event->keycode, event->state); |
548 | } | 544 | } |
@@ -618,14 +614,12 @@ static void handle_keyboard_group_leave(struct wl_listener *listener, | |||
618 | } | 614 | } |
619 | 615 | ||
620 | static int handle_keyboard_repeat(void *data) { | 616 | static int handle_keyboard_repeat(void *data) { |
621 | struct sway_keyboard *keyboard = (struct sway_keyboard *)data; | 617 | struct sway_keyboard *keyboard = data; |
622 | struct wlr_keyboard *wlr_device = | ||
623 | keyboard->seat_device->input_device->wlr_device->keyboard; | ||
624 | if (keyboard->repeat_binding) { | 618 | if (keyboard->repeat_binding) { |
625 | if (wlr_device->repeat_info.rate > 0) { | 619 | if (keyboard->wlr->repeat_info.rate > 0) { |
626 | // We queue the next event first, as the command might cancel it | 620 | // We queue the next event first, as the command might cancel it |
627 | if (wl_event_source_timer_update(keyboard->key_repeat_source, | 621 | if (wl_event_source_timer_update(keyboard->key_repeat_source, |
628 | 1000 / wlr_device->repeat_info.rate) < 0) { | 622 | 1000 / keyboard->wlr->repeat_info.rate) < 0) { |
629 | sway_log(SWAY_DEBUG, "failed to update key repeat timer"); | 623 | sway_log(SWAY_DEBUG, "failed to update key repeat timer"); |
630 | } | 624 | } |
631 | } | 625 | } |
@@ -658,31 +652,28 @@ static void determine_bar_visibility(uint32_t modifiers) { | |||
658 | } | 652 | } |
659 | 653 | ||
660 | static void handle_modifier_event(struct sway_keyboard *keyboard) { | 654 | static void handle_modifier_event(struct sway_keyboard *keyboard) { |
661 | struct wlr_input_device *wlr_device = | 655 | if (!keyboard->wlr->group) { |
662 | keyboard->seat_device->input_device->wlr_device; | ||
663 | if (!wlr_device->keyboard->group) { | ||
664 | struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); | 656 | struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); |
665 | 657 | ||
666 | if (kb_grab) { | 658 | if (kb_grab) { |
667 | wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, | 659 | wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); |
668 | wlr_device->keyboard); | ||
669 | wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, | 660 | wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, |
670 | &wlr_device->keyboard->modifiers); | 661 | &keyboard->wlr->modifiers); |
671 | } else { | 662 | } else { |
672 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | 663 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; |
673 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | 664 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); |
674 | wlr_seat_keyboard_notify_modifiers(wlr_seat, | 665 | wlr_seat_keyboard_notify_modifiers(wlr_seat, |
675 | &wlr_device->keyboard->modifiers); | 666 | &keyboard->wlr->modifiers); |
676 | } | 667 | } |
677 | 668 | ||
678 | uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); | 669 | uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
679 | determine_bar_visibility(modifiers); | 670 | determine_bar_visibility(modifiers); |
680 | } | 671 | } |
681 | 672 | ||
682 | if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { | 673 | if (keyboard->wlr->modifiers.group != keyboard->effective_layout) { |
683 | keyboard->effective_layout = wlr_device->keyboard->modifiers.group; | 674 | keyboard->effective_layout = keyboard->wlr->modifiers.group; |
684 | 675 | ||
685 | if (!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { | 676 | if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) { |
686 | ipc_event_input("xkb_layout", keyboard->seat_device->input_device); | 677 | ipc_event_input("xkb_layout", keyboard->seat_device->input_device); |
687 | } | 678 | } |
688 | } | 679 | } |
@@ -711,6 +702,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | |||
711 | } | 702 | } |
712 | 703 | ||
713 | keyboard->seat_device = device; | 704 | keyboard->seat_device = device; |
705 | keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device); | ||
714 | device->keyboard = keyboard; | 706 | device->keyboard = keyboard; |
715 | 707 | ||
716 | wl_list_init(&keyboard->keyboard_key.link); | 708 | wl_list_init(&keyboard->keyboard_key.link); |
@@ -724,23 +716,11 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | |||
724 | 716 | ||
725 | static void handle_xkb_context_log(struct xkb_context *context, | 717 | static void handle_xkb_context_log(struct xkb_context *context, |
726 | enum xkb_log_level level, const char *format, va_list args) { | 718 | enum xkb_log_level level, const char *format, va_list args) { |
727 | va_list args_copy; | 719 | char *error = vformat_str(format, args); |
728 | va_copy(args_copy, args); | ||
729 | size_t length = vsnprintf(NULL, 0, format, args_copy) + 1; | ||
730 | va_end(args_copy); | ||
731 | |||
732 | char *error = malloc(length); | ||
733 | if (!error) { | ||
734 | sway_log(SWAY_ERROR, "Failed to allocate libxkbcommon log message"); | ||
735 | return; | ||
736 | } | ||
737 | |||
738 | va_copy(args_copy, args); | ||
739 | vsnprintf(error, length, format, args_copy); | ||
740 | va_end(args_copy); | ||
741 | 720 | ||
742 | if (error[length - 2] == '\n') { | 721 | size_t len = strlen(error); |
743 | error[length - 2] = '\0'; | 722 | if (error[len - 1] == '\n') { |
723 | error[len - 1] = '\0'; | ||
744 | } | 724 | } |
745 | 725 | ||
746 | sway_log_importance_t importance = SWAY_DEBUG; | 726 | sway_log_importance_t importance = SWAY_DEBUG; |
@@ -761,7 +741,7 @@ static void handle_xkb_context_log(struct xkb_context *context, | |||
761 | 741 | ||
762 | struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, | 742 | struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, |
763 | char **error) { | 743 | char **error) { |
764 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | 744 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); |
765 | if (!sway_assert(context, "cannot create XKB context")) { | 745 | if (!sway_assert(context, "cannot create XKB context")) { |
766 | return NULL; | 746 | return NULL; |
767 | } | 747 | } |
@@ -775,13 +755,8 @@ struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, | |||
775 | if (!keymap_file) { | 755 | if (!keymap_file) { |
776 | sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file); | 756 | sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file); |
777 | if (error) { | 757 | if (error) { |
778 | size_t len = snprintf(NULL, 0, "cannot read xkb file %s: %s", | 758 | *error = format_str("cannot read xkb file %s: %s", |
779 | ic->xkb_file, strerror(errno)) + 1; | 759 | ic->xkb_file, strerror(errno)); |
780 | *error = malloc(len); | ||
781 | if (*error) { | ||
782 | snprintf(*error, len, "cannot read xkb_file %s: %s", | ||
783 | ic->xkb_file, strerror(errno)); | ||
784 | } | ||
785 | } | 760 | } |
786 | goto cleanup; | 761 | goto cleanup; |
787 | } | 762 | } |
@@ -819,13 +794,12 @@ static void destroy_empty_wlr_keyboard_group(void *data) { | |||
819 | 794 | ||
820 | static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { | 795 | static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { |
821 | struct sway_input_device *device = keyboard->seat_device->input_device; | 796 | struct sway_input_device *device = keyboard->seat_device->input_device; |
822 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | 797 | struct wlr_keyboard_group *wlr_group = keyboard->wlr->group; |
823 | struct wlr_keyboard_group *wlr_group = wlr_keyboard->group; | ||
824 | 798 | ||
825 | sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", | 799 | sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", |
826 | device->identifier, wlr_group); | 800 | device->identifier, wlr_group); |
827 | 801 | ||
828 | wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); | 802 | wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr); |
829 | 803 | ||
830 | if (wl_list_empty(&wlr_group->devices)) { | 804 | if (wl_list_empty(&wlr_group->devices)) { |
831 | sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", | 805 | sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", |
@@ -850,9 +824,7 @@ static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { | |||
850 | } | 824 | } |
851 | 825 | ||
852 | static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | 826 | static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { |
853 | struct sway_input_device *device = keyboard->seat_device->input_device; | 827 | if (!keyboard->wlr->group) { |
854 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | ||
855 | if (!wlr_keyboard->group) { | ||
856 | return; | 828 | return; |
857 | } | 829 | } |
858 | 830 | ||
@@ -868,7 +840,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | |||
868 | break; | 840 | break; |
869 | case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ | 841 | case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ |
870 | case KEYBOARD_GROUP_SMART:; | 842 | case KEYBOARD_GROUP_SMART:; |
871 | struct wlr_keyboard_group *group = wlr_keyboard->group; | 843 | struct wlr_keyboard_group *group = keyboard->wlr->group; |
872 | if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || | 844 | if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || |
873 | !repeat_info_match(keyboard, &group->keyboard)) { | 845 | !repeat_info_match(keyboard, &group->keyboard)) { |
874 | sway_keyboard_group_remove(keyboard); | 846 | sway_keyboard_group_remove(keyboard); |
@@ -879,7 +851,6 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | |||
879 | 851 | ||
880 | static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | 852 | static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { |
881 | struct sway_input_device *device = keyboard->seat_device->input_device; | 853 | struct sway_input_device *device = keyboard->seat_device->input_device; |
882 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | ||
883 | struct sway_seat *seat = keyboard->seat_device->sway_seat; | 854 | struct sway_seat *seat = keyboard->seat_device->sway_seat; |
884 | struct seat_config *sc = seat_get_config(seat); | 855 | struct seat_config *sc = seat_get_config(seat); |
885 | 856 | ||
@@ -911,7 +882,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
911 | repeat_info_match(keyboard, &wlr_group->keyboard)) { | 882 | repeat_info_match(keyboard, &wlr_group->keyboard)) { |
912 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", | 883 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", |
913 | device->identifier, wlr_group); | 884 | device->identifier, wlr_group); |
914 | wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); | 885 | wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr); |
915 | return; | 886 | return; |
916 | } | 887 | } |
917 | break; | 888 | break; |
@@ -950,7 +921,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
950 | goto cleanup; | 921 | goto cleanup; |
951 | } | 922 | } |
952 | sway_group->seat_device->input_device->wlr_device = | 923 | sway_group->seat_device->input_device->wlr_device = |
953 | sway_group->wlr_group->input_device; | 924 | &sway_group->wlr_group->keyboard.base; |
954 | 925 | ||
955 | if (!sway_keyboard_create(seat, sway_group->seat_device)) { | 926 | if (!sway_keyboard_create(seat, sway_group->seat_device)) { |
956 | sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); | 927 | sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); |
@@ -959,7 +930,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
959 | 930 | ||
960 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", | 931 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", |
961 | device->identifier, sway_group->wlr_group); | 932 | device->identifier, sway_group->wlr_group); |
962 | wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); | 933 | wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr); |
963 | 934 | ||
964 | wl_list_insert(&seat->keyboard_groups, &sway_group->link); | 935 | wl_list_insert(&seat->keyboard_groups, &sway_group->link); |
965 | 936 | ||
@@ -991,10 +962,8 @@ cleanup: | |||
991 | void sway_keyboard_configure(struct sway_keyboard *keyboard) { | 962 | void sway_keyboard_configure(struct sway_keyboard *keyboard) { |
992 | struct input_config *input_config = | 963 | struct input_config *input_config = |
993 | input_device_get_config(keyboard->seat_device->input_device); | 964 | input_device_get_config(keyboard->seat_device->input_device); |
994 | struct wlr_input_device *wlr_device = | ||
995 | keyboard->seat_device->input_device->wlr_device; | ||
996 | 965 | ||
997 | if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard), | 966 | if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr), |
998 | "sway_keyboard_configure should not be called with a " | 967 | "sway_keyboard_configure should not be called with a " |
999 | "keyboard group's keyboard")) { | 968 | "keyboard group's keyboard")) { |
1000 | return; | 969 | return; |
@@ -1036,11 +1005,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
1036 | 1005 | ||
1037 | sway_keyboard_group_remove_invalid(keyboard); | 1006 | sway_keyboard_group_remove_invalid(keyboard); |
1038 | 1007 | ||
1039 | wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); | 1008 | wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap); |
1040 | wlr_keyboard_set_repeat_info(wlr_device->keyboard, | 1009 | wlr_keyboard_set_repeat_info(keyboard->wlr, |
1041 | keyboard->repeat_rate, keyboard->repeat_delay); | 1010 | keyboard->repeat_rate, keyboard->repeat_delay); |
1042 | 1011 | ||
1043 | if (!wlr_device->keyboard->group) { | 1012 | if (!keyboard->wlr->group) { |
1044 | sway_keyboard_group_add(keyboard); | 1013 | sway_keyboard_group_add(keyboard); |
1045 | } | 1014 | } |
1046 | 1015 | ||
@@ -1060,40 +1029,42 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
1060 | } | 1029 | } |
1061 | } | 1030 | } |
1062 | if (locked_mods) { | 1031 | if (locked_mods) { |
1063 | wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, | 1032 | wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0, |
1064 | locked_mods, 0); | 1033 | locked_mods, 0); |
1065 | uint32_t leds = 0; | 1034 | uint32_t leds = 0; |
1066 | for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { | 1035 | for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { |
1067 | if (xkb_state_led_index_is_active( | 1036 | if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state, |
1068 | wlr_device->keyboard->xkb_state, | 1037 | keyboard->wlr->led_indexes[i])) { |
1069 | wlr_device->keyboard->led_indexes[i])) { | ||
1070 | leds |= (1 << i); | 1038 | leds |= (1 << i); |
1071 | } | 1039 | } |
1072 | } | 1040 | } |
1073 | if (wlr_device->keyboard->group) { | 1041 | if (keyboard->wlr->group) { |
1074 | wlr_keyboard_led_update( | 1042 | wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds); |
1075 | &wlr_device->keyboard->group->keyboard, leds); | ||
1076 | } else { | 1043 | } else { |
1077 | wlr_keyboard_led_update(wlr_device->keyboard, leds); | 1044 | wlr_keyboard_led_update(keyboard->wlr, leds); |
1078 | } | 1045 | } |
1079 | } | 1046 | } |
1080 | } else { | 1047 | } else { |
1081 | xkb_keymap_unref(keymap); | 1048 | xkb_keymap_unref(keymap); |
1082 | sway_keyboard_group_remove_invalid(keyboard); | 1049 | sway_keyboard_group_remove_invalid(keyboard); |
1083 | if (!wlr_device->keyboard->group) { | 1050 | if (!keyboard->wlr->group) { |
1084 | sway_keyboard_group_add(keyboard); | 1051 | sway_keyboard_group_add(keyboard); |
1085 | } | 1052 | } |
1086 | } | 1053 | } |
1087 | 1054 | ||
1055 | // If the seat has no active keyboard, set this one | ||
1088 | struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; | 1056 | struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; |
1089 | wlr_seat_set_keyboard(seat, wlr_device); | 1057 | struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard; |
1058 | if (current_keyboard == NULL) { | ||
1059 | wlr_seat_set_keyboard(seat, keyboard->wlr); | ||
1060 | } | ||
1090 | 1061 | ||
1091 | wl_list_remove(&keyboard->keyboard_key.link); | 1062 | wl_list_remove(&keyboard->keyboard_key.link); |
1092 | wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); | 1063 | wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); |
1093 | keyboard->keyboard_key.notify = handle_keyboard_key; | 1064 | keyboard->keyboard_key.notify = handle_keyboard_key; |
1094 | 1065 | ||
1095 | wl_list_remove(&keyboard->keyboard_modifiers.link); | 1066 | wl_list_remove(&keyboard->keyboard_modifiers.link); |
1096 | wl_signal_add(&wlr_device->keyboard->events.modifiers, | 1067 | wl_signal_add(&keyboard->wlr->events.modifiers, |
1097 | &keyboard->keyboard_modifiers); | 1068 | &keyboard->keyboard_modifiers); |
1098 | keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; | 1069 | keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; |
1099 | 1070 | ||
@@ -1110,12 +1081,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { | |||
1110 | if (!keyboard) { | 1081 | if (!keyboard) { |
1111 | return; | 1082 | return; |
1112 | } | 1083 | } |
1113 | if (keyboard->seat_device->input_device->wlr_device->keyboard->group) { | 1084 | if (keyboard->wlr->group) { |
1114 | sway_keyboard_group_remove(keyboard); | 1085 | sway_keyboard_group_remove(keyboard); |
1115 | } | 1086 | } |
1116 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | 1087 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; |
1117 | struct sway_input_device *device = keyboard->seat_device->input_device; | 1088 | if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) { |
1118 | if (wlr_seat_get_keyboard(wlr_seat) == device->wlr_device->keyboard) { | ||
1119 | wlr_seat_set_keyboard(wlr_seat, NULL); | 1089 | wlr_seat_set_keyboard(wlr_seat, NULL); |
1120 | } | 1090 | } |
1121 | if (keyboard->keymap) { | 1091 | if (keyboard->keymap) { |
diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 060a584a..0266c7a9 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c | |||
@@ -79,6 +79,16 @@ static bool set_accel_speed(struct libinput_device *device, double speed) { | |||
79 | return true; | 79 | return true; |
80 | } | 80 | } |
81 | 81 | ||
82 | static bool set_rotation_angle(struct libinput_device *device, double angle) { | ||
83 | if (!libinput_device_config_rotation_is_available(device) || | ||
84 | libinput_device_config_rotation_get_angle(device) == angle) { | ||
85 | return false; | ||
86 | } | ||
87 | sway_log(SWAY_DEBUG, "rotation_set_angle(%f)", angle); | ||
88 | log_status(libinput_device_config_rotation_set_angle(device, angle)); | ||
89 | return true; | ||
90 | } | ||
91 | |||
82 | static bool set_accel_profile(struct libinput_device *device, | 92 | static bool set_accel_profile(struct libinput_device *device, |
83 | enum libinput_config_accel_profile profile) { | 93 | enum libinput_config_accel_profile profile) { |
84 | if (!libinput_device_config_accel_is_available(device) || | 94 | if (!libinput_device_config_accel_is_available(device) || |
@@ -156,6 +166,18 @@ static bool set_scroll_button(struct libinput_device *dev, uint32_t button) { | |||
156 | return true; | 166 | return true; |
157 | } | 167 | } |
158 | 168 | ||
169 | static bool set_scroll_button_lock(struct libinput_device *dev, | ||
170 | enum libinput_config_scroll_button_lock_state lock) { | ||
171 | uint32_t scroll = libinput_device_config_scroll_get_methods(dev); | ||
172 | if ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 || | ||
173 | libinput_device_config_scroll_get_button_lock(dev) == lock) { | ||
174 | return false; | ||
175 | } | ||
176 | sway_log(SWAY_DEBUG, "scroll_set_button_lock(%" PRIu32 ")", lock); | ||
177 | log_status(libinput_device_config_scroll_set_button_lock(dev, lock)); | ||
178 | return true; | ||
179 | } | ||
180 | |||
159 | static bool set_dwt(struct libinput_device *device, bool dwt) { | 181 | static bool set_dwt(struct libinput_device *device, bool dwt) { |
160 | if (!libinput_device_config_dwt_is_available(device) || | 182 | if (!libinput_device_config_dwt_is_available(device) || |
161 | libinput_device_config_dwt_get_enabled(device) == dwt) { | 183 | libinput_device_config_dwt_get_enabled(device) == dwt) { |
@@ -166,6 +188,16 @@ static bool set_dwt(struct libinput_device *device, bool dwt) { | |||
166 | return true; | 188 | return true; |
167 | } | 189 | } |
168 | 190 | ||
191 | static bool set_dwtp(struct libinput_device *device, bool dwtp) { | ||
192 | if (!libinput_device_config_dwtp_is_available(device) || | ||
193 | libinput_device_config_dwtp_get_enabled(device) == dwtp) { | ||
194 | return false; | ||
195 | } | ||
196 | sway_log(SWAY_DEBUG, "dwtp_set_enabled(%d)", dwtp); | ||
197 | log_status(libinput_device_config_dwtp_set_enabled(device, dwtp)); | ||
198 | return true; | ||
199 | } | ||
200 | |||
169 | static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { | 201 | static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { |
170 | if (!libinput_device_config_calibration_has_matrix(dev)) { | 202 | if (!libinput_device_config_calibration_has_matrix(dev)) { |
171 | return false; | 203 | return false; |
@@ -187,35 +219,38 @@ static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { | |||
187 | return changed; | 219 | return changed; |
188 | } | 220 | } |
189 | 221 | ||
190 | void sway_input_configure_libinput_device(struct sway_input_device *input_device) { | 222 | static bool configure_send_events(struct libinput_device *device, |
191 | struct input_config *ic = input_device_get_config(input_device); | 223 | struct input_config *ic) { |
192 | if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { | ||
193 | return; | ||
194 | } | ||
195 | |||
196 | struct libinput_device *device = | ||
197 | wlr_libinput_get_device_handle(input_device->wlr_device); | ||
198 | sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')", | ||
199 | ic->identifier, input_device->identifier); | ||
200 | |||
201 | bool changed = false; | ||
202 | if (ic->mapped_to_output && | 224 | if (ic->mapped_to_output && |
225 | strcmp("*", ic->mapped_to_output) != 0 && | ||
203 | !output_by_name_or_id(ic->mapped_to_output)) { | 226 | !output_by_name_or_id(ic->mapped_to_output)) { |
204 | sway_log(SWAY_DEBUG, | 227 | sway_log(SWAY_DEBUG, |
205 | "%s '%s' is mapped to offline output '%s'; disabling input", | 228 | "%s '%s' is mapped to offline output '%s'; disabling input", |
206 | ic->input_type, ic->identifier, ic->mapped_to_output); | 229 | ic->input_type, ic->identifier, ic->mapped_to_output); |
207 | changed |= set_send_events(device, | 230 | return set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); |
208 | LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); | ||
209 | } else if (ic->send_events != INT_MIN) { | 231 | } else if (ic->send_events != INT_MIN) { |
210 | changed |= set_send_events(device, ic->send_events); | 232 | return set_send_events(device, ic->send_events); |
211 | } else { | 233 | } else { |
212 | // Have to reset to the default mode here, otherwise if ic->send_events | 234 | // Have to reset to the default mode here, otherwise if ic->send_events |
213 | // is unset and a mapped output just came online after being disabled, | 235 | // is unset and a mapped output just came online after being disabled, |
214 | // we'd remain stuck sending no events. | 236 | // we'd remain stuck sending no events. |
215 | changed |= set_send_events(device, | 237 | return set_send_events(device, |
216 | libinput_device_config_send_events_get_default_mode(device)); | 238 | libinput_device_config_send_events_get_default_mode(device)); |
217 | } | 239 | } |
240 | } | ||
241 | |||
242 | bool sway_input_configure_libinput_device(struct sway_input_device *input_device) { | ||
243 | struct input_config *ic = input_device_get_config(input_device); | ||
244 | if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { | ||
245 | return false; | ||
246 | } | ||
247 | |||
248 | struct libinput_device *device = | ||
249 | wlr_libinput_get_device_handle(input_device->wlr_device); | ||
250 | sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')", | ||
251 | ic->identifier, input_device->identifier); | ||
218 | 252 | ||
253 | bool changed = configure_send_events(device, ic); | ||
219 | if (ic->tap != INT_MIN) { | 254 | if (ic->tap != INT_MIN) { |
220 | changed |= set_tap(device, ic->tap); | 255 | changed |= set_tap(device, ic->tap); |
221 | } | 256 | } |
@@ -231,6 +266,9 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device | |||
231 | if (ic->pointer_accel != FLT_MIN) { | 266 | if (ic->pointer_accel != FLT_MIN) { |
232 | changed |= set_accel_speed(device, ic->pointer_accel); | 267 | changed |= set_accel_speed(device, ic->pointer_accel); |
233 | } | 268 | } |
269 | if (ic->rotation_angle != FLT_MIN) { | ||
270 | changed |= set_rotation_angle(device, ic->rotation_angle); | ||
271 | } | ||
234 | if (ic->accel_profile != INT_MIN) { | 272 | if (ic->accel_profile != INT_MIN) { |
235 | changed |= set_accel_profile(device, ic->accel_profile); | 273 | changed |= set_accel_profile(device, ic->accel_profile); |
236 | } | 274 | } |
@@ -252,13 +290,33 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device | |||
252 | if (ic->scroll_button != INT_MIN) { | 290 | if (ic->scroll_button != INT_MIN) { |
253 | changed |= set_scroll_button(device, ic->scroll_button); | 291 | changed |= set_scroll_button(device, ic->scroll_button); |
254 | } | 292 | } |
293 | if (ic->scroll_button_lock != INT_MIN) { | ||
294 | changed |= set_scroll_button_lock(device, ic->scroll_button_lock); | ||
295 | } | ||
255 | if (ic->dwt != INT_MIN) { | 296 | if (ic->dwt != INT_MIN) { |
256 | changed |= set_dwt(device, ic->dwt); | 297 | changed |= set_dwt(device, ic->dwt); |
257 | } | 298 | } |
299 | if (ic->dwtp != INT_MIN) { | ||
300 | changed |= set_dwtp(device, ic->dwtp); | ||
301 | } | ||
258 | if (ic->calibration_matrix.configured) { | 302 | if (ic->calibration_matrix.configured) { |
259 | changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); | 303 | changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); |
260 | } | 304 | } |
261 | 305 | ||
306 | return changed; | ||
307 | } | ||
308 | |||
309 | void sway_input_configure_libinput_device_send_events( | ||
310 | struct sway_input_device *input_device) { | ||
311 | struct input_config *ic = input_device_get_config(input_device); | ||
312 | if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { | ||
313 | return; | ||
314 | } | ||
315 | |||
316 | struct libinput_device *device = | ||
317 | wlr_libinput_get_device_handle(input_device->wlr_device); | ||
318 | bool changed = configure_send_events(device, ic); | ||
319 | |||
262 | if (changed) { | 320 | if (changed) { |
263 | ipc_event_input("libinput_config", input_device); | 321 | ipc_event_input("libinput_config", input_device); |
264 | } | 322 | } |
@@ -287,6 +345,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { | |||
287 | libinput_device_config_tap_get_default_drag_lock_enabled(device)); | 345 | libinput_device_config_tap_get_default_drag_lock_enabled(device)); |
288 | changed |= set_accel_speed(device, | 346 | changed |= set_accel_speed(device, |
289 | libinput_device_config_accel_get_default_speed(device)); | 347 | libinput_device_config_accel_get_default_speed(device)); |
348 | changed |= set_rotation_angle(device, | ||
349 | libinput_device_config_rotation_get_default_angle(device)); | ||
290 | changed |= set_accel_profile(device, | 350 | changed |= set_accel_profile(device, |
291 | libinput_device_config_accel_get_default_profile(device)); | 351 | libinput_device_config_accel_get_default_profile(device)); |
292 | changed |= set_natural_scroll(device, | 352 | changed |= set_natural_scroll(device, |
@@ -304,6 +364,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { | |||
304 | libinput_device_config_scroll_get_default_button(device)); | 364 | libinput_device_config_scroll_get_default_button(device)); |
305 | changed |= set_dwt(device, | 365 | changed |= set_dwt(device, |
306 | libinput_device_config_dwt_get_default_enabled(device)); | 366 | libinput_device_config_dwt_get_default_enabled(device)); |
367 | changed |= set_dwtp(device, | ||
368 | libinput_device_config_dwtp_get_default_enabled(device)); | ||
307 | 369 | ||
308 | float matrix[6]; | 370 | float matrix[6]; |
309 | libinput_device_config_calibration_get_default_matrix(device, matrix); | 371 | libinput_device_config_calibration_get_default_matrix(device, matrix); |
@@ -332,6 +394,13 @@ bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { | |||
332 | return false; | 394 | return false; |
333 | } | 395 | } |
334 | 396 | ||
335 | const char prefix[] = "platform-"; | 397 | const char prefix_platform[] = "platform-"; |
336 | return strncmp(id_path, prefix, strlen(prefix)) == 0; | 398 | if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) { |
399 | return false; | ||
400 | } | ||
401 | |||
402 | const char prefix_pci[] = "pci-"; | ||
403 | const char infix_platform[] = "-platform-"; | ||
404 | return (strncmp(id_path, prefix_pci, strlen(prefix_pci)) == 0) && | ||
405 | strstr(id_path, infix_platform); | ||
337 | } | 406 | } |
diff --git a/sway/input/seat.c b/sway/input/seat.c index 2d714acd..0c5672bc 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -1,22 +1,23 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <strings.h> | 4 | #include <strings.h> |
6 | #include <time.h> | 5 | #include <time.h> |
6 | #include <wlr/config.h> | ||
7 | #include <wlr/types/wlr_cursor.h> | 7 | #include <wlr/types/wlr_cursor.h> |
8 | #include <wlr/types/wlr_data_device.h> | 8 | #include <wlr/types/wlr_data_device.h> |
9 | #include <wlr/types/wlr_idle.h> | 9 | #include <wlr/types/wlr_idle_notify_v1.h> |
10 | #include <wlr/types/wlr_keyboard_group.h> | 10 | #include <wlr/types/wlr_keyboard_group.h> |
11 | #include <wlr/types/wlr_output_layout.h> | 11 | #include <wlr/types/wlr_output_layout.h> |
12 | #include <wlr/types/wlr_primary_selection.h> | 12 | #include <wlr/types/wlr_primary_selection.h> |
13 | #include <wlr/types/wlr_tablet_v2.h> | 13 | #include <wlr/types/wlr_tablet_v2.h> |
14 | #include <wlr/types/wlr_touch.h> | ||
14 | #include <wlr/types/wlr_xcursor_manager.h> | 15 | #include <wlr/types/wlr_xcursor_manager.h> |
15 | #include "config.h" | 16 | #include "config.h" |
16 | #include "list.h" | 17 | #include "list.h" |
17 | #include "log.h" | 18 | #include "log.h" |
18 | #include "sway/config.h" | 19 | #include "sway/config.h" |
19 | #include "sway/desktop.h" | 20 | #include "sway/scene_descriptor.h" |
20 | #include "sway/input/cursor.h" | 21 | #include "sway/input/cursor.h" |
21 | #include "sway/input/input-manager.h" | 22 | #include "sway/input/input-manager.h" |
22 | #include "sway/input/keyboard.h" | 23 | #include "sway/input/keyboard.h" |
@@ -42,6 +43,7 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) { | |||
42 | sway_keyboard_destroy(seat_device->keyboard); | 43 | sway_keyboard_destroy(seat_device->keyboard); |
43 | sway_tablet_destroy(seat_device->tablet); | 44 | sway_tablet_destroy(seat_device->tablet); |
44 | sway_tablet_pad_destroy(seat_device->tablet_pad); | 45 | sway_tablet_pad_destroy(seat_device->tablet_pad); |
46 | sway_switch_destroy(seat_device->switch_device); | ||
45 | wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, | 47 | wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, |
46 | seat_device->input_device->wlr_device); | 48 | seat_device->input_device->wlr_device); |
47 | wl_list_remove(&seat_device->link); | 49 | wl_list_remove(&seat_device->link); |
@@ -51,10 +53,26 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) { | |||
51 | static void seat_node_destroy(struct sway_seat_node *seat_node) { | 53 | static void seat_node_destroy(struct sway_seat_node *seat_node) { |
52 | wl_list_remove(&seat_node->destroy.link); | 54 | wl_list_remove(&seat_node->destroy.link); |
53 | wl_list_remove(&seat_node->link); | 55 | wl_list_remove(&seat_node->link); |
56 | |||
57 | /* | ||
58 | * This is the only time we remove items from the focus stack without | ||
59 | * immediately re-adding them. If we just removed the last thing, | ||
60 | * mark that nothing has focus anymore. | ||
61 | */ | ||
62 | if (wl_list_empty(&seat_node->seat->focus_stack)) { | ||
63 | seat_node->seat->has_focus = false; | ||
64 | } | ||
65 | |||
54 | free(seat_node); | 66 | free(seat_node); |
55 | } | 67 | } |
56 | 68 | ||
57 | void seat_destroy(struct sway_seat *seat) { | 69 | void seat_destroy(struct sway_seat *seat) { |
70 | wlr_seat_destroy(seat->wlr_seat); | ||
71 | } | ||
72 | |||
73 | static void handle_seat_destroy(struct wl_listener *listener, void *data) { | ||
74 | struct sway_seat *seat = wl_container_of(listener, seat, destroy); | ||
75 | |||
58 | if (seat == config->handler_context.seat) { | 76 | if (seat == config->handler_context.seat) { |
59 | config->handler_context.seat = input_manager_get_default_seat(); | 77 | config->handler_context.seat = input_manager_get_default_seat(); |
60 | } | 78 | } |
@@ -75,10 +93,11 @@ void seat_destroy(struct sway_seat *seat) { | |||
75 | wl_list_remove(&seat->request_set_selection.link); | 93 | wl_list_remove(&seat->request_set_selection.link); |
76 | wl_list_remove(&seat->request_set_primary_selection.link); | 94 | wl_list_remove(&seat->request_set_primary_selection.link); |
77 | wl_list_remove(&seat->link); | 95 | wl_list_remove(&seat->link); |
78 | wlr_seat_destroy(seat->wlr_seat); | 96 | wl_list_remove(&seat->destroy.link); |
79 | for (int i = 0; i < seat->deferred_bindings->length; i++) { | 97 | for (int i = 0; i < seat->deferred_bindings->length; i++) { |
80 | free_sway_binding(seat->deferred_bindings->items[i]); | 98 | free_sway_binding(seat->deferred_bindings->items[i]); |
81 | } | 99 | } |
100 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
82 | list_free(seat->deferred_bindings); | 101 | list_free(seat->deferred_bindings); |
83 | free(seat->prev_workspace_name); | 102 | free(seat->prev_workspace_name); |
84 | free(seat); | 103 | free(seat); |
@@ -86,21 +105,10 @@ void seat_destroy(struct sway_seat *seat) { | |||
86 | 105 | ||
87 | void seat_idle_notify_activity(struct sway_seat *seat, | 106 | void seat_idle_notify_activity(struct sway_seat *seat, |
88 | enum sway_input_idle_source source) { | 107 | enum sway_input_idle_source source) { |
89 | uint32_t mask = seat->idle_inhibit_sources; | 108 | if ((source & seat->idle_inhibit_sources) == 0) { |
90 | struct wlr_idle_timeout *timeout; | 109 | return; |
91 | int ntimers = 0, nidle = 0; | ||
92 | wl_list_for_each(timeout, &server.idle->idle_timers, link) { | ||
93 | ++ntimers; | ||
94 | if (timeout->idle_state) { | ||
95 | ++nidle; | ||
96 | } | ||
97 | } | ||
98 | if (nidle == ntimers) { | ||
99 | mask = seat->idle_wake_sources; | ||
100 | } | ||
101 | if ((source & mask) > 0) { | ||
102 | wlr_idle_notify_activity(server.idle, seat->wlr_seat); | ||
103 | } | 110 | } |
111 | wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat); | ||
104 | } | 112 | } |
105 | 113 | ||
106 | /** | 114 | /** |
@@ -130,7 +138,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( | |||
130 | if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { | 138 | if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { |
131 | continue; | 139 | continue; |
132 | } | 140 | } |
133 | if (input_device->wlr_device->keyboard == wlr_keyboard) { | 141 | if (input_device->wlr_device == &wlr_keyboard->base) { |
134 | return seat_device->keyboard; | 142 | return seat_device->keyboard; |
135 | } | 143 | } |
136 | } | 144 | } |
@@ -138,7 +146,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( | |||
138 | wl_list_for_each(group, &seat->keyboard_groups, link) { | 146 | wl_list_for_each(group, &seat->keyboard_groups, link) { |
139 | struct sway_input_device *input_device = | 147 | struct sway_input_device *input_device = |
140 | group->seat_device->input_device; | 148 | group->seat_device->input_device; |
141 | if (input_device->wlr_device->keyboard == wlr_keyboard) { | 149 | if (input_device->wlr_device == &wlr_keyboard->base) { |
142 | return group->seat_device->keyboard; | 150 | return group->seat_device->keyboard; |
143 | } | 151 | } |
144 | } | 152 | } |
@@ -162,11 +170,11 @@ static void seat_keyboard_notify_enter(struct sway_seat *seat, | |||
162 | state->pressed_keycodes, state->npressed, &keyboard->modifiers); | 170 | state->pressed_keycodes, state->npressed, &keyboard->modifiers); |
163 | } | 171 | } |
164 | 172 | ||
165 | static void seat_tablet_pads_notify_enter(struct sway_seat *seat, | 173 | static void seat_tablet_pads_set_focus(struct sway_seat *seat, |
166 | struct wlr_surface *surface) { | 174 | struct wlr_surface *surface) { |
167 | struct sway_seat_device *seat_device; | 175 | struct sway_seat_device *seat_device; |
168 | wl_list_for_each(seat_device, &seat->devices, link) { | 176 | wl_list_for_each(seat_device, &seat->devices, link) { |
169 | sway_tablet_pad_notify_enter(seat_device->tablet_pad, surface); | 177 | sway_tablet_pad_set_focus(seat_device->tablet_pad, surface); |
170 | } | 178 | } |
171 | } | 179 | } |
172 | 180 | ||
@@ -190,7 +198,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { | |||
190 | #endif | 198 | #endif |
191 | 199 | ||
192 | seat_keyboard_notify_enter(seat, view->surface); | 200 | seat_keyboard_notify_enter(seat, view->surface); |
193 | seat_tablet_pads_notify_enter(seat, view->surface); | 201 | seat_tablet_pads_set_focus(seat, view->surface); |
194 | sway_input_method_relay_set_focus(&seat->im_relay, view->surface); | 202 | sway_input_method_relay_set_focus(&seat->im_relay, view->surface); |
195 | 203 | ||
196 | struct wlr_pointer_constraint_v1 *constraint = | 204 | struct wlr_pointer_constraint_v1 *constraint = |
@@ -210,14 +218,13 @@ void seat_for_each_node(struct sway_seat *seat, | |||
210 | 218 | ||
211 | struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, | 219 | struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, |
212 | struct sway_node *ancestor) { | 220 | struct sway_node *ancestor) { |
213 | if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) { | 221 | if (node_is_view(ancestor)) { |
214 | return ancestor->sway_container; | 222 | return ancestor->sway_container; |
215 | } | 223 | } |
216 | struct sway_seat_node *current; | 224 | struct sway_seat_node *current; |
217 | wl_list_for_each(current, &seat->focus_stack, link) { | 225 | wl_list_for_each(current, &seat->focus_stack, link) { |
218 | struct sway_node *node = current->node; | 226 | struct sway_node *node = current->node; |
219 | if (node->type == N_CONTAINER && node->sway_container->view && | 227 | if (node_is_view(node) && node_has_ancestor(node, ancestor)) { |
220 | node_has_ancestor(node, ancestor)) { | ||
221 | return node->sway_container; | 228 | return node->sway_container; |
222 | } | 229 | } |
223 | } | 230 | } |
@@ -236,7 +243,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { | |||
236 | seat_node_destroy(seat_node); | 243 | seat_node_destroy(seat_node); |
237 | // If an unmanaged or layer surface is focused when an output gets | 244 | // If an unmanaged or layer surface is focused when an output gets |
238 | // disabled and an empty workspace on the output was focused by the | 245 | // disabled and an empty workspace on the output was focused by the |
239 | // seat, the seat needs to refocus it's focus inactive to update the | 246 | // seat, the seat needs to refocus its focus inactive to update the |
240 | // value of seat->workspace. | 247 | // value of seat->workspace. |
241 | if (seat->workspace == node->sway_workspace) { | 248 | if (seat->workspace == node->sway_workspace) { |
242 | struct sway_node *node = seat_get_focus_inactive(seat, &root->node); | 249 | struct sway_node *node = seat_get_focus_inactive(seat, &root->node); |
@@ -352,25 +359,15 @@ static void handle_new_node(struct wl_listener *listener, void *data) { | |||
352 | seat_node_from_node(seat, node); | 359 | seat_node_from_node(seat, node); |
353 | } | 360 | } |
354 | 361 | ||
355 | static void drag_icon_damage_whole(struct sway_drag_icon *icon) { | 362 | static void drag_icon_update_position(struct sway_seat *seat, struct wlr_scene_node *node) { |
356 | if (!icon->wlr_drag_icon->mapped) { | 363 | struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON); |
357 | return; | ||
358 | } | ||
359 | desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true); | ||
360 | } | ||
361 | |||
362 | void drag_icon_update_position(struct sway_drag_icon *icon) { | ||
363 | drag_icon_damage_whole(icon); | ||
364 | |||
365 | struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; | ||
366 | struct sway_seat *seat = icon->seat; | ||
367 | struct wlr_cursor *cursor = seat->cursor->cursor; | 364 | struct wlr_cursor *cursor = seat->cursor->cursor; |
365 | |||
368 | switch (wlr_icon->drag->grab_type) { | 366 | switch (wlr_icon->drag->grab_type) { |
369 | case WLR_DRAG_GRAB_KEYBOARD: | 367 | case WLR_DRAG_GRAB_KEYBOARD: |
370 | return; | 368 | return; |
371 | case WLR_DRAG_GRAB_KEYBOARD_POINTER: | 369 | case WLR_DRAG_GRAB_KEYBOARD_POINTER: |
372 | icon->x = cursor->x; | 370 | wlr_scene_node_set_position(node, cursor->x, cursor->y); |
373 | icon->y = cursor->y; | ||
374 | break; | 371 | break; |
375 | case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; | 372 | case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; |
376 | struct wlr_touch_point *point = | 373 | struct wlr_touch_point *point = |
@@ -378,39 +375,15 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { | |||
378 | if (point == NULL) { | 375 | if (point == NULL) { |
379 | return; | 376 | return; |
380 | } | 377 | } |
381 | icon->x = seat->touch_x; | 378 | wlr_scene_node_set_position(node, seat->touch_x, seat->touch_y); |
382 | icon->y = seat->touch_y; | ||
383 | } | 379 | } |
384 | |||
385 | drag_icon_damage_whole(icon); | ||
386 | } | 380 | } |
387 | 381 | ||
388 | static void drag_icon_handle_surface_commit(struct wl_listener *listener, | 382 | void drag_icons_update_position(struct sway_seat *seat) { |
389 | void *data) { | 383 | struct wlr_scene_node *node; |
390 | struct sway_drag_icon *icon = | 384 | wl_list_for_each(node, &seat->drag_icons->children, link) { |
391 | wl_container_of(listener, icon, surface_commit); | 385 | drag_icon_update_position(seat, node); |
392 | drag_icon_update_position(icon); | 386 | } |
393 | } | ||
394 | |||
395 | static void drag_icon_handle_map(struct wl_listener *listener, void *data) { | ||
396 | struct sway_drag_icon *icon = wl_container_of(listener, icon, map); | ||
397 | drag_icon_damage_whole(icon); | ||
398 | } | ||
399 | |||
400 | static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) { | ||
401 | struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap); | ||
402 | drag_icon_damage_whole(icon); | ||
403 | } | ||
404 | |||
405 | static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { | ||
406 | struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy); | ||
407 | icon->wlr_drag_icon->data = NULL; | ||
408 | wl_list_remove(&icon->link); | ||
409 | wl_list_remove(&icon->surface_commit.link); | ||
410 | wl_list_remove(&icon->unmap.link); | ||
411 | wl_list_remove(&icon->map.link); | ||
412 | wl_list_remove(&icon->destroy.link); | ||
413 | free(icon); | ||
414 | } | 387 | } |
415 | 388 | ||
416 | static void drag_handle_destroy(struct wl_listener *listener, void *data) { | 389 | static void drag_handle_destroy(struct wl_listener *listener, void *data) { |
@@ -482,27 +455,20 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { | |||
482 | 455 | ||
483 | struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; | 456 | struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; |
484 | if (wlr_drag_icon != NULL) { | 457 | if (wlr_drag_icon != NULL) { |
485 | struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); | 458 | struct wlr_scene_tree *tree = wlr_scene_drag_icon_create(seat->drag_icons, wlr_drag_icon); |
486 | if (icon == NULL) { | 459 | if (!tree) { |
487 | sway_log(SWAY_ERROR, "Allocation failed"); | 460 | sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree"); |
488 | return; | 461 | return; |
489 | } | 462 | } |
490 | icon->seat = seat; | ||
491 | icon->wlr_drag_icon = wlr_drag_icon; | ||
492 | wlr_drag_icon->data = icon; | ||
493 | 463 | ||
494 | icon->surface_commit.notify = drag_icon_handle_surface_commit; | 464 | if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON, |
495 | wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); | 465 | wlr_drag_icon)) { |
496 | icon->unmap.notify = drag_icon_handle_unmap; | 466 | sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor"); |
497 | wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); | 467 | wlr_scene_node_destroy(&tree->node); |
498 | icon->map.notify = drag_icon_handle_map; | 468 | return; |
499 | wl_signal_add(&wlr_drag_icon->events.map, &icon->map); | 469 | } |
500 | icon->destroy.notify = drag_icon_handle_destroy; | ||
501 | wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); | ||
502 | |||
503 | wl_list_insert(&root->drag_icons, &icon->link); | ||
504 | 470 | ||
505 | drag_icon_update_position(icon); | 471 | drag_icon_update_position(seat, &tree->node); |
506 | } | 472 | } |
507 | seatop_begin_default(seat); | 473 | seatop_begin_default(seat); |
508 | } | 474 | } |
@@ -549,8 +515,18 @@ struct sway_seat *seat_create(const char *seat_name) { | |||
549 | return NULL; | 515 | return NULL; |
550 | } | 516 | } |
551 | 517 | ||
518 | bool failed = false; | ||
519 | seat->scene_tree = alloc_scene_tree(root->layers.seat, &failed); | ||
520 | seat->drag_icons = alloc_scene_tree(seat->scene_tree, &failed); | ||
521 | if (failed) { | ||
522 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
523 | free(seat); | ||
524 | return NULL; | ||
525 | } | ||
526 | |||
552 | seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); | 527 | seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); |
553 | if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { | 528 | if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { |
529 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
554 | free(seat); | 530 | free(seat); |
555 | return NULL; | 531 | return NULL; |
556 | } | 532 | } |
@@ -558,11 +534,15 @@ struct sway_seat *seat_create(const char *seat_name) { | |||
558 | 534 | ||
559 | seat->cursor = sway_cursor_create(seat); | 535 | seat->cursor = sway_cursor_create(seat); |
560 | if (!seat->cursor) { | 536 | if (!seat->cursor) { |
537 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
561 | wlr_seat_destroy(seat->wlr_seat); | 538 | wlr_seat_destroy(seat->wlr_seat); |
562 | free(seat); | 539 | free(seat); |
563 | return NULL; | 540 | return NULL; |
564 | } | 541 | } |
565 | 542 | ||
543 | seat->destroy.notify = handle_seat_destroy; | ||
544 | wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy); | ||
545 | |||
566 | seat->idle_inhibit_sources = seat->idle_wake_sources = | 546 | seat->idle_inhibit_sources = seat->idle_wake_sources = |
567 | IDLE_SOURCE_KEYBOARD | | 547 | IDLE_SOURCE_KEYBOARD | |
568 | IDLE_SOURCE_POINTER | | 548 | IDLE_SOURCE_POINTER | |
@@ -636,7 +616,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { | |||
636 | case WLR_INPUT_DEVICE_TOUCH: | 616 | case WLR_INPUT_DEVICE_TOUCH: |
637 | caps |= WL_SEAT_CAPABILITY_TOUCH; | 617 | caps |= WL_SEAT_CAPABILITY_TOUCH; |
638 | break; | 618 | break; |
639 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 619 | case WLR_INPUT_DEVICE_TABLET: |
640 | caps |= WL_SEAT_CAPABILITY_POINTER; | 620 | caps |= WL_SEAT_CAPABILITY_POINTER; |
641 | break; | 621 | break; |
642 | case WLR_INPUT_DEVICE_SWITCH: | 622 | case WLR_INPUT_DEVICE_SWITCH: |
@@ -654,7 +634,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { | |||
654 | } else { | 634 | } else { |
655 | wlr_seat_set_capabilities(seat->wlr_seat, caps); | 635 | wlr_seat_set_capabilities(seat->wlr_seat, caps); |
656 | if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { | 636 | if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { |
657 | cursor_set_image(seat->cursor, "left_ptr", NULL); | 637 | cursor_set_image(seat->cursor, "default", NULL); |
658 | } | 638 | } |
659 | } | 639 | } |
660 | } | 640 | } |
@@ -694,19 +674,28 @@ static const char *get_builtin_output_name(void) { | |||
694 | static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { | 674 | static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { |
695 | switch (seat_device->input_device->wlr_device->type) { | 675 | switch (seat_device->input_device->wlr_device->type) { |
696 | case WLR_INPUT_DEVICE_TOUCH: | 676 | case WLR_INPUT_DEVICE_TOUCH: |
697 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 677 | case WLR_INPUT_DEVICE_TABLET: |
698 | return true; | 678 | return true; |
699 | default: | 679 | default: |
700 | return false; | 680 | return false; |
701 | } | 681 | } |
702 | } | 682 | } |
703 | 683 | ||
704 | static void seat_apply_input_config(struct sway_seat *seat, | 684 | static void seat_apply_input_mapping(struct sway_seat *seat, |
705 | struct sway_seat_device *sway_device) { | 685 | struct sway_seat_device *sway_device) { |
706 | struct input_config *ic = | 686 | struct input_config *ic = |
707 | input_device_get_config(sway_device->input_device); | 687 | input_device_get_config(sway_device->input_device); |
708 | 688 | ||
709 | sway_log(SWAY_DEBUG, "Applying input config to %s", | 689 | switch (sway_device->input_device->wlr_device->type) { |
690 | case WLR_INPUT_DEVICE_POINTER: | ||
691 | case WLR_INPUT_DEVICE_TOUCH: | ||
692 | case WLR_INPUT_DEVICE_TABLET: | ||
693 | break; | ||
694 | default: | ||
695 | return; // these devices don't support mappings | ||
696 | } | ||
697 | |||
698 | sway_log(SWAY_DEBUG, "Applying input mapping to %s", | ||
710 | sway_device->input_device->identifier); | 699 | sway_device->input_device->identifier); |
711 | 700 | ||
712 | const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output; | 701 | const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output; |
@@ -715,14 +704,26 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
715 | ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; | 704 | ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; |
716 | 705 | ||
717 | switch (mapped_to) { | 706 | switch (mapped_to) { |
718 | case MAPPED_TO_DEFAULT: | 707 | case MAPPED_TO_DEFAULT:; |
719 | /* | 708 | /* |
720 | * If the wlroots backend provides an output name, use that. | 709 | * If the wlroots backend provides an output name, use that. |
721 | * | 710 | * |
722 | * Otherwise, try to map built-in touch and tablet tool devices to the | 711 | * Otherwise, try to map built-in touch and pointer devices to the |
723 | * built-in output. | 712 | * built-in output. |
724 | */ | 713 | */ |
725 | mapped_to_output = sway_device->input_device->wlr_device->output_name; | 714 | struct wlr_input_device *dev = sway_device->input_device->wlr_device; |
715 | switch (dev->type) { | ||
716 | case WLR_INPUT_DEVICE_POINTER: | ||
717 | mapped_to_output = wlr_pointer_from_input_device(dev)->output_name; | ||
718 | break; | ||
719 | case WLR_INPUT_DEVICE_TOUCH: | ||
720 | mapped_to_output = wlr_touch_from_input_device(dev)->output_name; | ||
721 | break; | ||
722 | default: | ||
723 | mapped_to_output = NULL; | ||
724 | break; | ||
725 | } | ||
726 | #if WLR_HAS_LIBINPUT_BACKEND | ||
726 | if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && | 727 | if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && |
727 | sway_libinput_device_is_builtin(sway_device->input_device)) { | 728 | sway_libinput_device_is_builtin(sway_device->input_device)) { |
728 | mapped_to_output = get_builtin_output_name(); | 729 | mapped_to_output = get_builtin_output_name(); |
@@ -731,6 +732,10 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
731 | mapped_to_output, sway_device->input_device->identifier); | 732 | mapped_to_output, sway_device->input_device->identifier); |
732 | } | 733 | } |
733 | } | 734 | } |
735 | #else | ||
736 | (void)is_touch_or_tablet_tool; | ||
737 | (void)get_builtin_output_name; | ||
738 | #endif | ||
734 | if (mapped_to_output == NULL) { | 739 | if (mapped_to_output == NULL) { |
735 | return; | 740 | return; |
736 | } | 741 | } |
@@ -774,12 +779,9 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
774 | 779 | ||
775 | static void seat_configure_pointer(struct sway_seat *seat, | 780 | static void seat_configure_pointer(struct sway_seat *seat, |
776 | struct sway_seat_device *sway_device) { | 781 | struct sway_seat_device *sway_device) { |
777 | if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { | 782 | seat_configure_xcursor(seat); |
778 | seat_configure_xcursor(seat); | ||
779 | } | ||
780 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 783 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
781 | sway_device->input_device->wlr_device); | 784 | sway_device->input_device->wlr_device); |
782 | seat_apply_input_config(seat, sway_device); | ||
783 | wl_event_source_timer_update( | 785 | wl_event_source_timer_update( |
784 | seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); | 786 | seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); |
785 | } | 787 | } |
@@ -790,13 +792,22 @@ static void seat_configure_keyboard(struct sway_seat *seat, | |||
790 | sway_keyboard_create(seat, seat_device); | 792 | sway_keyboard_create(seat, seat_device); |
791 | } | 793 | } |
792 | sway_keyboard_configure(seat_device->keyboard); | 794 | sway_keyboard_configure(seat_device->keyboard); |
793 | wlr_seat_set_keyboard(seat->wlr_seat, | 795 | |
794 | seat_device->input_device->wlr_device); | 796 | // We only need to update the current keyboard, as the rest will be updated |
795 | struct sway_node *focus = seat_get_focus(seat); | 797 | // as they are activated. |
796 | if (focus && node_is_view(focus)) { | 798 | struct wlr_keyboard *wlr_keyboard = |
797 | // force notify reenter to pick up the new configuration | 799 | wlr_keyboard_from_input_device(seat_device->input_device->wlr_device); |
800 | struct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard; | ||
801 | if (wlr_keyboard != current_keyboard) { | ||
802 | return; | ||
803 | } | ||
804 | |||
805 | // force notify reenter to pick up the new configuration. This reuses | ||
806 | // the current focused surface to avoid breaking input grabs. | ||
807 | struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; | ||
808 | if (surface) { | ||
798 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); | 809 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); |
799 | seat_keyboard_notify_enter(seat, focus->sway_container->view->surface); | 810 | seat_keyboard_notify_enter(seat, surface); |
800 | } | 811 | } |
801 | } | 812 | } |
802 | 813 | ||
@@ -805,7 +816,6 @@ static void seat_configure_switch(struct sway_seat *seat, | |||
805 | if (!seat_device->switch_device) { | 816 | if (!seat_device->switch_device) { |
806 | sway_switch_create(seat, seat_device); | 817 | sway_switch_create(seat, seat_device); |
807 | } | 818 | } |
808 | seat_apply_input_config(seat, seat_device); | ||
809 | sway_switch_configure(seat_device->switch_device); | 819 | sway_switch_configure(seat_device->switch_device); |
810 | } | 820 | } |
811 | 821 | ||
@@ -813,7 +823,6 @@ static void seat_configure_touch(struct sway_seat *seat, | |||
813 | struct sway_seat_device *sway_device) { | 823 | struct sway_seat_device *sway_device) { |
814 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 824 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
815 | sway_device->input_device->wlr_device); | 825 | sway_device->input_device->wlr_device); |
816 | seat_apply_input_config(seat, sway_device); | ||
817 | } | 826 | } |
818 | 827 | ||
819 | static void seat_configure_tablet_tool(struct sway_seat *seat, | 828 | static void seat_configure_tablet_tool(struct sway_seat *seat, |
@@ -824,7 +833,6 @@ static void seat_configure_tablet_tool(struct sway_seat *seat, | |||
824 | sway_configure_tablet(sway_device->tablet); | 833 | sway_configure_tablet(sway_device->tablet); |
825 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 834 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
826 | sway_device->input_device->wlr_device); | 835 | sway_device->input_device->wlr_device); |
827 | seat_apply_input_config(seat, sway_device); | ||
828 | } | 836 | } |
829 | 837 | ||
830 | static void seat_configure_tablet_pad(struct sway_seat *seat, | 838 | static void seat_configure_tablet_pad(struct sway_seat *seat, |
@@ -874,13 +882,25 @@ void seat_configure_device(struct sway_seat *seat, | |||
874 | case WLR_INPUT_DEVICE_TOUCH: | 882 | case WLR_INPUT_DEVICE_TOUCH: |
875 | seat_configure_touch(seat, seat_device); | 883 | seat_configure_touch(seat, seat_device); |
876 | break; | 884 | break; |
877 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 885 | case WLR_INPUT_DEVICE_TABLET: |
878 | seat_configure_tablet_tool(seat, seat_device); | 886 | seat_configure_tablet_tool(seat, seat_device); |
879 | break; | 887 | break; |
880 | case WLR_INPUT_DEVICE_TABLET_PAD: | 888 | case WLR_INPUT_DEVICE_TABLET_PAD: |
881 | seat_configure_tablet_pad(seat, seat_device); | 889 | seat_configure_tablet_pad(seat, seat_device); |
882 | break; | 890 | break; |
883 | } | 891 | } |
892 | |||
893 | seat_apply_input_mapping(seat, seat_device); | ||
894 | } | ||
895 | |||
896 | void seat_configure_device_mapping(struct sway_seat *seat, | ||
897 | struct sway_input_device *input_device) { | ||
898 | struct sway_seat_device *seat_device = seat_get_device(seat, input_device); | ||
899 | if (!seat_device) { | ||
900 | return; | ||
901 | } | ||
902 | |||
903 | seat_apply_input_mapping(seat, seat_device); | ||
884 | } | 904 | } |
885 | 905 | ||
886 | void seat_reset_device(struct sway_seat *seat, | 906 | void seat_reset_device(struct sway_seat *seat, |
@@ -901,7 +921,7 @@ void seat_reset_device(struct sway_seat *seat, | |||
901 | case WLR_INPUT_DEVICE_TOUCH: | 921 | case WLR_INPUT_DEVICE_TOUCH: |
902 | seat_reset_input_config(seat, seat_device); | 922 | seat_reset_input_config(seat, seat_device); |
903 | break; | 923 | break; |
904 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 924 | case WLR_INPUT_DEVICE_TABLET: |
905 | seat_reset_input_config(seat, seat_device); | 925 | seat_reset_input_config(seat, seat_device); |
906 | break; | 926 | break; |
907 | case WLR_INPUT_DEVICE_TABLET_PAD: | 927 | case WLR_INPUT_DEVICE_TABLET_PAD: |
@@ -997,7 +1017,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
997 | 1017 | ||
998 | wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); | 1018 | wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); |
999 | struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( | 1019 | struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( |
1000 | server.xwayland.xcursor_manager, "left_ptr", 1); | 1020 | server.xwayland.xcursor_manager, "default", 1); |
1001 | if (xcursor != NULL) { | 1021 | if (xcursor != NULL) { |
1002 | struct wlr_xcursor_image *image = xcursor->images[0]; | 1022 | struct wlr_xcursor_image *image = xcursor->images[0]; |
1003 | wlr_xwayland_set_cursor( | 1023 | wlr_xwayland_set_cursor( |
@@ -1023,32 +1043,35 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
1023 | sway_log(SWAY_ERROR, | 1043 | sway_log(SWAY_ERROR, |
1024 | "Cannot create XCursor manager for theme '%s'", cursor_theme); | 1044 | "Cannot create XCursor manager for theme '%s'", cursor_theme); |
1025 | } | 1045 | } |
1026 | } | ||
1027 | 1046 | ||
1028 | for (int i = 0; i < root->outputs->length; ++i) { | 1047 | |
1029 | struct sway_output *sway_output = root->outputs->items[i]; | 1048 | for (int i = 0; i < root->outputs->length; ++i) { |
1030 | struct wlr_output *output = sway_output->wlr_output; | 1049 | struct sway_output *sway_output = root->outputs->items[i]; |
1031 | bool result = | 1050 | struct wlr_output *output = sway_output->wlr_output; |
1032 | wlr_xcursor_manager_load(seat->cursor->xcursor_manager, | 1051 | bool result = |
1033 | output->scale); | 1052 | wlr_xcursor_manager_load(seat->cursor->xcursor_manager, |
1034 | if (!result) { | 1053 | output->scale); |
1035 | sway_log(SWAY_ERROR, | 1054 | if (!result) { |
1036 | "Cannot load xcursor theme for output '%s' with scale %f", | 1055 | sway_log(SWAY_ERROR, |
1037 | output->name, output->scale); | 1056 | "Cannot load xcursor theme for output '%s' with scale %f", |
1057 | output->name, output->scale); | ||
1058 | } | ||
1038 | } | 1059 | } |
1039 | } | ||
1040 | 1060 | ||
1041 | // Reset the cursor so that we apply it to outputs that just appeared | 1061 | // Reset the cursor so that we apply it to outputs that just appeared |
1042 | cursor_set_image(seat->cursor, NULL, NULL); | 1062 | cursor_set_image(seat->cursor, NULL, NULL); |
1043 | cursor_set_image(seat->cursor, "left_ptr", NULL); | 1063 | cursor_set_image(seat->cursor, "default", NULL); |
1044 | wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, | 1064 | wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, |
1045 | seat->cursor->cursor->y); | 1065 | seat->cursor->cursor->y); |
1066 | } | ||
1046 | } | 1067 | } |
1047 | 1068 | ||
1048 | bool seat_is_input_allowed(struct sway_seat *seat, | 1069 | bool seat_is_input_allowed(struct sway_seat *seat, |
1049 | struct wlr_surface *surface) { | 1070 | struct wlr_surface *surface) { |
1050 | struct wl_client *client = wl_resource_get_client(surface->resource); | 1071 | if (server.session_lock.lock) { |
1051 | return !seat->exclusive_client || seat->exclusive_client == client; | 1072 | return sway_session_lock_has_surface(server.session_lock.lock, surface); |
1073 | } | ||
1074 | return true; | ||
1052 | } | 1075 | } |
1053 | 1076 | ||
1054 | static void send_unfocus(struct sway_container *con, void *data) { | 1077 | static void send_unfocus(struct sway_container *con, void *data) { |
@@ -1107,15 +1130,7 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1107 | } | 1130 | } |
1108 | } | 1131 | } |
1109 | 1132 | ||
1110 | void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | 1133 | static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *node) { |
1111 | if (seat->focused_layer) { | ||
1112 | struct wlr_layer_surface_v1 *layer = seat->focused_layer; | ||
1113 | seat_set_focus_layer(seat, NULL); | ||
1114 | seat_set_focus(seat, node); | ||
1115 | seat_set_focus_layer(seat, layer); | ||
1116 | return; | ||
1117 | } | ||
1118 | |||
1119 | struct sway_node *last_focus = seat_get_focus(seat); | 1134 | struct sway_node *last_focus = seat_get_focus(seat); |
1120 | if (last_focus == node) { | 1135 | if (last_focus == node) { |
1121 | return; | 1136 | return; |
@@ -1248,6 +1263,24 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1248 | } | 1263 | } |
1249 | } | 1264 | } |
1250 | 1265 | ||
1266 | void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | ||
1267 | // Prevents the layer from losing focus if it has keyboard exclusivity | ||
1268 | if (seat->has_exclusive_layer) { | ||
1269 | struct wlr_layer_surface_v1 *layer = seat->focused_layer; | ||
1270 | seat_set_focus_layer(seat, NULL); | ||
1271 | seat_set_workspace_focus(seat, node); | ||
1272 | seat_set_focus_layer(seat, layer); | ||
1273 | } else if (seat->focused_layer) { | ||
1274 | seat_set_focus_layer(seat, NULL); | ||
1275 | seat_set_workspace_focus(seat, node); | ||
1276 | } else { | ||
1277 | seat_set_workspace_focus(seat, node); | ||
1278 | } | ||
1279 | if (server.session_lock.lock) { | ||
1280 | seat_set_focus_surface(seat, server.session_lock.lock->focused, false); | ||
1281 | } | ||
1282 | } | ||
1283 | |||
1251 | void seat_set_focus_container(struct sway_seat *seat, | 1284 | void seat_set_focus_container(struct sway_seat *seat, |
1252 | struct sway_container *con) { | 1285 | struct sway_container *con) { |
1253 | seat_set_focus(seat, con ? &con->node : NULL); | 1286 | seat_set_focus(seat, con ? &con->node : NULL); |
@@ -1273,7 +1306,7 @@ void seat_set_focus_surface(struct sway_seat *seat, | |||
1273 | } | 1306 | } |
1274 | 1307 | ||
1275 | sway_input_method_relay_set_focus(&seat->im_relay, surface); | 1308 | sway_input_method_relay_set_focus(&seat->im_relay, surface); |
1276 | seat_tablet_pads_notify_enter(seat, surface); | 1309 | seat_tablet_pads_set_focus(seat, surface); |
1277 | } | 1310 | } |
1278 | 1311 | ||
1279 | void seat_set_focus_layer(struct sway_seat *seat, | 1312 | void seat_set_focus_layer(struct sway_seat *seat, |
@@ -1287,28 +1320,23 @@ void seat_set_focus_layer(struct sway_seat *seat, | |||
1287 | seat_set_focus(seat, previous); | 1320 | seat_set_focus(seat, previous); |
1288 | } | 1321 | } |
1289 | return; | 1322 | return; |
1290 | } else if (!layer || seat->focused_layer == layer) { | 1323 | } else if (!layer) { |
1291 | return; | 1324 | return; |
1292 | } | 1325 | } |
1293 | assert(layer->mapped); | 1326 | assert(layer->surface->mapped); |
1294 | seat_set_focus_surface(seat, layer->surface, true); | 1327 | if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP && |
1295 | if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { | 1328 | layer->current.keyboard_interactive |
1296 | seat->focused_layer = layer; | 1329 | == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { |
1330 | seat->has_exclusive_layer = true; | ||
1297 | } | 1331 | } |
1298 | } | 1332 | if (seat->focused_layer == layer) { |
1299 | |||
1300 | void seat_set_exclusive_client(struct sway_seat *seat, | ||
1301 | struct wl_client *client) { | ||
1302 | if (!client) { | ||
1303 | seat->exclusive_client = client; | ||
1304 | // Triggers a refocus of the topmost surface layer if necessary | ||
1305 | // TODO: Make layer surface focus per-output based on cursor position | ||
1306 | for (int i = 0; i < root->outputs->length; ++i) { | ||
1307 | struct sway_output *output = root->outputs->items[i]; | ||
1308 | arrange_layers(output); | ||
1309 | } | ||
1310 | return; | 1333 | return; |
1311 | } | 1334 | } |
1335 | seat_set_focus_surface(seat, layer->surface, true); | ||
1336 | seat->focused_layer = layer; | ||
1337 | } | ||
1338 | |||
1339 | void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client) { | ||
1312 | if (seat->focused_layer) { | 1340 | if (seat->focused_layer) { |
1313 | if (wl_resource_get_client(seat->focused_layer->resource) != client) { | 1341 | if (wl_resource_get_client(seat->focused_layer->resource) != client) { |
1314 | seat_set_focus_layer(seat, NULL); | 1342 | seat_set_focus_layer(seat, NULL); |
@@ -1335,7 +1363,6 @@ void seat_set_exclusive_client(struct sway_seat *seat, | |||
1335 | now.tv_nsec / 1000, point->touch_id); | 1363 | now.tv_nsec / 1000, point->touch_id); |
1336 | } | 1364 | } |
1337 | } | 1365 | } |
1338 | seat->exclusive_client = client; | ||
1339 | } | 1366 | } |
1340 | 1367 | ||
1341 | struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, | 1368 | struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, |
@@ -1416,9 +1443,8 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) { | |||
1416 | if (!seat->has_focus) { | 1443 | if (!seat->has_focus) { |
1417 | return NULL; | 1444 | return NULL; |
1418 | } | 1445 | } |
1419 | if (wl_list_empty(&seat->focus_stack)) { | 1446 | sway_assert(!wl_list_empty(&seat->focus_stack), |
1420 | return NULL; | 1447 | "focus_stack is empty, but has_focus is true"); |
1421 | } | ||
1422 | struct sway_seat_node *current = | 1448 | struct sway_seat_node *current = |
1423 | wl_container_of(seat->focus_stack.next, current, link); | 1449 | wl_container_of(seat->focus_stack.next, current, link); |
1424 | return current->node; | 1450 | return current->node; |
@@ -1503,7 +1529,7 @@ struct seat_config *seat_get_config_by_name(const char *name) { | |||
1503 | } | 1529 | } |
1504 | 1530 | ||
1505 | void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, | 1531 | void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, |
1506 | uint32_t button, enum wlr_button_state state) { | 1532 | uint32_t button, enum wl_pointer_button_state state) { |
1507 | seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, | 1533 | seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, |
1508 | time_msec, button, state); | 1534 | time_msec, button, state); |
1509 | } | 1535 | } |
@@ -1540,7 +1566,7 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) { | |||
1540 | 1566 | ||
1541 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, | 1567 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, |
1542 | struct wlr_input_device *device, uint32_t button, | 1568 | struct wlr_input_device *device, uint32_t button, |
1543 | enum wlr_button_state state) { | 1569 | enum wl_pointer_button_state state) { |
1544 | if (seat->seatop_impl->button) { | 1570 | if (seat->seatop_impl->button) { |
1545 | seat->seatop_impl->button(seat, time_msec, device, button, state); | 1571 | seat->seatop_impl->button(seat, time_msec, device, button, state); |
1546 | } | 1572 | } |
@@ -1553,12 +1579,38 @@ void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
1553 | } | 1579 | } |
1554 | 1580 | ||
1555 | void seatop_pointer_axis(struct sway_seat *seat, | 1581 | void seatop_pointer_axis(struct sway_seat *seat, |
1556 | struct wlr_event_pointer_axis *event) { | 1582 | struct wlr_pointer_axis_event *event) { |
1557 | if (seat->seatop_impl->pointer_axis) { | 1583 | if (seat->seatop_impl->pointer_axis) { |
1558 | seat->seatop_impl->pointer_axis(seat, event); | 1584 | seat->seatop_impl->pointer_axis(seat, event); |
1559 | } | 1585 | } |
1560 | } | 1586 | } |
1561 | 1587 | ||
1588 | void seatop_touch_motion(struct sway_seat *seat, struct wlr_touch_motion_event *event, | ||
1589 | double lx, double ly) { | ||
1590 | if (seat->seatop_impl->touch_motion) { | ||
1591 | seat->seatop_impl->touch_motion(seat, event, lx, ly); | ||
1592 | } | ||
1593 | } | ||
1594 | |||
1595 | void seatop_touch_up(struct sway_seat *seat, struct wlr_touch_up_event *event) { | ||
1596 | if (seat->seatop_impl->touch_up) { | ||
1597 | seat->seatop_impl->touch_up(seat, event); | ||
1598 | } | ||
1599 | } | ||
1600 | |||
1601 | void seatop_touch_down(struct sway_seat *seat, struct wlr_touch_down_event *event, | ||
1602 | double lx, double ly) { | ||
1603 | if (seat->seatop_impl->touch_down) { | ||
1604 | seat->seatop_impl->touch_down(seat, event, lx, ly); | ||
1605 | } | ||
1606 | } | ||
1607 | |||
1608 | void seatop_touch_cancel(struct sway_seat *seat, struct wlr_touch_cancel_event *event) { | ||
1609 | if (seat->seatop_impl->touch_cancel) { | ||
1610 | seat->seatop_impl->touch_cancel(seat, event); | ||
1611 | } | ||
1612 | } | ||
1613 | |||
1562 | void seatop_tablet_tool_tip(struct sway_seat *seat, | 1614 | void seatop_tablet_tool_tip(struct sway_seat *seat, |
1563 | struct sway_tablet_tool *tool, uint32_t time_msec, | 1615 | struct sway_tablet_tool *tool, uint32_t time_msec, |
1564 | enum wlr_tablet_tool_tip_state state) { | 1616 | enum wlr_tablet_tool_tip_state state) { |
@@ -1576,6 +1628,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat, | |||
1576 | } | 1628 | } |
1577 | } | 1629 | } |
1578 | 1630 | ||
1631 | void seatop_hold_begin(struct sway_seat *seat, | ||
1632 | struct wlr_pointer_hold_begin_event *event) { | ||
1633 | if (seat->seatop_impl->hold_begin) { | ||
1634 | seat->seatop_impl->hold_begin(seat, event); | ||
1635 | } | ||
1636 | } | ||
1637 | |||
1638 | void seatop_hold_end(struct sway_seat *seat, | ||
1639 | struct wlr_pointer_hold_end_event *event) { | ||
1640 | if (seat->seatop_impl->hold_end) { | ||
1641 | seat->seatop_impl->hold_end(seat, event); | ||
1642 | } | ||
1643 | } | ||
1644 | |||
1645 | void seatop_pinch_begin(struct sway_seat *seat, | ||
1646 | struct wlr_pointer_pinch_begin_event *event) { | ||
1647 | if (seat->seatop_impl->pinch_begin) { | ||
1648 | seat->seatop_impl->pinch_begin(seat, event); | ||
1649 | } | ||
1650 | } | ||
1651 | |||
1652 | void seatop_pinch_update(struct sway_seat *seat, | ||
1653 | struct wlr_pointer_pinch_update_event *event) { | ||
1654 | if (seat->seatop_impl->pinch_update) { | ||
1655 | seat->seatop_impl->pinch_update(seat, event); | ||
1656 | } | ||
1657 | } | ||
1658 | |||
1659 | void seatop_pinch_end(struct sway_seat *seat, | ||
1660 | struct wlr_pointer_pinch_end_event *event) { | ||
1661 | if (seat->seatop_impl->pinch_end) { | ||
1662 | seat->seatop_impl->pinch_end(seat, event); | ||
1663 | } | ||
1664 | } | ||
1665 | |||
1666 | void seatop_swipe_begin(struct sway_seat *seat, | ||
1667 | struct wlr_pointer_swipe_begin_event *event) { | ||
1668 | if (seat->seatop_impl->swipe_begin) { | ||
1669 | seat->seatop_impl->swipe_begin(seat, event); | ||
1670 | } | ||
1671 | } | ||
1672 | |||
1673 | void seatop_swipe_update(struct sway_seat *seat, | ||
1674 | struct wlr_pointer_swipe_update_event *event) { | ||
1675 | if (seat->seatop_impl->swipe_update) { | ||
1676 | seat->seatop_impl->swipe_update(seat, event); | ||
1677 | } | ||
1678 | } | ||
1679 | |||
1680 | void seatop_swipe_end(struct sway_seat *seat, | ||
1681 | struct wlr_pointer_swipe_end_event *event) { | ||
1682 | if (seat->seatop_impl->swipe_end) { | ||
1683 | seat->seatop_impl->swipe_end(seat, event); | ||
1684 | } | ||
1685 | } | ||
1686 | |||
1579 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { | 1687 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { |
1580 | if (seat->seatop_impl->rebase) { | 1688 | if (seat->seatop_impl->rebase) { |
1581 | seat->seatop_impl->rebase(seat, time_msec); | 1689 | seat->seatop_impl->rebase(seat, time_msec); |
@@ -1591,13 +1699,6 @@ void seatop_end(struct sway_seat *seat) { | |||
1591 | seat->seatop_impl = NULL; | 1699 | seat->seatop_impl = NULL; |
1592 | } | 1700 | } |
1593 | 1701 | ||
1594 | void seatop_render(struct sway_seat *seat, struct sway_output *output, | ||
1595 | pixman_region32_t *damage) { | ||
1596 | if (seat->seatop_impl->render) { | ||
1597 | seat->seatop_impl->render(seat, output, damage); | ||
1598 | } | ||
1599 | } | ||
1600 | |||
1601 | bool seatop_allows_set_cursor(struct sway_seat *seat) { | 1702 | bool seatop_allows_set_cursor(struct sway_seat *seat) { |
1602 | return seat->seatop_impl->allow_set_cursor; | 1703 | return seat->seatop_impl->allow_set_cursor; |
1603 | } | 1704 | } |
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index f9eb8c8a..0c6f7c5e 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c | |||
@@ -1,14 +1,17 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <libevdev/libevdev.h> | 2 | #include <libevdev/libevdev.h> |
4 | #include <wlr/types/wlr_cursor.h> | 3 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/types/wlr_subcompositor.h> | ||
5 | #include <wlr/types/wlr_tablet_v2.h> | 5 | #include <wlr/types/wlr_tablet_v2.h> |
6 | #include <wlr/types/wlr_xcursor_manager.h> | 6 | #include <wlr/types/wlr_xcursor_manager.h> |
7 | #include "gesture.h" | ||
7 | #include "sway/desktop/transaction.h" | 8 | #include "sway/desktop/transaction.h" |
8 | #include "sway/input/cursor.h" | 9 | #include "sway/input/cursor.h" |
9 | #include "sway/input/seat.h" | 10 | #include "sway/input/seat.h" |
10 | #include "sway/input/tablet.h" | 11 | #include "sway/input/tablet.h" |
12 | #include "sway/layers.h" | ||
11 | #include "sway/output.h" | 13 | #include "sway/output.h" |
14 | #include "sway/scene_descriptor.h" | ||
12 | #include "sway/tree/view.h" | 15 | #include "sway/tree/view.h" |
13 | #include "sway/tree/workspace.h" | 16 | #include "sway/tree/workspace.h" |
14 | #include "log.h" | 17 | #include "log.h" |
@@ -20,6 +23,7 @@ struct seatop_default_event { | |||
20 | struct sway_node *previous_node; | 23 | struct sway_node *previous_node; |
21 | uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; | 24 | uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; |
22 | size_t pressed_button_count; | 25 | size_t pressed_button_count; |
26 | struct gesture_tracker gestures; | ||
23 | }; | 27 | }; |
24 | 28 | ||
25 | /*-----------------------------------------\ | 29 | /*-----------------------------------------\ |
@@ -51,6 +55,9 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { | |||
51 | while (cont) { | 55 | while (cont) { |
52 | if (container_parent_layout(cont) == layout) { | 56 | if (container_parent_layout(cont) == layout) { |
53 | list_t *siblings = container_get_siblings(cont); | 57 | list_t *siblings = container_get_siblings(cont); |
58 | if (!siblings) { | ||
59 | return false; | ||
60 | } | ||
54 | int index = list_find(siblings, cont); | 61 | int index = list_find(siblings, cont); |
55 | if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { | 62 | if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { |
56 | return false; | 63 | return false; |
@@ -226,14 +233,15 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
226 | struct sway_container *cont = node && node->type == N_CONTAINER ? | 233 | struct sway_container *cont = node && node->type == N_CONTAINER ? |
227 | node->sway_container : NULL; | 234 | node->sway_container : NULL; |
228 | 235 | ||
229 | if (wlr_surface_is_layer_surface(surface)) { | 236 | struct wlr_layer_surface_v1 *layer; |
237 | #if HAVE_XWAYLAND | ||
238 | struct wlr_xwayland_surface *xsurface; | ||
239 | #endif | ||
240 | if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) && | ||
241 | layer->current.keyboard_interactive) { | ||
230 | // Handle tapping a layer surface | 242 | // Handle tapping a layer surface |
231 | struct wlr_layer_surface_v1 *layer = | 243 | seat_set_focus_layer(seat, layer); |
232 | wlr_layer_surface_v1_from_wlr_surface(surface); | 244 | transaction_commit_dirty(); |
233 | if (layer->current.keyboard_interactive) { | ||
234 | seat_set_focus_layer(seat, layer); | ||
235 | transaction_commit_dirty(); | ||
236 | } | ||
237 | } else if (cont) { | 245 | } else if (cont) { |
238 | bool is_floating_or_child = container_is_floating_or_child(cont); | 246 | bool is_floating_or_child = container_is_floating_or_child(cont); |
239 | bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); | 247 | bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); |
@@ -258,20 +266,17 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
258 | 266 | ||
259 | // Handle tapping on a container surface | 267 | // Handle tapping on a container surface |
260 | seat_set_focus_container(seat, cont); | 268 | seat_set_focus_container(seat, cont); |
261 | seatop_begin_down(seat, node->sway_container, time_msec, sx, sy); | 269 | seatop_begin_down(seat, node->sway_container, sx, sy); |
262 | } | 270 | } |
263 | #if HAVE_XWAYLAND | 271 | #if HAVE_XWAYLAND |
264 | // Handle tapping on an xwayland unmanaged view | 272 | // Handle tapping on an xwayland unmanaged view |
265 | else if (wlr_surface_is_xwayland_surface(surface)) { | 273 | else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && |
266 | struct wlr_xwayland_surface *xsurface = | 274 | xsurface->override_redirect && |
267 | wlr_xwayland_surface_from_wlr_surface(surface); | 275 | wlr_xwayland_or_surface_wants_focus(xsurface)) { |
268 | if (xsurface->override_redirect && | 276 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; |
269 | wlr_xwayland_or_surface_wants_focus(xsurface)) { | 277 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
270 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | 278 | seat_set_focus_surface(seat, xsurface->surface, false); |
271 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 279 | transaction_commit_dirty(); |
272 | seat_set_focus_surface(seat, xsurface->surface, false); | ||
273 | transaction_commit_dirty(); | ||
274 | } | ||
275 | } | 280 | } |
276 | #endif | 281 | #endif |
277 | 282 | ||
@@ -285,7 +290,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
285 | 290 | ||
286 | static bool trigger_pointer_button_binding(struct sway_seat *seat, | 291 | static bool trigger_pointer_button_binding(struct sway_seat *seat, |
287 | struct wlr_input_device *device, uint32_t button, | 292 | struct wlr_input_device *device, uint32_t button, |
288 | enum wlr_button_state state, uint32_t modifiers, | 293 | enum wl_pointer_button_state state, uint32_t modifiers, |
289 | bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { | 294 | bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { |
290 | // We can reach this for non-pointer devices if we're currently emulating | 295 | // We can reach this for non-pointer devices if we're currently emulating |
291 | // pointer input for one. Emulated input should not trigger bindings. The | 296 | // pointer input for one. Emulated input should not trigger bindings. The |
@@ -299,7 +304,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat, | |||
299 | char *device_identifier = device ? input_device_get_identifier(device) | 304 | char *device_identifier = device ? input_device_get_identifier(device) |
300 | : strdup("*"); | 305 | : strdup("*"); |
301 | struct sway_binding *binding = NULL; | 306 | struct sway_binding *binding = NULL; |
302 | if (state == WLR_BUTTON_PRESSED) { | 307 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { |
303 | state_add_button(e, button); | 308 | state_add_button(e, button); |
304 | binding = get_active_mouse_binding(e, | 309 | binding = get_active_mouse_binding(e, |
305 | config->current_mode->mouse_bindings, modifiers, false, | 310 | config->current_mode->mouse_bindings, modifiers, false, |
@@ -324,7 +329,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat, | |||
324 | 329 | ||
325 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 330 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
326 | struct wlr_input_device *device, uint32_t button, | 331 | struct wlr_input_device *device, uint32_t button, |
327 | enum wlr_button_state state) { | 332 | enum wl_pointer_button_state state) { |
328 | struct sway_cursor *cursor = seat->cursor; | 333 | struct sway_cursor *cursor = seat->cursor; |
329 | 334 | ||
330 | // Determine what's under the cursor | 335 | // Determine what's under the cursor |
@@ -357,7 +362,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
357 | 362 | ||
358 | // Handle clicking an empty workspace | 363 | // Handle clicking an empty workspace |
359 | if (node && node->type == N_WORKSPACE) { | 364 | if (node && node->type == N_WORKSPACE) { |
360 | if (state == WLR_BUTTON_PRESSED) { | 365 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { |
361 | seat_set_focus(seat, node); | 366 | seat_set_focus(seat, node); |
362 | transaction_commit_dirty(); | 367 | transaction_commit_dirty(); |
363 | } | 368 | } |
@@ -365,21 +370,23 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
365 | return; | 370 | return; |
366 | } | 371 | } |
367 | 372 | ||
368 | // Handle clicking a layer surface | 373 | // Handle clicking a layer surface and its popups/subsurfaces |
369 | if (surface && wlr_surface_is_layer_surface(surface)) { | 374 | struct wlr_layer_surface_v1 *layer = NULL; |
370 | struct wlr_layer_surface_v1 *layer = | 375 | if ((layer = toplevel_layer_surface_from_surface(surface))) { |
371 | wlr_layer_surface_v1_from_wlr_surface(surface); | ||
372 | if (layer->current.keyboard_interactive) { | 376 | if (layer->current.keyboard_interactive) { |
373 | seat_set_focus_layer(seat, layer); | 377 | seat_set_focus_layer(seat, layer); |
374 | transaction_commit_dirty(); | 378 | transaction_commit_dirty(); |
375 | } | 379 | } |
380 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { | ||
381 | seatop_begin_down_on_surface(seat, surface, sx, sy); | ||
382 | } | ||
376 | seat_pointer_notify_button(seat, time_msec, button, state); | 383 | seat_pointer_notify_button(seat, time_msec, button, state); |
377 | return; | 384 | return; |
378 | } | 385 | } |
379 | 386 | ||
380 | // Handle tiling resize via border | 387 | // Handle tiling resize via border |
381 | if (cont && resize_edge && button == BTN_LEFT && | 388 | if (cont && resize_edge && button == BTN_LEFT && |
382 | state == WLR_BUTTON_PRESSED && !is_floating) { | 389 | state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) { |
383 | // If a resize is triggered on a tabbed or stacked container, change | 390 | // If a resize is triggered on a tabbed or stacked container, change |
384 | // focus to the tab which already had inactive focus -- otherwise, we'd | 391 | // focus to the tab which already had inactive focus -- otherwise, we'd |
385 | // change the active tab when the user probably just wanted to resize. | 392 | // change the active tab when the user probably just wanted to resize. |
@@ -397,7 +404,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
397 | // Handle tiling resize via mod | 404 | // Handle tiling resize via mod |
398 | bool mod_pressed = modifiers & config->floating_mod; | 405 | bool mod_pressed = modifiers & config->floating_mod; |
399 | if (cont && !is_floating_or_child && mod_pressed && | 406 | if (cont && !is_floating_or_child && mod_pressed && |
400 | state == WLR_BUTTON_PRESSED) { | 407 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
401 | uint32_t btn_resize = config->floating_mod_inverse ? | 408 | uint32_t btn_resize = config->floating_mod_inverse ? |
402 | BTN_LEFT : BTN_RIGHT; | 409 | BTN_LEFT : BTN_RIGHT; |
403 | if (button == btn_resize) { | 410 | if (button == btn_resize) { |
@@ -424,13 +431,31 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
424 | } | 431 | } |
425 | } | 432 | } |
426 | 433 | ||
434 | // Handle changing focus when clicking on a container | ||
435 | if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { | ||
436 | // Default case: focus the container that was just clicked. | ||
437 | node = &cont->node; | ||
438 | |||
439 | // If the container is a tab/stacked container and the click happened | ||
440 | // on a tab, switch to the tab. If the tab contents were already | ||
441 | // focused, focus the tab container itself. If the tab container was | ||
442 | // already focused, cycle back to focusing the tab contents. | ||
443 | if (on_titlebar) { | ||
444 | struct sway_container *focus = seat_get_focused_container(seat); | ||
445 | if (focus == cont || !container_has_ancestor(focus, cont)) { | ||
446 | node = seat_get_focus_inactive(seat, &cont->node); | ||
447 | } | ||
448 | } | ||
449 | |||
450 | seat_set_focus(seat, node); | ||
451 | transaction_commit_dirty(); | ||
452 | } | ||
453 | |||
427 | // Handle beginning floating move | 454 | // Handle beginning floating move |
428 | if (cont && is_floating_or_child && !is_fullscreen_or_child && | 455 | if (cont && is_floating_or_child && !is_fullscreen_or_child && |
429 | state == WLR_BUTTON_PRESSED) { | 456 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
430 | uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; | 457 | uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; |
431 | if (button == btn_move && (mod_pressed || on_titlebar)) { | 458 | if (button == btn_move && (mod_pressed || on_titlebar)) { |
432 | seat_set_focus_container(seat, | ||
433 | seat_get_focus_inactive_view(seat, &cont->node)); | ||
434 | seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); | 459 | seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); |
435 | return; | 460 | return; |
436 | } | 461 | } |
@@ -438,9 +463,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
438 | 463 | ||
439 | // Handle beginning floating resize | 464 | // Handle beginning floating resize |
440 | if (cont && is_floating_or_child && !is_fullscreen_or_child && | 465 | if (cont && is_floating_or_child && !is_fullscreen_or_child && |
441 | state == WLR_BUTTON_PRESSED) { | 466 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
442 | // Via border | 467 | // Via border |
443 | if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { | 468 | if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { |
469 | seat_set_focus_container(seat, cont); | ||
444 | seatop_begin_resize_floating(seat, cont, resize_edge); | 470 | seatop_begin_resize_floating(seat, cont, resize_edge); |
445 | return; | 471 | return; |
446 | } | 472 | } |
@@ -455,6 +481,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
455 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; | 481 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; |
456 | edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? | 482 | edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? |
457 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; | 483 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; |
484 | seat_set_focus_container(seat, floater); | ||
458 | seatop_begin_resize_floating(seat, floater, edge); | 485 | seatop_begin_resize_floating(seat, floater, edge); |
459 | return; | 486 | return; |
460 | } | 487 | } |
@@ -462,55 +489,43 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
462 | 489 | ||
463 | // Handle moving a tiling container | 490 | // Handle moving a tiling container |
464 | if (config->tiling_drag && (mod_pressed || on_titlebar) && | 491 | if (config->tiling_drag && (mod_pressed || on_titlebar) && |
465 | state == WLR_BUTTON_PRESSED && !is_floating_or_child && | 492 | state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child && |
466 | cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { | 493 | cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { |
467 | struct sway_container *focus = seat_get_focused_container(seat); | 494 | // If moving a container by its title bar, use a threshold for the drag |
468 | bool focused = focus == cont || container_has_ancestor(focus, cont); | ||
469 | if (on_titlebar && !focused) { | ||
470 | node = seat_get_focus_inactive(seat, &cont->node); | ||
471 | seat_set_focus(seat, node); | ||
472 | } | ||
473 | |||
474 | // If moving a container by it's title bar, use a threshold for the drag | ||
475 | if (!mod_pressed && config->tiling_drag_threshold > 0) { | 495 | if (!mod_pressed && config->tiling_drag_threshold > 0) { |
476 | seatop_begin_move_tiling_threshold(seat, cont); | 496 | seatop_begin_move_tiling_threshold(seat, cont); |
477 | } else { | 497 | } else { |
478 | seatop_begin_move_tiling(seat, cont); | 498 | seatop_begin_move_tiling(seat, cont); |
479 | } | 499 | } |
500 | |||
480 | return; | 501 | return; |
481 | } | 502 | } |
482 | 503 | ||
483 | // Handle mousedown on a container surface | 504 | // Handle mousedown on a container surface |
484 | if (surface && cont && state == WLR_BUTTON_PRESSED) { | 505 | if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { |
485 | seat_set_focus_container(seat, cont); | 506 | seatop_begin_down(seat, cont, sx, sy); |
486 | seatop_begin_down(seat, cont, time_msec, sx, sy); | 507 | seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED); |
487 | seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); | ||
488 | return; | 508 | return; |
489 | } | 509 | } |
490 | 510 | ||
491 | // Handle clicking a container surface or decorations | 511 | // Handle clicking a container surface or decorations |
492 | if (cont && state == WLR_BUTTON_PRESSED) { | 512 | if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { |
493 | node = seat_get_focus_inactive(seat, &cont->node); | ||
494 | seat_set_focus(seat, node); | ||
495 | transaction_commit_dirty(); | ||
496 | seat_pointer_notify_button(seat, time_msec, button, state); | 513 | seat_pointer_notify_button(seat, time_msec, button, state); |
497 | return; | 514 | return; |
498 | } | 515 | } |
499 | 516 | ||
500 | #if HAVE_XWAYLAND | 517 | #if HAVE_XWAYLAND |
501 | // Handle clicking on xwayland unmanaged view | 518 | // Handle clicking on xwayland unmanaged view |
502 | if (surface && wlr_surface_is_xwayland_surface(surface)) { | 519 | struct wlr_xwayland_surface *xsurface; |
503 | struct wlr_xwayland_surface *xsurface = | 520 | if (surface && |
504 | wlr_xwayland_surface_from_wlr_surface(surface); | 521 | (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && |
505 | if (xsurface->override_redirect && | 522 | xsurface->override_redirect && |
506 | wlr_xwayland_or_surface_wants_focus(xsurface)) { | 523 | wlr_xwayland_or_surface_wants_focus(xsurface)) { |
507 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | 524 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; |
508 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 525 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
509 | seat_set_focus_surface(seat, xsurface->surface, false); | 526 | seat_set_focus_surface(seat, xsurface->surface, false); |
510 | transaction_commit_dirty(); | 527 | transaction_commit_dirty(); |
511 | seat_pointer_notify_button(seat, time_msec, button, state); | 528 | seat_pointer_notify_button(seat, time_msec, button, state); |
512 | return; | ||
513 | } | ||
514 | } | 529 | } |
515 | #endif | 530 | #endif |
516 | 531 | ||
@@ -533,6 +548,21 @@ static void check_focus_follows_mouse(struct sway_seat *seat, | |||
533 | if (wlr_output == NULL) { | 548 | if (wlr_output == NULL) { |
534 | return; | 549 | return; |
535 | } | 550 | } |
551 | |||
552 | struct wlr_surface *surface = NULL; | ||
553 | double sx, sy; | ||
554 | node_at_coords(seat, seat->cursor->cursor->x, seat->cursor->cursor->y, | ||
555 | &surface, &sx, &sy); | ||
556 | |||
557 | // Focus topmost layer surface | ||
558 | struct wlr_layer_surface_v1 *layer = NULL; | ||
559 | if ((layer = toplevel_layer_surface_from_surface(surface)) && | ||
560 | layer->current.keyboard_interactive) { | ||
561 | seat_set_focus_layer(seat, layer); | ||
562 | transaction_commit_dirty(); | ||
563 | return; | ||
564 | } | ||
565 | |||
536 | struct sway_output *hovered_output = wlr_output->data; | 566 | struct sway_output *hovered_output = wlr_output->data; |
537 | if (focus && hovered_output != node_get_output(focus)) { | 567 | if (focus && hovered_output != node_get_output(focus)) { |
538 | struct sway_workspace *ws = output_get_active_workspace(hovered_output); | 568 | struct sway_workspace *ws = output_get_active_workspace(hovered_output); |
@@ -593,12 +623,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
593 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 623 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
594 | } | 624 | } |
595 | 625 | ||
596 | struct sway_drag_icon *drag_icon; | 626 | drag_icons_update_position(seat); |
597 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
598 | if (drag_icon->seat == seat) { | ||
599 | drag_icon_update_position(drag_icon); | ||
600 | } | ||
601 | } | ||
602 | 627 | ||
603 | e->previous_node = node; | 628 | e->previous_node = node; |
604 | } | 629 | } |
@@ -628,25 +653,50 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, | |||
628 | wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); | 653 | wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); |
629 | } | 654 | } |
630 | 655 | ||
631 | struct sway_drag_icon *drag_icon; | 656 | drag_icons_update_position(seat); |
632 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
633 | if (drag_icon->seat == seat) { | ||
634 | drag_icon_update_position(drag_icon); | ||
635 | } | ||
636 | } | ||
637 | 657 | ||
638 | e->previous_node = node; | 658 | e->previous_node = node; |
639 | } | 659 | } |
640 | 660 | ||
661 | static void handle_touch_down(struct sway_seat *seat, | ||
662 | struct wlr_touch_down_event *event, double lx, double ly) { | ||
663 | struct wlr_surface *surface = NULL; | ||
664 | struct wlr_seat *wlr_seat = seat->wlr_seat; | ||
665 | struct sway_cursor *cursor = seat->cursor; | ||
666 | double sx, sy; | ||
667 | node_at_coords(seat, seat->touch_x, seat->touch_y, &surface, &sx, &sy); | ||
668 | |||
669 | if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { | ||
670 | if (seat_is_input_allowed(seat, surface)) { | ||
671 | cursor->simulating_pointer_from_touch = false; | ||
672 | seatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly); | ||
673 | } | ||
674 | } else if (!cursor->simulating_pointer_from_touch && | ||
675 | (!surface || seat_is_input_allowed(seat, surface))) { | ||
676 | // Fallback to cursor simulation. | ||
677 | // The pointer_touch_id state is needed, so drags are not aborted when over | ||
678 | // a surface supporting touch and multi touch events don't interfere. | ||
679 | cursor->simulating_pointer_from_touch = true; | ||
680 | cursor->pointer_touch_id = seat->touch_id; | ||
681 | double dx, dy; | ||
682 | dx = seat->touch_x - cursor->cursor->x; | ||
683 | dy = seat->touch_y - cursor->cursor->y; | ||
684 | pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy, | ||
685 | dx, dy); | ||
686 | dispatch_cursor_button(cursor, &event->touch->base, event->time_msec, | ||
687 | BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); | ||
688 | } | ||
689 | } | ||
690 | |||
641 | /*----------------------------------------\ | 691 | /*----------------------------------------\ |
642 | * Functions used by handle_pointer_axis / | 692 | * Functions used by handle_pointer_axis / |
643 | *--------------------------------------*/ | 693 | *--------------------------------------*/ |
644 | 694 | ||
645 | static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { | 695 | static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { |
646 | switch (event->orientation) { | 696 | switch (event->orientation) { |
647 | case WLR_AXIS_ORIENTATION_VERTICAL: | 697 | case WL_POINTER_AXIS_VERTICAL_SCROLL: |
648 | return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; | 698 | return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; |
649 | case WLR_AXIS_ORIENTATION_HORIZONTAL: | 699 | case WL_POINTER_AXIS_HORIZONTAL_SCROLL: |
650 | return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; | 700 | return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; |
651 | default: | 701 | default: |
652 | sway_log(SWAY_DEBUG, "Unknown axis orientation"); | 702 | sway_log(SWAY_DEBUG, "Unknown axis orientation"); |
@@ -655,9 +705,9 @@ static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { | |||
655 | } | 705 | } |
656 | 706 | ||
657 | static void handle_pointer_axis(struct sway_seat *seat, | 707 | static void handle_pointer_axis(struct sway_seat *seat, |
658 | struct wlr_event_pointer_axis *event) { | 708 | struct wlr_pointer_axis_event *event) { |
659 | struct sway_input_device *input_device = | 709 | struct sway_input_device *input_device = |
660 | event->device ? event->device->data : NULL; | 710 | event->pointer ? event->pointer->base.data : NULL; |
661 | struct input_config *ic = | 711 | struct input_config *ic = |
662 | input_device ? input_device_get_config(input_device) : NULL; | 712 | input_device ? input_device_get_config(input_device) : NULL; |
663 | struct sway_cursor *cursor = seat->cursor; | 713 | struct sway_cursor *cursor = seat->cursor; |
@@ -703,6 +753,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
703 | 753 | ||
704 | // Scrolling on a tabbed or stacked title bar (handled as press event) | 754 | // Scrolling on a tabbed or stacked title bar (handled as press event) |
705 | if (!handled && (on_titlebar || on_titlebar_border)) { | 755 | if (!handled && (on_titlebar || on_titlebar_border)) { |
756 | struct sway_node *new_focus; | ||
706 | enum sway_container_layout layout = container_parent_layout(cont); | 757 | enum sway_container_layout layout = container_parent_layout(cont); |
707 | if (layout == L_TABBED || layout == L_STACKED) { | 758 | if (layout == L_TABBED || layout == L_STACKED) { |
708 | struct sway_node *tabcontainer = node_get_parent(node); | 759 | struct sway_node *tabcontainer = node_get_parent(node); |
@@ -710,7 +761,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
710 | seat_get_active_tiling_child(seat, tabcontainer); | 761 | seat_get_active_tiling_child(seat, tabcontainer); |
711 | list_t *siblings = container_get_siblings(cont); | 762 | list_t *siblings = container_get_siblings(cont); |
712 | int desired = list_find(siblings, active->sway_container) + | 763 | int desired = list_find(siblings, active->sway_container) + |
713 | round(scroll_factor * event->delta_discrete); | 764 | roundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); |
714 | if (desired < 0) { | 765 | if (desired < 0) { |
715 | desired = 0; | 766 | desired = 0; |
716 | } else if (desired >= siblings->length) { | 767 | } else if (desired >= siblings->length) { |
@@ -719,14 +770,16 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
719 | 770 | ||
720 | struct sway_container *new_sibling_con = siblings->items[desired]; | 771 | struct sway_container *new_sibling_con = siblings->items[desired]; |
721 | struct sway_node *new_sibling = &new_sibling_con->node; | 772 | struct sway_node *new_sibling = &new_sibling_con->node; |
722 | struct sway_node *new_focus = | ||
723 | seat_get_focus_inactive(seat, new_sibling); | ||
724 | // Use the focused child of the tabbed/stacked container, not the | 773 | // Use the focused child of the tabbed/stacked container, not the |
725 | // container the user scrolled on. | 774 | // container the user scrolled on. |
726 | seat_set_focus(seat, new_focus); | 775 | new_focus = seat_get_focus_inactive(seat, new_sibling); |
727 | transaction_commit_dirty(); | 776 | } else { |
728 | handled = true; | 777 | new_focus = seat_get_focus_inactive(seat, &cont->node); |
729 | } | 778 | } |
779 | |||
780 | seat_set_focus(seat, new_focus); | ||
781 | transaction_commit_dirty(); | ||
782 | handled = true; | ||
730 | } | 783 | } |
731 | 784 | ||
732 | // Handle mouse bindings - x11 mouse buttons 4-7 - release event | 785 | // Handle mouse bindings - x11 mouse buttons 4-7 - release event |
@@ -742,8 +795,307 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
742 | 795 | ||
743 | if (!handled) { | 796 | if (!handled) { |
744 | wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, | 797 | wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, |
745 | event->orientation, scroll_factor * event->delta, | 798 | event->orientation, scroll_factor * event->delta, |
746 | round(scroll_factor * event->delta_discrete), event->source); | 799 | roundf(scroll_factor * event->delta_discrete), event->source, |
800 | event->relative_direction); | ||
801 | } | ||
802 | } | ||
803 | |||
804 | /*------------------------------------\ | ||
805 | * Functions used by gesture support / | ||
806 | *----------------------------------*/ | ||
807 | |||
808 | /** | ||
809 | * Check gesture binding for a specific gesture type and finger count. | ||
810 | * Returns true if binding is present, false otherwise | ||
811 | */ | ||
812 | static bool gesture_binding_check(list_t *bindings, enum gesture_type type, | ||
813 | uint8_t fingers, struct sway_input_device *device) { | ||
814 | char *input = | ||
815 | device ? input_device_get_identifier(device->wlr_device) : strdup("*"); | ||
816 | |||
817 | for (int i = 0; i < bindings->length; ++i) { | ||
818 | struct sway_gesture_binding *binding = bindings->items[i]; | ||
819 | |||
820 | // Check type and finger count | ||
821 | if (!gesture_check(&binding->gesture, type, fingers)) { | ||
822 | continue; | ||
823 | } | ||
824 | |||
825 | // Check that input matches | ||
826 | if (strcmp(binding->input, "*") != 0 && | ||
827 | strcmp(binding->input, input) != 0) { | ||
828 | continue; | ||
829 | } | ||
830 | |||
831 | free(input); | ||
832 | |||
833 | return true; | ||
834 | } | ||
835 | |||
836 | free(input); | ||
837 | |||
838 | return false; | ||
839 | } | ||
840 | |||
841 | /** | ||
842 | * Return the gesture binding which matches gesture type, finger count | ||
843 | * and direction, otherwise return null. | ||
844 | */ | ||
845 | static struct sway_gesture_binding* gesture_binding_match( | ||
846 | list_t *bindings, struct gesture *gesture, const char *input) { | ||
847 | struct sway_gesture_binding *current = NULL; | ||
848 | |||
849 | // Find best matching binding | ||
850 | for (int i = 0; i < bindings->length; ++i) { | ||
851 | struct sway_gesture_binding *binding = bindings->items[i]; | ||
852 | bool exact = binding->flags & BINDING_EXACT; | ||
853 | |||
854 | // Check gesture matching | ||
855 | if (!gesture_match(&binding->gesture, gesture, exact)) { | ||
856 | continue; | ||
857 | } | ||
858 | |||
859 | // Check input matching | ||
860 | if (strcmp(binding->input, "*") != 0 && | ||
861 | strcmp(binding->input, input) != 0) { | ||
862 | continue; | ||
863 | } | ||
864 | |||
865 | // If we already have a match ... | ||
866 | if (current) { | ||
867 | // ... check if input matching is equivalent | ||
868 | if (strcmp(current->input, binding->input) == 0) { | ||
869 | |||
870 | // ... - do not override an exact binding | ||
871 | if (!exact && current->flags & BINDING_EXACT) { | ||
872 | continue; | ||
873 | } | ||
874 | |||
875 | // ... - and ensure direction matching is better or equal | ||
876 | if (gesture_compare(¤t->gesture, &binding->gesture) > 0) { | ||
877 | continue; | ||
878 | } | ||
879 | } else if (strcmp(binding->input, "*") == 0) { | ||
880 | // ... do not accept worse input match | ||
881 | continue; | ||
882 | } | ||
883 | } | ||
884 | |||
885 | // Accept newer or better match | ||
886 | current = binding; | ||
887 | |||
888 | // If exact binding and input is found, quit search | ||
889 | if (strcmp(current->input, input) == 0 && | ||
890 | gesture_compare(¤t->gesture, gesture) == 0) { | ||
891 | break; | ||
892 | } | ||
893 | } // for all gesture bindings | ||
894 | |||
895 | return current; | ||
896 | } | ||
897 | |||
898 | // Wrapper around gesture_tracker_end to use tracker with sway bindings | ||
899 | static struct sway_gesture_binding* gesture_tracker_end_and_match( | ||
900 | struct gesture_tracker *tracker, struct sway_input_device* device) { | ||
901 | // Determine name of input that received gesture | ||
902 | char *input = device | ||
903 | ? input_device_get_identifier(device->wlr_device) | ||
904 | : strdup("*"); | ||
905 | |||
906 | // Match tracking result to binding | ||
907 | struct gesture *gesture = gesture_tracker_end(tracker); | ||
908 | struct sway_gesture_binding *binding = gesture_binding_match( | ||
909 | config->current_mode->gesture_bindings, gesture, input); | ||
910 | free(gesture); | ||
911 | free(input); | ||
912 | |||
913 | return binding; | ||
914 | } | ||
915 | |||
916 | // Small wrapper around seat_execute_command to work on gesture bindings | ||
917 | static void gesture_binding_execute(struct sway_seat *seat, | ||
918 | struct sway_gesture_binding *binding) { | ||
919 | struct sway_binding *dummy_binding = | ||
920 | calloc(1, sizeof(struct sway_binding)); | ||
921 | dummy_binding->type = BINDING_GESTURE; | ||
922 | dummy_binding->command = binding->command; | ||
923 | |||
924 | char *description = gesture_to_string(&binding->gesture); | ||
925 | sway_log(SWAY_DEBUG, "executing gesture binding: %s", description); | ||
926 | free(description); | ||
927 | |||
928 | seat_execute_command(seat, dummy_binding); | ||
929 | |||
930 | free(dummy_binding); | ||
931 | } | ||
932 | |||
933 | static void handle_hold_begin(struct sway_seat *seat, | ||
934 | struct wlr_pointer_hold_begin_event *event) { | ||
935 | // Start tracking gesture if there is a matching binding ... | ||
936 | struct sway_input_device *device = | ||
937 | event->pointer ? event->pointer->base.data : NULL; | ||
938 | list_t *bindings = config->current_mode->gesture_bindings; | ||
939 | if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) { | ||
940 | struct seatop_default_event *seatop = seat->seatop_data; | ||
941 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers); | ||
942 | } else { | ||
943 | // ... otherwise forward to client | ||
944 | struct sway_cursor *cursor = seat->cursor; | ||
945 | wlr_pointer_gestures_v1_send_hold_begin( | ||
946 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
947 | event->time_msec, event->fingers); | ||
948 | } | ||
949 | } | ||
950 | |||
951 | static void handle_hold_end(struct sway_seat *seat, | ||
952 | struct wlr_pointer_hold_end_event *event) { | ||
953 | // Ensure that gesture is being tracked and was not cancelled | ||
954 | struct seatop_default_event *seatop = seat->seatop_data; | ||
955 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) { | ||
956 | struct sway_cursor *cursor = seat->cursor; | ||
957 | wlr_pointer_gestures_v1_send_hold_end( | ||
958 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
959 | event->time_msec, event->cancelled); | ||
960 | return; | ||
961 | } | ||
962 | if (event->cancelled) { | ||
963 | gesture_tracker_cancel(&seatop->gestures); | ||
964 | return; | ||
965 | } | ||
966 | |||
967 | // End gesture tracking and execute matched binding | ||
968 | struct sway_input_device *device = | ||
969 | event->pointer ? event->pointer->base.data : NULL; | ||
970 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
971 | &seatop->gestures, device); | ||
972 | |||
973 | if (binding) { | ||
974 | gesture_binding_execute(seat, binding); | ||
975 | } | ||
976 | } | ||
977 | |||
978 | static void handle_pinch_begin(struct sway_seat *seat, | ||
979 | struct wlr_pointer_pinch_begin_event *event) { | ||
980 | // Start tracking gesture if there is a matching binding ... | ||
981 | struct sway_input_device *device = | ||
982 | event->pointer ? event->pointer->base.data : NULL; | ||
983 | list_t *bindings = config->current_mode->gesture_bindings; | ||
984 | if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) { | ||
985 | struct seatop_default_event *seatop = seat->seatop_data; | ||
986 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers); | ||
987 | } else { | ||
988 | // ... otherwise forward to client | ||
989 | struct sway_cursor *cursor = seat->cursor; | ||
990 | wlr_pointer_gestures_v1_send_pinch_begin( | ||
991 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
992 | event->time_msec, event->fingers); | ||
993 | } | ||
994 | } | ||
995 | |||
996 | static void handle_pinch_update(struct sway_seat *seat, | ||
997 | struct wlr_pointer_pinch_update_event *event) { | ||
998 | // Update any ongoing tracking ... | ||
999 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1000 | if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { | ||
1001 | gesture_tracker_update(&seatop->gestures, event->dx, event->dy, | ||
1002 | event->scale, event->rotation); | ||
1003 | } else { | ||
1004 | // ... otherwise forward to client | ||
1005 | struct sway_cursor *cursor = seat->cursor; | ||
1006 | wlr_pointer_gestures_v1_send_pinch_update( | ||
1007 | server.input->pointer_gestures, | ||
1008 | cursor->seat->wlr_seat, | ||
1009 | event->time_msec, event->dx, event->dy, | ||
1010 | event->scale, event->rotation); | ||
1011 | } | ||
1012 | } | ||
1013 | |||
1014 | static void handle_pinch_end(struct sway_seat *seat, | ||
1015 | struct wlr_pointer_pinch_end_event *event) { | ||
1016 | // Ensure that gesture is being tracked and was not cancelled | ||
1017 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1018 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { | ||
1019 | struct sway_cursor *cursor = seat->cursor; | ||
1020 | wlr_pointer_gestures_v1_send_pinch_end( | ||
1021 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
1022 | event->time_msec, event->cancelled); | ||
1023 | return; | ||
1024 | } | ||
1025 | if (event->cancelled) { | ||
1026 | gesture_tracker_cancel(&seatop->gestures); | ||
1027 | return; | ||
1028 | } | ||
1029 | |||
1030 | // End gesture tracking and execute matched binding | ||
1031 | struct sway_input_device *device = | ||
1032 | event->pointer ? event->pointer->base.data : NULL; | ||
1033 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
1034 | &seatop->gestures, device); | ||
1035 | |||
1036 | if (binding) { | ||
1037 | gesture_binding_execute(seat, binding); | ||
1038 | } | ||
1039 | } | ||
1040 | |||
1041 | static void handle_swipe_begin(struct sway_seat *seat, | ||
1042 | struct wlr_pointer_swipe_begin_event *event) { | ||
1043 | // Start tracking gesture if there is a matching binding ... | ||
1044 | struct sway_input_device *device = | ||
1045 | event->pointer ? event->pointer->base.data : NULL; | ||
1046 | list_t *bindings = config->current_mode->gesture_bindings; | ||
1047 | if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) { | ||
1048 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1049 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers); | ||
1050 | } else { | ||
1051 | // ... otherwise forward to client | ||
1052 | struct sway_cursor *cursor = seat->cursor; | ||
1053 | wlr_pointer_gestures_v1_send_swipe_begin( | ||
1054 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
1055 | event->time_msec, event->fingers); | ||
1056 | } | ||
1057 | } | ||
1058 | |||
1059 | static void handle_swipe_update(struct sway_seat *seat, | ||
1060 | struct wlr_pointer_swipe_update_event *event) { | ||
1061 | |||
1062 | // Update any ongoing tracking ... | ||
1063 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1064 | if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { | ||
1065 | gesture_tracker_update(&seatop->gestures, | ||
1066 | event->dx, event->dy, NAN, NAN); | ||
1067 | } else { | ||
1068 | // ... otherwise forward to client | ||
1069 | struct sway_cursor *cursor = seat->cursor; | ||
1070 | wlr_pointer_gestures_v1_send_swipe_update( | ||
1071 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
1072 | event->time_msec, event->dx, event->dy); | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | static void handle_swipe_end(struct sway_seat *seat, | ||
1077 | struct wlr_pointer_swipe_end_event *event) { | ||
1078 | // Ensure gesture is being tracked and was not cancelled | ||
1079 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1080 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { | ||
1081 | struct sway_cursor *cursor = seat->cursor; | ||
1082 | wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures, | ||
1083 | cursor->seat->wlr_seat, event->time_msec, event->cancelled); | ||
1084 | return; | ||
1085 | } | ||
1086 | if (event->cancelled) { | ||
1087 | gesture_tracker_cancel(&seatop->gestures); | ||
1088 | return; | ||
1089 | } | ||
1090 | |||
1091 | // End gesture tracking and execute matched binding | ||
1092 | struct sway_input_device *device = | ||
1093 | event->pointer ? event->pointer->base.data : NULL; | ||
1094 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
1095 | &seatop->gestures, device); | ||
1096 | |||
1097 | if (binding) { | ||
1098 | gesture_binding_execute(seat, binding); | ||
747 | } | 1099 | } |
748 | } | 1100 | } |
749 | 1101 | ||
@@ -776,6 +1128,15 @@ static const struct sway_seatop_impl seatop_impl = { | |||
776 | .pointer_axis = handle_pointer_axis, | 1128 | .pointer_axis = handle_pointer_axis, |
777 | .tablet_tool_tip = handle_tablet_tool_tip, | 1129 | .tablet_tool_tip = handle_tablet_tool_tip, |
778 | .tablet_tool_motion = handle_tablet_tool_motion, | 1130 | .tablet_tool_motion = handle_tablet_tool_motion, |
1131 | .hold_begin = handle_hold_begin, | ||
1132 | .hold_end = handle_hold_end, | ||
1133 | .pinch_begin = handle_pinch_begin, | ||
1134 | .pinch_update = handle_pinch_update, | ||
1135 | .pinch_end = handle_pinch_end, | ||
1136 | .swipe_begin = handle_swipe_begin, | ||
1137 | .swipe_update = handle_swipe_update, | ||
1138 | .swipe_end = handle_swipe_end, | ||
1139 | .touch_down = handle_touch_down, | ||
779 | .rebase = handle_rebase, | 1140 | .rebase = handle_rebase, |
780 | .allow_set_cursor = true, | 1141 | .allow_set_cursor = true, |
781 | }; | 1142 | }; |
@@ -786,8 +1147,8 @@ void seatop_begin_default(struct sway_seat *seat) { | |||
786 | struct seatop_default_event *e = | 1147 | struct seatop_default_event *e = |
787 | calloc(1, sizeof(struct seatop_default_event)); | 1148 | calloc(1, sizeof(struct seatop_default_event)); |
788 | sway_assert(e, "Unable to allocate seatop_default_event"); | 1149 | sway_assert(e, "Unable to allocate seatop_default_event"); |
1150 | |||
789 | seat->seatop_impl = &seatop_impl; | 1151 | seat->seatop_impl = &seatop_impl; |
790 | seat->seatop_data = e; | 1152 | seat->seatop_data = e; |
791 | |||
792 | seatop_rebase(seat, 0); | 1153 | seatop_rebase(seat, 0); |
793 | } | 1154 | } |
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 844cf5ab..340e334b 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c | |||
@@ -1,23 +1,138 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <wlr/types/wlr_cursor.h> | 2 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/types/wlr_tablet_v2.h> | 3 | #include <wlr/types/wlr_tablet_v2.h> |
4 | #include <wlr/types/wlr_touch.h> | ||
5 | #include "sway/input/cursor.h" | 5 | #include "sway/input/cursor.h" |
6 | #include "sway/input/seat.h" | 6 | #include "sway/input/seat.h" |
7 | #include "sway/tree/view.h" | 7 | #include "sway/tree/view.h" |
8 | #include "sway/desktop/transaction.h" | 8 | #include "sway/desktop/transaction.h" |
9 | #include "log.h" | 9 | #include "log.h" |
10 | 10 | ||
11 | struct seatop_touch_point_event { | ||
12 | double ref_lx, ref_ly; // touch's x/y at start of op | ||
13 | double ref_con_lx, ref_con_ly; // container's x/y at start of op | ||
14 | int32_t touch_id; | ||
15 | struct wl_list link; | ||
16 | }; | ||
17 | |||
11 | struct seatop_down_event { | 18 | struct seatop_down_event { |
12 | struct sway_container *con; | 19 | struct sway_container *con; |
20 | struct sway_seat *seat; | ||
21 | struct wl_listener surface_destroy; | ||
22 | struct wlr_surface *surface; | ||
13 | double ref_lx, ref_ly; // cursor's x/y at start of op | 23 | double ref_lx, ref_ly; // cursor's x/y at start of op |
14 | double ref_con_lx, ref_con_ly; // container's x/y at start of op | 24 | double ref_con_lx, ref_con_ly; // container's x/y at start of op |
25 | struct wl_list point_events; // seatop_touch_point_event::link | ||
15 | }; | 26 | }; |
16 | 27 | ||
28 | static void handle_touch_motion(struct sway_seat *seat, | ||
29 | struct wlr_touch_motion_event *event, double lx, double ly) { | ||
30 | struct seatop_down_event *e = seat->seatop_data; | ||
31 | |||
32 | struct seatop_touch_point_event *point_event; | ||
33 | bool found = false; | ||
34 | wl_list_for_each(point_event, &e->point_events, link) { | ||
35 | if (point_event->touch_id == event->touch_id) { | ||
36 | found = true; | ||
37 | break; | ||
38 | } | ||
39 | } | ||
40 | if (!found) { | ||
41 | return; // Probably not a point_event from this seatop_down | ||
42 | } | ||
43 | |||
44 | double moved_x = lx - point_event->ref_lx; | ||
45 | double moved_y = ly - point_event->ref_ly; | ||
46 | double sx = point_event->ref_con_lx + moved_x; | ||
47 | double sy = point_event->ref_con_ly + moved_y; | ||
48 | |||
49 | wlr_seat_touch_notify_motion(seat->wlr_seat, event->time_msec, | ||
50 | event->touch_id, sx, sy); | ||
51 | } | ||
52 | |||
53 | static void handle_touch_up(struct sway_seat *seat, | ||
54 | struct wlr_touch_up_event *event) { | ||
55 | struct seatop_down_event *e = seat->seatop_data; | ||
56 | struct seatop_touch_point_event *point_event, *tmp; | ||
57 | |||
58 | wl_list_for_each_safe(point_event, tmp, &e->point_events, link) { | ||
59 | if (point_event->touch_id == event->touch_id) { | ||
60 | wl_list_remove(&point_event->link); | ||
61 | free(point_event); | ||
62 | break; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | wlr_seat_touch_notify_up(seat->wlr_seat, event->time_msec, event->touch_id); | ||
67 | |||
68 | if (wl_list_empty(&e->point_events)) { | ||
69 | seatop_begin_default(seat); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | static void handle_touch_down(struct sway_seat *seat, | ||
74 | struct wlr_touch_down_event *event, double lx, double ly) { | ||
75 | struct seatop_down_event *e = seat->seatop_data; | ||
76 | double sx, sy; | ||
77 | struct wlr_surface *surface = NULL; | ||
78 | struct sway_node *focused_node = node_at_coords(seat, seat->touch_x, | ||
79 | seat->touch_y, &surface, &sx, &sy); | ||
80 | |||
81 | if (!surface || surface != e->surface) { // Must start from the initial surface | ||
82 | return; | ||
83 | } | ||
84 | |||
85 | struct seatop_touch_point_event *point_event = | ||
86 | calloc(1, sizeof(struct seatop_touch_point_event)); | ||
87 | if (!sway_assert(point_event, "Unable to allocate point_event")) { | ||
88 | return; | ||
89 | } | ||
90 | point_event->touch_id = event->touch_id; | ||
91 | point_event->ref_lx = lx; | ||
92 | point_event->ref_ly = ly; | ||
93 | point_event->ref_con_lx = sx; | ||
94 | point_event->ref_con_ly = sy; | ||
95 | |||
96 | wl_list_insert(&e->point_events, &point_event->link); | ||
97 | |||
98 | wlr_seat_touch_notify_down(seat->wlr_seat, surface, event->time_msec, | ||
99 | event->touch_id, sx, sy); | ||
100 | |||
101 | if (focused_node) { | ||
102 | seat_set_focus(seat, focused_node); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | static void handle_touch_cancel(struct sway_seat *seat, | ||
107 | struct wlr_touch_cancel_event *event) { | ||
108 | struct seatop_down_event *e = seat->seatop_data; | ||
109 | struct seatop_touch_point_event *point_event, *tmp; | ||
110 | |||
111 | wl_list_for_each_safe(point_event, tmp, &e->point_events, link) { | ||
112 | if (point_event->touch_id == event->touch_id) { | ||
113 | wl_list_remove(&point_event->link); | ||
114 | free(point_event); | ||
115 | break; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | if (e->surface) { | ||
120 | struct wl_client *client = wl_resource_get_client(e->surface->resource); | ||
121 | struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat->wlr_seat, client); | ||
122 | if (seat_client != NULL) { | ||
123 | wlr_seat_touch_notify_cancel(seat->wlr_seat, seat_client); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | if (wl_list_empty(&e->point_events)) { | ||
128 | seatop_begin_default(seat); | ||
129 | } | ||
130 | } | ||
131 | |||
17 | static void handle_pointer_axis(struct sway_seat *seat, | 132 | static void handle_pointer_axis(struct sway_seat *seat, |
18 | struct wlr_event_pointer_axis *event) { | 133 | struct wlr_pointer_axis_event *event) { |
19 | struct sway_input_device *input_device = | 134 | struct sway_input_device *input_device = |
20 | event->device ? event->device->data : NULL; | 135 | event->pointer ? event->pointer->base.data : NULL; |
21 | struct input_config *ic = | 136 | struct input_config *ic = |
22 | input_device ? input_device_get_config(input_device) : NULL; | 137 | input_device ? input_device_get_config(input_device) : NULL; |
23 | float scroll_factor = | 138 | float scroll_factor = |
@@ -25,12 +140,13 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
25 | 140 | ||
26 | wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, | 141 | wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, |
27 | event->orientation, scroll_factor * event->delta, | 142 | event->orientation, scroll_factor * event->delta, |
28 | round(scroll_factor * event->delta_discrete), event->source); | 143 | roundf(scroll_factor * event->delta_discrete), event->source, |
144 | event->relative_direction); | ||
29 | } | 145 | } |
30 | 146 | ||
31 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 147 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
32 | struct wlr_input_device *device, uint32_t button, | 148 | struct wlr_input_device *device, uint32_t button, |
33 | enum wlr_button_state state) { | 149 | enum wl_pointer_button_state state) { |
34 | seat_pointer_notify_button(seat, time_msec, button, state); | 150 | seat_pointer_notify_button(seat, time_msec, button, state); |
35 | 151 | ||
36 | if (seat->cursor->pressed_button_count == 0) { | 152 | if (seat->cursor->pressed_button_count == 0) { |
@@ -40,8 +156,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
40 | 156 | ||
41 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | 157 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { |
42 | struct seatop_down_event *e = seat->seatop_data; | 158 | struct seatop_down_event *e = seat->seatop_data; |
43 | struct sway_container *con = e->con; | 159 | if (seat_is_input_allowed(seat, e->surface)) { |
44 | if (seat_is_input_allowed(seat, con->view->surface)) { | ||
45 | double moved_x = seat->cursor->cursor->x - e->ref_lx; | 160 | double moved_x = seat->cursor->cursor->x - e->ref_lx; |
46 | double moved_y = seat->cursor->cursor->y - e->ref_ly; | 161 | double moved_y = seat->cursor->cursor->y - e->ref_ly; |
47 | double sx = e->ref_con_lx + moved_x; | 162 | double sx = e->ref_con_lx + moved_x; |
@@ -62,8 +177,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
62 | static void handle_tablet_tool_motion(struct sway_seat *seat, | 177 | static void handle_tablet_tool_motion(struct sway_seat *seat, |
63 | struct sway_tablet_tool *tool, uint32_t time_msec) { | 178 | struct sway_tablet_tool *tool, uint32_t time_msec) { |
64 | struct seatop_down_event *e = seat->seatop_data; | 179 | struct seatop_down_event *e = seat->seatop_data; |
65 | struct sway_container *con = e->con; | 180 | if (seat_is_input_allowed(seat, e->surface)) { |
66 | if (seat_is_input_allowed(seat, con->view->surface)) { | ||
67 | double moved_x = seat->cursor->cursor->x - e->ref_lx; | 181 | double moved_x = seat->cursor->cursor->x - e->ref_lx; |
68 | double moved_y = seat->cursor->cursor->y - e->ref_ly; | 182 | double moved_y = seat->cursor->cursor->y - e->ref_ly; |
69 | double sx = e->ref_con_lx + moved_x; | 183 | double sx = e->ref_con_lx + moved_x; |
@@ -72,6 +186,14 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, | |||
72 | } | 186 | } |
73 | } | 187 | } |
74 | 188 | ||
189 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
190 | struct seatop_down_event *e = | ||
191 | wl_container_of(listener, e, surface_destroy); | ||
192 | if (e) { | ||
193 | seatop_begin_default(e->seat); | ||
194 | } | ||
195 | } | ||
196 | |||
75 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 197 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
76 | struct seatop_down_event *e = seat->seatop_data; | 198 | struct seatop_down_event *e = seat->seatop_data; |
77 | if (e->con == con) { | 199 | if (e->con == con) { |
@@ -79,34 +201,63 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | |||
79 | } | 201 | } |
80 | } | 202 | } |
81 | 203 | ||
204 | static void handle_end(struct sway_seat *seat) { | ||
205 | struct seatop_down_event *e = seat->seatop_data; | ||
206 | wl_list_remove(&e->surface_destroy.link); | ||
207 | } | ||
208 | |||
82 | static const struct sway_seatop_impl seatop_impl = { | 209 | static const struct sway_seatop_impl seatop_impl = { |
83 | .button = handle_button, | 210 | .button = handle_button, |
84 | .pointer_motion = handle_pointer_motion, | 211 | .pointer_motion = handle_pointer_motion, |
85 | .pointer_axis = handle_pointer_axis, | 212 | .pointer_axis = handle_pointer_axis, |
86 | .tablet_tool_tip = handle_tablet_tool_tip, | 213 | .tablet_tool_tip = handle_tablet_tool_tip, |
87 | .tablet_tool_motion = handle_tablet_tool_motion, | 214 | .tablet_tool_motion = handle_tablet_tool_motion, |
215 | .touch_motion = handle_touch_motion, | ||
216 | .touch_up = handle_touch_up, | ||
217 | .touch_down = handle_touch_down, | ||
218 | .touch_cancel = handle_touch_cancel, | ||
88 | .unref = handle_unref, | 219 | .unref = handle_unref, |
220 | .end = handle_end, | ||
89 | .allow_set_cursor = true, | 221 | .allow_set_cursor = true, |
90 | }; | 222 | }; |
91 | 223 | ||
92 | void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, | 224 | void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, |
93 | uint32_t time_msec, int sx, int sy) { | 225 | double sx, double sy) { |
226 | seatop_begin_down_on_surface(seat, con->view->surface, sx, sy); | ||
227 | struct seatop_down_event *e = seat->seatop_data; | ||
228 | e->con = con; | ||
229 | |||
230 | container_raise_floating(con); | ||
231 | transaction_commit_dirty(); | ||
232 | } | ||
233 | |||
234 | void seatop_begin_touch_down(struct sway_seat *seat, | ||
235 | struct wlr_surface *surface, struct wlr_touch_down_event *event, | ||
236 | double sx, double sy, double lx, double ly) { | ||
237 | seatop_begin_down_on_surface(seat, surface, sx, sy); | ||
238 | handle_touch_down(seat, event, lx, ly); | ||
239 | } | ||
240 | |||
241 | void seatop_begin_down_on_surface(struct sway_seat *seat, | ||
242 | struct wlr_surface *surface, double sx, double sy) { | ||
94 | seatop_end(seat); | 243 | seatop_end(seat); |
95 | 244 | ||
96 | struct seatop_down_event *e = | 245 | struct seatop_down_event *e = |
97 | calloc(1, sizeof(struct seatop_down_event)); | 246 | calloc(1, sizeof(struct seatop_down_event)); |
98 | if (!e) { | 247 | if (!sway_assert(e, "Unable to allocate e")) { |
99 | return; | 248 | return; |
100 | } | 249 | } |
101 | e->con = con; | 250 | e->con = NULL; |
251 | e->seat = seat; | ||
252 | e->surface = surface; | ||
253 | wl_signal_add(&e->surface->events.destroy, &e->surface_destroy); | ||
254 | e->surface_destroy.notify = handle_destroy; | ||
102 | e->ref_lx = seat->cursor->cursor->x; | 255 | e->ref_lx = seat->cursor->cursor->x; |
103 | e->ref_ly = seat->cursor->cursor->y; | 256 | e->ref_ly = seat->cursor->cursor->y; |
104 | e->ref_con_lx = sx; | 257 | e->ref_con_lx = sx; |
105 | e->ref_con_ly = sy; | 258 | e->ref_con_ly = sy; |
259 | wl_list_init(&e->point_events); | ||
106 | 260 | ||
107 | seat->seatop_impl = &seatop_impl; | 261 | seat->seatop_impl = &seatop_impl; |
108 | seat->seatop_data = e; | 262 | seat->seatop_data = e; |
109 | |||
110 | container_raise_floating(con); | ||
111 | transaction_commit_dirty(); | ||
112 | } | 263 | } |
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index ddcd4c53..83668d88 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c | |||
@@ -1,6 +1,4 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <wlr/types/wlr_cursor.h> | 1 | #include <wlr/types/wlr_cursor.h> |
3 | #include "sway/desktop.h" | ||
4 | #include "sway/desktop/transaction.h" | 2 | #include "sway/desktop/transaction.h" |
5 | #include "sway/input/cursor.h" | 3 | #include "sway/input/cursor.h" |
6 | #include "sway/input/seat.h" | 4 | #include "sway/input/seat.h" |
@@ -23,7 +21,7 @@ static void finalize_move(struct sway_seat *seat) { | |||
23 | 21 | ||
24 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 22 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
25 | struct wlr_input_device *device, uint32_t button, | 23 | struct wlr_input_device *device, uint32_t button, |
26 | enum wlr_button_state state) { | 24 | enum wl_pointer_button_state state) { |
27 | if (seat->cursor->pressed_button_count == 0) { | 25 | if (seat->cursor->pressed_button_count == 0) { |
28 | finalize_move(seat); | 26 | finalize_move(seat); |
29 | } | 27 | } |
@@ -39,9 +37,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
39 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | 37 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { |
40 | struct seatop_move_floating_event *e = seat->seatop_data; | 38 | struct seatop_move_floating_event *e = seat->seatop_data; |
41 | struct wlr_cursor *cursor = seat->cursor->cursor; | 39 | struct wlr_cursor *cursor = seat->cursor->cursor; |
42 | desktop_damage_whole_container(e->con); | ||
43 | container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); | 40 | container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); |
44 | desktop_damage_whole_container(e->con); | ||
45 | transaction_commit_dirty(); | 41 | transaction_commit_dirty(); |
46 | } | 42 | } |
47 | 43 | ||
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 223c6c08..c525b77a 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c | |||
@@ -1,8 +1,6 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <wlr/types/wlr_cursor.h> | 2 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/util/edges.h> | 3 | #include <wlr/util/edges.h> |
5 | #include "sway/desktop.h" | ||
6 | #include "sway/desktop/transaction.h" | 4 | #include "sway/desktop/transaction.h" |
7 | #include "sway/input/cursor.h" | 5 | #include "sway/input/cursor.h" |
8 | #include "sway/input/seat.h" | 6 | #include "sway/input/seat.h" |
@@ -24,29 +22,17 @@ struct seatop_move_tiling_event { | |||
24 | struct sway_container *con; | 22 | struct sway_container *con; |
25 | struct sway_node *target_node; | 23 | struct sway_node *target_node; |
26 | enum wlr_edges target_edge; | 24 | enum wlr_edges target_edge; |
27 | struct wlr_box drop_box; | ||
28 | double ref_lx, ref_ly; // cursor's x/y at start of op | 25 | double ref_lx, ref_ly; // cursor's x/y at start of op |
29 | bool threshold_reached; | 26 | bool threshold_reached; |
30 | bool split_target; | 27 | bool split_target; |
31 | bool insert_after_target; | 28 | bool insert_after_target; |
29 | struct wlr_scene_rect *indicator_rect; | ||
32 | }; | 30 | }; |
33 | 31 | ||
34 | static void handle_render(struct sway_seat *seat, | 32 | static void handle_end(struct sway_seat *seat) { |
35 | struct sway_output *output, pixman_region32_t *damage) { | ||
36 | struct seatop_move_tiling_event *e = seat->seatop_data; | 33 | struct seatop_move_tiling_event *e = seat->seatop_data; |
37 | if (!e->threshold_reached) { | 34 | wlr_scene_node_destroy(&e->indicator_rect->node); |
38 | return; | 35 | e->indicator_rect = NULL; |
39 | } | ||
40 | if (e->target_node && node_get_output(e->target_node) == output) { | ||
41 | float color[4]; | ||
42 | memcpy(&color, config->border_colors.focused.indicator, | ||
43 | sizeof(float) * 4); | ||
44 | premultiply_alpha(color, 0.5); | ||
45 | struct wlr_box box; | ||
46 | memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); | ||
47 | scale_box(&box, output->wlr_output->scale); | ||
48 | render_rect(output, damage, &box, color); | ||
49 | } | ||
50 | } | 36 | } |
51 | 37 | ||
52 | static void handle_motion_prethreshold(struct sway_seat *seat) { | 38 | static void handle_motion_prethreshold(struct sway_seat *seat) { |
@@ -67,6 +53,7 @@ static void handle_motion_prethreshold(struct sway_seat *seat) { | |||
67 | 53 | ||
68 | // If the threshold has been exceeded, start the actual drag | 54 | // If the threshold has been exceeded, start the actual drag |
69 | if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { | 55 | if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { |
56 | wlr_scene_node_set_enabled(&e->indicator_rect->node, true); | ||
70 | e->threshold_reached = true; | 57 | e->threshold_reached = true; |
71 | cursor_set_image(seat->cursor, "grab", NULL); | 58 | cursor_set_image(seat->cursor, "grab", NULL); |
72 | } | 59 | } |
@@ -165,6 +152,11 @@ static bool split_titlebar(struct sway_node *node, struct sway_container *avoid, | |||
165 | return false; | 152 | return false; |
166 | } | 153 | } |
167 | 154 | ||
155 | static void update_indicator(struct seatop_move_tiling_event *e, struct wlr_box *box) { | ||
156 | wlr_scene_node_set_position(&e->indicator_rect->node, box->x, box->y); | ||
157 | wlr_scene_rect_set_size(e->indicator_rect, box->width, box->height); | ||
158 | } | ||
159 | |||
168 | static void handle_motion_postthreshold(struct sway_seat *seat) { | 160 | static void handle_motion_postthreshold(struct sway_seat *seat) { |
169 | struct seatop_move_tiling_event *e = seat->seatop_data; | 161 | struct seatop_move_tiling_event *e = seat->seatop_data; |
170 | e->split_target = false; | 162 | e->split_target = false; |
@@ -173,8 +165,6 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
173 | struct sway_cursor *cursor = seat->cursor; | 165 | struct sway_cursor *cursor = seat->cursor; |
174 | struct sway_node *node = node_at_coords(seat, | 166 | struct sway_node *node = node_at_coords(seat, |
175 | cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); | 167 | cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); |
176 | // Damage the old location | ||
177 | desktop_damage_box(&e->drop_box); | ||
178 | 168 | ||
179 | if (!node) { | 169 | if (!node) { |
180 | // Eg. hovered over a layer surface such as swaybar | 170 | // Eg. hovered over a layer surface such as swaybar |
@@ -187,8 +177,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
187 | // Empty workspace | 177 | // Empty workspace |
188 | e->target_node = node; | 178 | e->target_node = node; |
189 | e->target_edge = WLR_EDGE_NONE; | 179 | e->target_edge = WLR_EDGE_NONE; |
190 | workspace_get_box(node->sway_workspace, &e->drop_box); | 180 | |
191 | desktop_damage_box(&e->drop_box); | 181 | struct wlr_box drop_box; |
182 | workspace_get_box(node->sway_workspace, &drop_box); | ||
183 | update_indicator(e, &drop_box); | ||
192 | return; | 184 | return; |
193 | } | 185 | } |
194 | 186 | ||
@@ -201,11 +193,18 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
201 | return; | 193 | return; |
202 | } | 194 | } |
203 | 195 | ||
196 | struct wlr_box drop_box = { | ||
197 | .x = con->pending.content_x, | ||
198 | .y = con->pending.content_y, | ||
199 | .width = con->pending.content_width, | ||
200 | .height = con->pending.content_height, | ||
201 | }; | ||
202 | |||
204 | // Check if the cursor is over a tilebar only if the destination | 203 | // Check if the cursor is over a tilebar only if the destination |
205 | // container is not a descendant of the source container. | 204 | // container is not a descendant of the source container. |
206 | if (!surface && !container_has_ancestor(con, e->con) && | 205 | if (!surface && !container_has_ancestor(con, e->con) && |
207 | split_titlebar(node, e->con, cursor->cursor, | 206 | split_titlebar(node, e->con, cursor->cursor, |
208 | &e->drop_box, &e->insert_after_target)) { | 207 | &drop_box, &e->insert_after_target)) { |
209 | // Don't allow dropping over the source container's titlebar | 208 | // Don't allow dropping over the source container's titlebar |
210 | // to give users a chance to cancel a drag operation. | 209 | // to give users a chance to cancel a drag operation. |
211 | if (con == e->con) { | 210 | if (con == e->con) { |
@@ -215,6 +214,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
215 | e->split_target = true; | 214 | e->split_target = true; |
216 | } | 215 | } |
217 | e->target_edge = WLR_EDGE_NONE; | 216 | e->target_edge = WLR_EDGE_NONE; |
217 | update_indicator(e, &drop_box); | ||
218 | return; | 218 | return; |
219 | } | 219 | } |
220 | 220 | ||
@@ -256,8 +256,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
256 | e->target_node = node_get_parent(e->target_node); | 256 | e->target_node = node_get_parent(e->target_node); |
257 | } | 257 | } |
258 | e->target_edge = edge; | 258 | e->target_edge = edge; |
259 | e->drop_box = box; | 259 | update_indicator(e, &box); |
260 | desktop_damage_box(&e->drop_box); | ||
261 | return; | 260 | return; |
262 | } | 261 | } |
263 | con = con->pending.parent; | 262 | con = con->pending.parent; |
@@ -299,12 +298,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
299 | } | 298 | } |
300 | 299 | ||
301 | e->target_node = node; | 300 | e->target_node = node; |
302 | e->drop_box.x = con->pending.content_x; | 301 | resize_box(&drop_box, e->target_edge, thickness); |
303 | e->drop_box.y = con->pending.content_y; | 302 | update_indicator(e, &drop_box); |
304 | e->drop_box.width = con->pending.content_width; | ||
305 | e->drop_box.height = con->pending.content_height; | ||
306 | resize_box(&e->drop_box, e->target_edge, thickness); | ||
307 | desktop_damage_box(&e->drop_box); | ||
308 | } | 303 | } |
309 | 304 | ||
310 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | 305 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { |
@@ -410,7 +405,7 @@ static void finalize_move(struct sway_seat *seat) { | |||
410 | 405 | ||
411 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 406 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
412 | struct wlr_input_device *device, uint32_t button, | 407 | struct wlr_input_device *device, uint32_t button, |
413 | enum wlr_button_state state) { | 408 | enum wl_pointer_button_state state) { |
414 | if (seat->cursor->pressed_button_count == 0) { | 409 | if (seat->cursor->pressed_button_count == 0) { |
415 | finalize_move(seat); | 410 | finalize_move(seat); |
416 | } | 411 | } |
@@ -439,7 +434,7 @@ static const struct sway_seatop_impl seatop_impl = { | |||
439 | .pointer_motion = handle_pointer_motion, | 434 | .pointer_motion = handle_pointer_motion, |
440 | .tablet_tool_tip = handle_tablet_tool_tip, | 435 | .tablet_tool_tip = handle_tablet_tool_tip, |
441 | .unref = handle_unref, | 436 | .unref = handle_unref, |
442 | .render = handle_render, | 437 | .end = handle_end, |
443 | }; | 438 | }; |
444 | 439 | ||
445 | void seatop_begin_move_tiling_threshold(struct sway_seat *seat, | 440 | void seatop_begin_move_tiling_threshold(struct sway_seat *seat, |
@@ -451,6 +446,20 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, | |||
451 | if (!e) { | 446 | if (!e) { |
452 | return; | 447 | return; |
453 | } | 448 | } |
449 | |||
450 | const float *indicator = config->border_colors.focused.indicator; | ||
451 | float color[4] = { | ||
452 | indicator[0] * .5, | ||
453 | indicator[1] * .5, | ||
454 | indicator[2] * .5, | ||
455 | indicator[3] * .5, | ||
456 | }; | ||
457 | e->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, color); | ||
458 | if (!e->indicator_rect) { | ||
459 | free(e); | ||
460 | return; | ||
461 | } | ||
462 | |||
454 | e->con = con; | 463 | e->con = con; |
455 | e->ref_lx = seat->cursor->cursor->x; | 464 | e->ref_lx = seat->cursor->cursor->x; |
456 | e->ref_ly = seat->cursor->cursor->y; | 465 | e->ref_ly = seat->cursor->cursor->y; |
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index 8400a4b3..bec86e33 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <wlr/types/wlr_cursor.h> | 2 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/types/wlr_xcursor_manager.h> | 3 | #include <wlr/types/wlr_xcursor_manager.h> |
@@ -21,7 +20,7 @@ struct seatop_resize_floating_event { | |||
21 | 20 | ||
22 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 21 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
23 | struct wlr_input_device *device, uint32_t button, | 22 | struct wlr_input_device *device, uint32_t button, |
24 | enum wlr_button_state state) { | 23 | enum wl_pointer_button_state state) { |
25 | struct seatop_resize_floating_event *e = seat->seatop_data; | 24 | struct seatop_resize_floating_event *e = seat->seatop_data; |
26 | struct sway_container *con = e->con; | 25 | struct sway_container *con = e->con; |
27 | 26 | ||
@@ -80,17 +79,25 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
80 | double height = e->ref_height + grow_height; | 79 | double height = e->ref_height + grow_height; |
81 | int min_width, max_width, min_height, max_height; | 80 | int min_width, max_width, min_height, max_height; |
82 | floating_calculate_constraints(&min_width, &max_width, | 81 | floating_calculate_constraints(&min_width, &max_width, |
83 | &min_height, &max_height); | 82 | &min_height, &max_height); |
84 | width = fmax(min_width + border_width, fmin(width, max_width)); | 83 | width = fmin(width, max_width - border_width); |
85 | height = fmax(min_height + border_height, fmin(height, max_height)); | 84 | width = fmax(width, min_width + border_width); |
85 | width = fmax(width, 1); | ||
86 | height = fmin(height, max_height - border_height); | ||
87 | height = fmax(height, min_height + border_height); | ||
88 | height = fmax(height, 1); | ||
86 | 89 | ||
87 | // Apply the view's min/max size | 90 | // Apply the view's min/max size |
88 | if (con->view) { | 91 | if (con->view) { |
89 | double view_min_width, view_max_width, view_min_height, view_max_height; | 92 | double view_min_width, view_max_width, view_min_height, view_max_height; |
90 | view_get_constraints(con->view, &view_min_width, &view_max_width, | 93 | view_get_constraints(con->view, &view_min_width, &view_max_width, |
91 | &view_min_height, &view_max_height); | 94 | &view_min_height, &view_max_height); |
92 | width = fmax(view_min_width + border_width, fmin(width, view_max_width)); | 95 | width = fmin(width, view_max_width - border_width); |
93 | height = fmax(view_min_height + border_height, fmin(height, view_max_height)); | 96 | width = fmax(width, view_min_width + border_width); |
97 | width = fmax(width, 1); | ||
98 | height = fmin(height, view_max_height - border_height); | ||
99 | height = fmax(height, view_min_height + border_height); | ||
100 | height = fmax(height, 1); | ||
94 | 101 | ||
95 | } | 102 | } |
96 | 103 | ||
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index 869d11b5..15fd333b 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <wlr/types/wlr_cursor.h> | 1 | #include <wlr/types/wlr_cursor.h> |
3 | #include <wlr/util/edges.h> | 2 | #include <wlr/util/edges.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -46,7 +45,7 @@ static struct sway_container *container_get_resize_sibling( | |||
46 | 45 | ||
47 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 46 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
48 | struct wlr_input_device *device, uint32_t button, | 47 | struct wlr_input_device *device, uint32_t button, |
49 | enum wlr_button_state state) { | 48 | enum wl_pointer_button_state state) { |
50 | struct seatop_resize_tiling_event *e = seat->seatop_data; | 49 | struct seatop_resize_tiling_event *e = seat->seatop_data; |
51 | 50 | ||
52 | if (seat->cursor->pressed_button_count == 0) { | 51 | if (seat->cursor->pressed_button_count == 0) { |
diff --git a/sway/input/switch.c b/sway/input/switch.c index 9ea87a1a..831f4dbf 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #include "sway/config.h" | 1 | #include "sway/config.h" |
2 | #include "sway/input/switch.h" | 2 | #include "sway/input/switch.h" |
3 | #include <wlr/types/wlr_idle.h> | ||
4 | #include "log.h" | 3 | #include "log.h" |
5 | 4 | ||
6 | struct sway_switch *sway_switch_create(struct sway_seat *seat, | 5 | struct sway_switch *sway_switch_create(struct sway_seat *seat, |
@@ -11,6 +10,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat, | |||
11 | return NULL; | 10 | return NULL; |
12 | } | 11 | } |
13 | device->switch_device = switch_device; | 12 | device->switch_device = switch_device; |
13 | switch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device); | ||
14 | switch_device->seat_device = device; | 14 | switch_device->seat_device = device; |
15 | switch_device->state = WLR_SWITCH_STATE_OFF; | 15 | switch_device->state = WLR_SWITCH_STATE_OFF; |
16 | wl_list_init(&switch_device->switch_toggle.link); | 16 | wl_list_init(&switch_device->switch_toggle.link); |
@@ -19,9 +19,22 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat, | |||
19 | return switch_device; | 19 | return switch_device; |
20 | } | 20 | } |
21 | 21 | ||
22 | static bool sway_switch_trigger_test(enum sway_switch_trigger trigger, | ||
23 | enum wlr_switch_state state) { | ||
24 | switch (trigger) { | ||
25 | case SWAY_SWITCH_TRIGGER_ON: | ||
26 | return state == WLR_SWITCH_STATE_ON; | ||
27 | case SWAY_SWITCH_TRIGGER_OFF: | ||
28 | return state == WLR_SWITCH_STATE_OFF; | ||
29 | case SWAY_SWITCH_TRIGGER_TOGGLE: | ||
30 | return true; | ||
31 | } | ||
32 | abort(); // unreachable | ||
33 | } | ||
34 | |||
22 | static void execute_binding(struct sway_switch *sway_switch) { | 35 | static void execute_binding(struct sway_switch *sway_switch) { |
23 | struct sway_seat* seat = sway_switch->seat_device->sway_seat; | 36 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; |
24 | bool input_inhibited = seat->exclusive_client != NULL; | 37 | bool locked = server.session_lock.lock; |
25 | 38 | ||
26 | list_t *bindings = config->current_mode->switch_bindings; | 39 | list_t *bindings = config->current_mode->switch_bindings; |
27 | struct sway_switch_binding *matched_binding = NULL; | 40 | struct sway_switch_binding *matched_binding = NULL; |
@@ -30,22 +43,21 @@ static void execute_binding(struct sway_switch *sway_switch) { | |||
30 | if (binding->type != sway_switch->type) { | 43 | if (binding->type != sway_switch->type) { |
31 | continue; | 44 | continue; |
32 | } | 45 | } |
33 | if (binding->state != WLR_SWITCH_STATE_TOGGLE && | 46 | if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) { |
34 | binding->state != sway_switch->state) { | ||
35 | continue; | 47 | continue; |
36 | } | 48 | } |
37 | if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE | 49 | if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE |
38 | || (binding->flags & BINDING_RELOAD) == 0)) { | 50 | || (binding->flags & BINDING_RELOAD) == 0)) { |
39 | continue; | 51 | continue; |
40 | } | 52 | } |
41 | bool binding_locked = binding->flags & BINDING_LOCKED; | 53 | bool binding_locked = binding->flags & BINDING_LOCKED; |
42 | if (!binding_locked && input_inhibited) { | 54 | if (!binding_locked && locked) { |
43 | continue; | 55 | continue; |
44 | } | 56 | } |
45 | 57 | ||
46 | matched_binding = binding; | 58 | matched_binding = binding; |
47 | 59 | ||
48 | if (binding_locked == input_inhibited) { | 60 | if (binding_locked == locked) { |
49 | break; | 61 | break; |
50 | } | 62 | } |
51 | } | 63 | } |
@@ -65,7 +77,7 @@ static void execute_binding(struct sway_switch *sway_switch) { | |||
65 | static void handle_switch_toggle(struct wl_listener *listener, void *data) { | 77 | static void handle_switch_toggle(struct wl_listener *listener, void *data) { |
66 | struct sway_switch *sway_switch = | 78 | struct sway_switch *sway_switch = |
67 | wl_container_of(listener, sway_switch, switch_toggle); | 79 | wl_container_of(listener, sway_switch, switch_toggle); |
68 | struct wlr_event_switch_toggle *event = data; | 80 | struct wlr_switch_toggle_event *event = data; |
69 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; | 81 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; |
70 | seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); | 82 | seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); |
71 | 83 | ||
@@ -82,10 +94,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) { | |||
82 | } | 94 | } |
83 | 95 | ||
84 | void sway_switch_configure(struct sway_switch *sway_switch) { | 96 | void sway_switch_configure(struct sway_switch *sway_switch) { |
85 | struct wlr_input_device *wlr_device = | ||
86 | sway_switch->seat_device->input_device->wlr_device; | ||
87 | wl_list_remove(&sway_switch->switch_toggle.link); | 97 | wl_list_remove(&sway_switch->switch_toggle.link); |
88 | wl_signal_add(&wlr_device->switch_device->events.toggle, | 98 | wl_signal_add(&sway_switch->wlr->events.toggle, |
89 | &sway_switch->switch_toggle); | 99 | &sway_switch->switch_toggle); |
90 | sway_switch->switch_toggle.notify = handle_switch_toggle; | 100 | sway_switch->switch_toggle.notify = handle_switch_toggle; |
91 | sway_log(SWAY_DEBUG, "Configured switch for device"); | 101 | sway_log(SWAY_DEBUG, "Configured switch for device"); |
diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 26e86e36..2863642a 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <wlr/backend/libinput.h> | 2 | #include <wlr/config.h> |
4 | #include <wlr/types/wlr_tablet_v2.h> | 3 | #include <wlr/types/wlr_tablet_v2.h> |
5 | #include <wlr/types/wlr_tablet_tool.h> | 4 | #include <wlr/types/wlr_tablet_tool.h> |
6 | #include <wlr/types/wlr_tablet_pad.h> | 5 | #include <wlr/types/wlr_tablet_pad.h> |
@@ -9,6 +8,10 @@ | |||
9 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
10 | #include "sway/input/tablet.h" | 9 | #include "sway/input/tablet.h" |
11 | 10 | ||
11 | #if WLR_HAS_LIBINPUT_BACKEND | ||
12 | #include <wlr/backend/libinput.h> | ||
13 | #endif | ||
14 | |||
12 | static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { | 15 | static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { |
13 | struct sway_tablet_pad *pad = | 16 | struct sway_tablet_pad *pad = |
14 | wl_container_of(listener, pad, tablet_destroy); | 17 | wl_container_of(listener, pad, tablet_destroy); |
@@ -54,15 +57,14 @@ void sway_configure_tablet(struct sway_tablet *tablet) { | |||
54 | tablet->seat_device->input_device->wlr_device; | 57 | tablet->seat_device->input_device->wlr_device; |
55 | struct sway_seat *seat = tablet->seat_device->sway_seat; | 58 | struct sway_seat *seat = tablet->seat_device->sway_seat; |
56 | 59 | ||
57 | if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { | 60 | seat_configure_xcursor(seat); |
58 | seat_configure_xcursor(seat); | ||
59 | } | ||
60 | 61 | ||
61 | if (!tablet->tablet_v2) { | 62 | if (!tablet->tablet_v2) { |
62 | tablet->tablet_v2 = | 63 | tablet->tablet_v2 = |
63 | wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); | 64 | wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); |
64 | } | 65 | } |
65 | 66 | ||
67 | #if WLR_HAS_LIBINPUT_BACKEND | ||
66 | /* Search for a sibling tablet pad */ | 68 | /* Search for a sibling tablet pad */ |
67 | if (!wlr_input_device_is_libinput(device)) { | 69 | if (!wlr_input_device_is_libinput(device)) { |
68 | /* We can only do this on libinput devices */ | 70 | /* We can only do this on libinput devices */ |
@@ -87,6 +89,7 @@ void sway_configure_tablet(struct sway_tablet *tablet) { | |||
87 | break; | 89 | break; |
88 | } | 90 | } |
89 | } | 91 | } |
92 | #endif | ||
90 | } | 93 | } |
91 | 94 | ||
92 | void sway_tablet_destroy(struct sway_tablet *tablet) { | 95 | void sway_tablet_destroy(struct sway_tablet *tablet) { |
@@ -196,7 +199,7 @@ static void handle_tablet_pad_attach(struct wl_listener *listener, | |||
196 | 199 | ||
197 | static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { | 200 | static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { |
198 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); | 201 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); |
199 | struct wlr_event_tablet_pad_ring *event = data; | 202 | struct wlr_tablet_pad_ring_event *event = data; |
200 | 203 | ||
201 | if (!pad->current_surface) { | 204 | if (!pad->current_surface) { |
202 | return; | 205 | return; |
@@ -210,7 +213,7 @@ static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { | |||
210 | 213 | ||
211 | static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { | 214 | static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { |
212 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); | 215 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); |
213 | struct wlr_event_tablet_pad_strip *event = data; | 216 | struct wlr_tablet_pad_strip_event *event = data; |
214 | 217 | ||
215 | if (!pad->current_surface) { | 218 | if (!pad->current_surface) { |
216 | return; | 219 | return; |
@@ -224,7 +227,7 @@ static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { | |||
224 | 227 | ||
225 | static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { | 228 | static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { |
226 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); | 229 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); |
227 | struct wlr_event_tablet_pad_button *event = data; | 230 | struct wlr_tablet_pad_button_event *event = data; |
228 | 231 | ||
229 | if (!pad->current_surface) { | 232 | if (!pad->current_surface) { |
230 | return; | 233 | return; |
@@ -246,6 +249,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, | |||
246 | return NULL; | 249 | return NULL; |
247 | } | 250 | } |
248 | 251 | ||
252 | tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device); | ||
249 | tablet_pad->seat_device = device; | 253 | tablet_pad->seat_device = device; |
250 | wl_list_init(&tablet_pad->attach.link); | 254 | wl_list_init(&tablet_pad->attach.link); |
251 | wl_list_init(&tablet_pad->button.link); | 255 | wl_list_init(&tablet_pad->button.link); |
@@ -260,40 +264,41 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, | |||
260 | } | 264 | } |
261 | 265 | ||
262 | void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { | 266 | void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { |
263 | struct wlr_input_device *device = | 267 | struct wlr_input_device *wlr_device = |
264 | tablet_pad->seat_device->input_device->wlr_device; | 268 | tablet_pad->seat_device->input_device->wlr_device; |
265 | struct sway_seat *seat = tablet_pad->seat_device->sway_seat; | 269 | struct sway_seat *seat = tablet_pad->seat_device->sway_seat; |
266 | 270 | ||
267 | if (!tablet_pad->tablet_v2_pad) { | 271 | if (!tablet_pad->tablet_v2_pad) { |
268 | tablet_pad->tablet_v2_pad = | 272 | tablet_pad->tablet_v2_pad = |
269 | wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); | 273 | wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device); |
270 | } | 274 | } |
271 | 275 | ||
272 | wl_list_remove(&tablet_pad->attach.link); | 276 | wl_list_remove(&tablet_pad->attach.link); |
273 | tablet_pad->attach.notify = handle_tablet_pad_attach; | 277 | tablet_pad->attach.notify = handle_tablet_pad_attach; |
274 | wl_signal_add(&device->tablet_pad->events.attach_tablet, | 278 | wl_signal_add(&tablet_pad->wlr->events.attach_tablet, |
275 | &tablet_pad->attach); | 279 | &tablet_pad->attach); |
276 | 280 | ||
277 | wl_list_remove(&tablet_pad->button.link); | 281 | wl_list_remove(&tablet_pad->button.link); |
278 | tablet_pad->button.notify = handle_tablet_pad_button; | 282 | tablet_pad->button.notify = handle_tablet_pad_button; |
279 | wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); | 283 | wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button); |
280 | 284 | ||
281 | wl_list_remove(&tablet_pad->strip.link); | 285 | wl_list_remove(&tablet_pad->strip.link); |
282 | tablet_pad->strip.notify = handle_tablet_pad_strip; | 286 | tablet_pad->strip.notify = handle_tablet_pad_strip; |
283 | wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); | 287 | wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip); |
284 | 288 | ||
285 | wl_list_remove(&tablet_pad->ring.link); | 289 | wl_list_remove(&tablet_pad->ring.link); |
286 | tablet_pad->ring.notify = handle_tablet_pad_ring; | 290 | tablet_pad->ring.notify = handle_tablet_pad_ring; |
287 | wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); | 291 | wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring); |
288 | 292 | ||
293 | #if WLR_HAS_LIBINPUT_BACKEND | ||
289 | /* Search for a sibling tablet */ | 294 | /* Search for a sibling tablet */ |
290 | if (!wlr_input_device_is_libinput(device)) { | 295 | if (!wlr_input_device_is_libinput(wlr_device)) { |
291 | /* We can only do this on libinput devices */ | 296 | /* We can only do this on libinput devices */ |
292 | return; | 297 | return; |
293 | } | 298 | } |
294 | 299 | ||
295 | struct libinput_device_group *group = | 300 | struct libinput_device_group *group = |
296 | libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); | 301 | libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device)); |
297 | struct sway_tablet *tool; | 302 | struct sway_tablet *tool; |
298 | wl_list_for_each(tool, &seat->cursor->tablets, link) { | 303 | wl_list_for_each(tool, &seat->cursor->tablets, link) { |
299 | struct wlr_input_device *tablet = | 304 | struct wlr_input_device *tablet = |
@@ -310,6 +315,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { | |||
310 | break; | 315 | break; |
311 | } | 316 | } |
312 | } | 317 | } |
318 | #endif | ||
313 | } | 319 | } |
314 | 320 | ||
315 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { | 321 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { |
@@ -333,14 +339,10 @@ static void handle_pad_tablet_surface_destroy(struct wl_listener *listener, | |||
333 | struct sway_tablet_pad *tablet_pad = | 339 | struct sway_tablet_pad *tablet_pad = |
334 | wl_container_of(listener, tablet_pad, surface_destroy); | 340 | wl_container_of(listener, tablet_pad, surface_destroy); |
335 | 341 | ||
336 | wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, | 342 | sway_tablet_pad_set_focus(tablet_pad, NULL); |
337 | tablet_pad->current_surface); | ||
338 | wl_list_remove(&tablet_pad->surface_destroy.link); | ||
339 | wl_list_init(&tablet_pad->surface_destroy.link); | ||
340 | tablet_pad->current_surface = NULL; | ||
341 | } | 343 | } |
342 | 344 | ||
343 | void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | 345 | void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad, |
344 | struct wlr_surface *surface) { | 346 | struct wlr_surface *surface) { |
345 | if (!tablet_pad || !tablet_pad->tablet) { | 347 | if (!tablet_pad || !tablet_pad->tablet) { |
346 | return; | 348 | return; |
@@ -359,7 +361,8 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | |||
359 | tablet_pad->current_surface = NULL; | 361 | tablet_pad->current_surface = NULL; |
360 | } | 362 | } |
361 | 363 | ||
362 | if (!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { | 364 | if (surface == NULL || |
365 | !wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { | ||
363 | return; | 366 | return; |
364 | } | 367 | } |
365 | 368 | ||
@@ -367,7 +370,6 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | |||
367 | tablet_pad->tablet->tablet_v2, surface); | 370 | tablet_pad->tablet->tablet_v2, surface); |
368 | 371 | ||
369 | tablet_pad->current_surface = surface; | 372 | tablet_pad->current_surface = surface; |
370 | wl_list_remove(&tablet_pad->surface_destroy.link); | ||
371 | tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; | 373 | tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; |
372 | wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); | 374 | wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); |
373 | } | 375 | } |
diff --git a/sway/input/text_input.c b/sway/input/text_input.c index b8c19c17..c38a3bb2 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c | |||
@@ -2,7 +2,14 @@ | |||
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include "log.h" | 3 | #include "log.h" |
4 | #include "sway/input/seat.h" | 4 | #include "sway/input/seat.h" |
5 | #include "sway/scene_descriptor.h" | ||
6 | #include "sway/tree/root.h" | ||
7 | #include "sway/tree/view.h" | ||
8 | #include "sway/output.h" | ||
5 | #include "sway/input/text_input.h" | 9 | #include "sway/input/text_input.h" |
10 | #include "sway/input/text_input_popup.h" | ||
11 | #include "sway/layers.h" | ||
12 | static void input_popup_update(struct sway_input_popup *popup); | ||
6 | 13 | ||
7 | static struct sway_text_input *relay_get_focusable_text_input( | 14 | static struct sway_text_input *relay_get_focusable_text_input( |
8 | struct sway_input_method_relay *relay) { | 15 | struct sway_input_method_relay *relay) { |
@@ -77,8 +84,6 @@ static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) { | |||
77 | struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat); | 84 | struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat); |
78 | wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, | 85 | wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, |
79 | active_keyboard); | 86 | active_keyboard); |
80 | wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab, | ||
81 | &active_keyboard->modifiers); | ||
82 | 87 | ||
83 | wl_signal_add(&keyboard_grab->events.destroy, | 88 | wl_signal_add(&keyboard_grab->events.destroy, |
84 | &relay->input_method_keyboard_grab_destroy); | 89 | &relay->input_method_keyboard_grab_destroy); |
@@ -104,6 +109,10 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) { | |||
104 | input_method_destroy); | 109 | input_method_destroy); |
105 | struct wlr_input_method_v2 *context = data; | 110 | struct wlr_input_method_v2 *context = data; |
106 | assert(context == relay->input_method); | 111 | assert(context == relay->input_method); |
112 | wl_list_remove(&relay->input_method_commit.link); | ||
113 | wl_list_remove(&relay->input_method_grab_keyboard.link); | ||
114 | wl_list_remove(&relay->input_method_destroy.link); | ||
115 | wl_list_remove(&relay->input_method_new_popup_surface.link); | ||
107 | relay->input_method = NULL; | 116 | relay->input_method = NULL; |
108 | struct sway_text_input *text_input = relay_get_focused_text_input(relay); | 117 | struct sway_text_input *text_input = relay_get_focused_text_input(relay); |
109 | if (text_input) { | 118 | if (text_input) { |
@@ -135,6 +144,11 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, | |||
135 | input->current.content_type.hint, | 144 | input->current.content_type.hint, |
136 | input->current.content_type.purpose); | 145 | input->current.content_type.purpose); |
137 | } | 146 | } |
147 | struct sway_input_popup *popup; | ||
148 | wl_list_for_each(popup, &relay->input_popups, link) { | ||
149 | // send_text_input_rectangle is called in this function | ||
150 | input_popup_update(popup); | ||
151 | } | ||
138 | wlr_input_method_v2_send_done(input_method); | 152 | wlr_input_method_v2_send_done(input_method); |
139 | // TODO: pass intent, display popup size | 153 | // TODO: pass intent, display popup size |
140 | } | 154 | } |
@@ -257,6 +271,211 @@ static void relay_handle_text_input(struct wl_listener *listener, | |||
257 | sway_text_input_create(relay, wlr_text_input); | 271 | sway_text_input_create(relay, wlr_text_input); |
258 | } | 272 | } |
259 | 273 | ||
274 | static void input_popup_update(struct sway_input_popup *popup) { | ||
275 | struct sway_text_input *text_input = | ||
276 | relay_get_focused_text_input(popup->relay); | ||
277 | |||
278 | if (text_input == NULL || text_input->input->focused_surface == NULL) { | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | if (popup->scene_tree != NULL) { | ||
283 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
284 | popup->scene_tree = NULL; | ||
285 | } | ||
286 | if (popup->desc.relative != NULL) { | ||
287 | wlr_scene_node_destroy(popup->desc.relative); | ||
288 | popup->desc.relative = NULL; | ||
289 | } | ||
290 | popup->desc.view = NULL; | ||
291 | |||
292 | if (!popup->popup_surface->surface->mapped) { | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | bool cursor_rect = text_input->input->current.features | ||
297 | & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; | ||
298 | struct wlr_surface *focused_surface = text_input->input->focused_surface; | ||
299 | struct wlr_box cursor_area = text_input->input->current.cursor_rectangle; | ||
300 | |||
301 | struct wlr_box output_box; | ||
302 | struct wlr_box parent = {0}; | ||
303 | struct wlr_layer_surface_v1 *layer_surface = | ||
304 | wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); | ||
305 | struct wlr_scene_tree *relative_parent; | ||
306 | |||
307 | struct wlr_box geo = {0}; | ||
308 | |||
309 | popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface); | ||
310 | if (layer_surface != NULL) { | ||
311 | struct sway_layer_surface *layer = | ||
312 | layer_surface->data; | ||
313 | if (layer == NULL) { | ||
314 | return; | ||
315 | } | ||
316 | |||
317 | relative_parent = layer->scene->tree; | ||
318 | struct wlr_output *output = layer->layer_surface->output; | ||
319 | wlr_output_layout_get_box(root->output_layout, output, &output_box); | ||
320 | int lx, ly; | ||
321 | wlr_scene_node_coords(&layer->tree->node, &lx, &ly); | ||
322 | parent.x = lx; | ||
323 | parent.y = ly; | ||
324 | popup->desc.view = NULL; | ||
325 | } else { | ||
326 | struct sway_view *view = view_from_wlr_surface(focused_surface); | ||
327 | relative_parent = view->scene_tree; | ||
328 | geo = view->geometry; | ||
329 | int lx, ly; | ||
330 | wlr_scene_node_coords(&view->scene_tree->node, &lx, &ly); | ||
331 | struct wlr_output *output = wlr_output_layout_output_at(root->output_layout, | ||
332 | view->container->pending.content_x + view->geometry.x, | ||
333 | view->container->pending.content_y + view->geometry.y); | ||
334 | wlr_output_layout_get_box(root->output_layout, output, &output_box); | ||
335 | parent.x = lx; | ||
336 | parent.y = ly; | ||
337 | |||
338 | parent.width = view->geometry.width; | ||
339 | parent.height = view->geometry.height; | ||
340 | popup->desc.view = view; | ||
341 | } | ||
342 | |||
343 | struct wlr_scene_tree *relative = wlr_scene_tree_create(relative_parent); | ||
344 | |||
345 | popup->desc.relative = &relative->node; | ||
346 | if (!scene_descriptor_assign(&popup->scene_tree->node, | ||
347 | SWAY_SCENE_DESC_POPUP, &popup->desc)) { | ||
348 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
349 | popup->scene_tree = NULL; | ||
350 | return; | ||
351 | } | ||
352 | |||
353 | if (!cursor_rect) { | ||
354 | cursor_area.x = 0; | ||
355 | cursor_area.y = 0; | ||
356 | cursor_area.width = parent.width; | ||
357 | cursor_area.height = parent.height; | ||
358 | } | ||
359 | |||
360 | int popup_width = popup->popup_surface->surface->current.width; | ||
361 | int popup_height = popup->popup_surface->surface->current.height; | ||
362 | int x1 = parent.x + cursor_area.x; | ||
363 | int x2 = parent.x + cursor_area.x + cursor_area.width; | ||
364 | int y1 = parent.y + cursor_area.y; | ||
365 | int y2 = parent.y + cursor_area.y + cursor_area.height; | ||
366 | int x = x1; | ||
367 | int y = y2; | ||
368 | |||
369 | int available_right = output_box.x + output_box.width - x1; | ||
370 | int available_left = x2 - output_box.x; | ||
371 | if (available_right < popup_width && available_left > available_right) { | ||
372 | x = x2 - popup_width; | ||
373 | } | ||
374 | |||
375 | int available_down = output_box.y + output_box.height - y2; | ||
376 | int available_up = y1 - output_box.y; | ||
377 | if (available_down < popup_height && available_up > available_down) { | ||
378 | y = y1 - popup_height; | ||
379 | } | ||
380 | |||
381 | wlr_scene_node_set_position(&relative->node, x - parent.x - geo.x, y - parent.y - geo.y); | ||
382 | if (cursor_rect) { | ||
383 | struct wlr_box box = { | ||
384 | .x = x1 - x, | ||
385 | .y = y1 - y, | ||
386 | .width = cursor_area.width, | ||
387 | .height = cursor_area.height, | ||
388 | }; | ||
389 | wlr_input_popup_surface_v2_send_text_input_rectangle( | ||
390 | popup->popup_surface, &box); | ||
391 | } | ||
392 | wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); | ||
393 | } | ||
394 | |||
395 | static void input_popup_set_focus(struct sway_input_popup *popup, | ||
396 | struct wlr_surface *surface) { | ||
397 | wl_list_remove(&popup->focused_surface_unmap.link); | ||
398 | |||
399 | if (surface == NULL) { | ||
400 | wl_list_init(&popup->focused_surface_unmap.link); | ||
401 | input_popup_update(popup); | ||
402 | return; | ||
403 | } | ||
404 | struct wlr_layer_surface_v1 *layer_surface = | ||
405 | wlr_layer_surface_v1_try_from_wlr_surface(surface); | ||
406 | if (layer_surface != NULL) { | ||
407 | wl_signal_add( | ||
408 | &layer_surface->surface->events.unmap, &popup->focused_surface_unmap); | ||
409 | input_popup_update(popup); | ||
410 | return; | ||
411 | } | ||
412 | |||
413 | struct sway_view *view = view_from_wlr_surface(surface); | ||
414 | wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); | ||
415 | } | ||
416 | |||
417 | static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { | ||
418 | struct sway_input_popup *popup = | ||
419 | wl_container_of(listener, popup, popup_destroy); | ||
420 | wl_list_remove(&popup->focused_surface_unmap.link); | ||
421 | wl_list_remove(&popup->popup_surface_commit.link); | ||
422 | wl_list_remove(&popup->popup_destroy.link); | ||
423 | wl_list_remove(&popup->link); | ||
424 | |||
425 | free(popup); | ||
426 | } | ||
427 | |||
428 | static void handle_im_popup_surface_commit(struct wl_listener *listener, | ||
429 | void *data) { | ||
430 | struct sway_input_popup *popup = | ||
431 | wl_container_of(listener, popup, popup_surface_commit); | ||
432 | input_popup_update(popup); | ||
433 | } | ||
434 | |||
435 | static void handle_im_focused_surface_unmap( | ||
436 | struct wl_listener *listener, void *data) { | ||
437 | struct sway_input_popup *popup = | ||
438 | wl_container_of(listener, popup, focused_surface_unmap); | ||
439 | input_popup_update(popup); | ||
440 | } | ||
441 | |||
442 | static void handle_im_new_popup_surface(struct wl_listener *listener, | ||
443 | void *data) { | ||
444 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
445 | input_method_new_popup_surface); | ||
446 | struct sway_input_popup *popup = calloc(1, sizeof(*popup)); | ||
447 | popup->relay = relay; | ||
448 | popup->popup_surface = data; | ||
449 | popup->popup_surface->data = popup; | ||
450 | |||
451 | wl_signal_add( | ||
452 | &popup->popup_surface->events.destroy, &popup->popup_destroy); | ||
453 | popup->popup_destroy.notify = handle_im_popup_destroy; | ||
454 | wl_signal_add(&popup->popup_surface->surface->events.commit, | ||
455 | &popup->popup_surface_commit); | ||
456 | popup->popup_surface_commit.notify = handle_im_popup_surface_commit; | ||
457 | wl_list_init(&popup->focused_surface_unmap.link); | ||
458 | popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap; | ||
459 | |||
460 | struct sway_text_input *text_input = relay_get_focused_text_input(relay); | ||
461 | if (text_input != NULL) { | ||
462 | input_popup_set_focus(popup, text_input->input->focused_surface); | ||
463 | } else { | ||
464 | input_popup_set_focus(popup, NULL); | ||
465 | } | ||
466 | |||
467 | wl_list_insert(&relay->input_popups, &popup->link); | ||
468 | } | ||
469 | |||
470 | static void text_input_send_enter(struct sway_text_input *text_input, | ||
471 | struct wlr_surface *surface) { | ||
472 | wlr_text_input_v3_send_enter(text_input->input, surface); | ||
473 | struct sway_input_popup *popup; | ||
474 | wl_list_for_each(popup, &text_input->relay->input_popups, link) { | ||
475 | input_popup_set_focus(popup, surface); | ||
476 | } | ||
477 | } | ||
478 | |||
260 | static void relay_handle_input_method(struct wl_listener *listener, | 479 | static void relay_handle_input_method(struct wl_listener *listener, |
261 | void *data) { | 480 | void *data) { |
262 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | 481 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, |
@@ -282,10 +501,13 @@ static void relay_handle_input_method(struct wl_listener *listener, | |||
282 | wl_signal_add(&relay->input_method->events.destroy, | 501 | wl_signal_add(&relay->input_method->events.destroy, |
283 | &relay->input_method_destroy); | 502 | &relay->input_method_destroy); |
284 | relay->input_method_destroy.notify = handle_im_destroy; | 503 | relay->input_method_destroy.notify = handle_im_destroy; |
504 | wl_signal_add(&relay->input_method->events.new_popup_surface, | ||
505 | &relay->input_method_new_popup_surface); | ||
506 | relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface; | ||
285 | 507 | ||
286 | struct sway_text_input *text_input = relay_get_focusable_text_input(relay); | 508 | struct sway_text_input *text_input = relay_get_focusable_text_input(relay); |
287 | if (text_input) { | 509 | if (text_input) { |
288 | wlr_text_input_v3_send_enter(text_input->input, | 510 | text_input_send_enter(text_input, |
289 | text_input->pending_focused_surface); | 511 | text_input->pending_focused_surface); |
290 | text_input_set_pending_focused_surface(text_input, NULL); | 512 | text_input_set_pending_focused_surface(text_input, NULL); |
291 | } | 513 | } |
@@ -295,6 +517,7 @@ void sway_input_method_relay_init(struct sway_seat *seat, | |||
295 | struct sway_input_method_relay *relay) { | 517 | struct sway_input_method_relay *relay) { |
296 | relay->seat = seat; | 518 | relay->seat = seat; |
297 | wl_list_init(&relay->text_inputs); | 519 | wl_list_init(&relay->text_inputs); |
520 | wl_list_init(&relay->input_popups); | ||
298 | 521 | ||
299 | relay->text_input_new.notify = relay_handle_text_input; | 522 | relay->text_input_new.notify = relay_handle_text_input; |
300 | wl_signal_add(&server.text_input->events.text_input, | 523 | wl_signal_add(&server.text_input->events.text_input, |
diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 1b64f86e..81ca3483 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c | |||
@@ -3,7 +3,8 @@ | |||
3 | #include <json.h> | 3 | #include <json.h> |
4 | #include <libevdev/libevdev.h> | 4 | #include <libevdev/libevdev.h> |
5 | #include <stdio.h> | 5 | #include <stdio.h> |
6 | #include <wlr/backend/libinput.h> | 6 | #include <wlr/config.h> |
7 | #include <wlr/types/wlr_content_type_v1.h> | ||
7 | #include <wlr/types/wlr_output.h> | 8 | #include <wlr/types/wlr_output.h> |
8 | #include <xkbcommon/xkbcommon.h> | 9 | #include <xkbcommon/xkbcommon.h> |
9 | #include "config.h" | 10 | #include "config.h" |
@@ -20,6 +21,10 @@ | |||
20 | #include "wlr-layer-shell-unstable-v1-protocol.h" | 21 | #include "wlr-layer-shell-unstable-v1-protocol.h" |
21 | #include "sway/desktop/idle_inhibit_v1.h" | 22 | #include "sway/desktop/idle_inhibit_v1.h" |
22 | 23 | ||
24 | #if WLR_HAS_LIBINPUT_BACKEND | ||
25 | #include <wlr/backend/libinput.h> | ||
26 | #endif | ||
27 | |||
23 | static const int i3_output_id = INT32_MAX; | 28 | static const int i3_output_id = INT32_MAX; |
24 | static const int i3_scratch_id = INT32_MAX - 1; | 29 | static const int i3_scratch_id = INT32_MAX - 1; |
25 | 30 | ||
@@ -112,12 +117,43 @@ static const char *ipc_json_output_adaptive_sync_status_description( | |||
112 | return "disabled"; | 117 | return "disabled"; |
113 | case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: | 118 | case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: |
114 | return "enabled"; | 119 | return "enabled"; |
115 | case WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN: | ||
116 | return "unknown"; | ||
117 | } | 120 | } |
118 | return NULL; | 121 | return NULL; |
119 | } | 122 | } |
120 | 123 | ||
124 | static const char *ipc_json_output_mode_aspect_ratio_description( | ||
125 | enum wlr_output_mode_aspect_ratio aspect_ratio) { | ||
126 | switch (aspect_ratio) { | ||
127 | case WLR_OUTPUT_MODE_ASPECT_RATIO_NONE: | ||
128 | return "none"; | ||
129 | case WLR_OUTPUT_MODE_ASPECT_RATIO_4_3: | ||
130 | return "4:3"; | ||
131 | case WLR_OUTPUT_MODE_ASPECT_RATIO_16_9: | ||
132 | return "16:9"; | ||
133 | case WLR_OUTPUT_MODE_ASPECT_RATIO_64_27: | ||
134 | return "64:27"; | ||
135 | case WLR_OUTPUT_MODE_ASPECT_RATIO_256_135: | ||
136 | return "256:135"; | ||
137 | } | ||
138 | return NULL; | ||
139 | } | ||
140 | |||
141 | static json_object *ipc_json_output_mode_description( | ||
142 | const struct wlr_output_mode *mode) { | ||
143 | const char *pic_ar = | ||
144 | ipc_json_output_mode_aspect_ratio_description(mode->picture_aspect_ratio); | ||
145 | json_object *mode_object = json_object_new_object(); | ||
146 | json_object_object_add(mode_object, "width", | ||
147 | json_object_new_int(mode->width)); | ||
148 | json_object_object_add(mode_object, "height", | ||
149 | json_object_new_int(mode->height)); | ||
150 | json_object_object_add(mode_object, "refresh", | ||
151 | json_object_new_int(mode->refresh)); | ||
152 | json_object_object_add(mode_object, "picture_aspect_ratio", | ||
153 | json_object_new_string(pic_ar)); | ||
154 | return mode_object; | ||
155 | } | ||
156 | |||
121 | #if HAVE_XWAYLAND | 157 | #if HAVE_XWAYLAND |
122 | static const char *ipc_json_xwindow_type_description(struct sway_view *view) { | 158 | static const char *ipc_json_xwindow_type_description(struct sway_view *view) { |
123 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; | 159 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; |
@@ -170,6 +206,20 @@ static const char *ipc_json_user_idle_inhibitor_description(enum sway_idle_inhib | |||
170 | return NULL; | 206 | return NULL; |
171 | } | 207 | } |
172 | 208 | ||
209 | static const char *ipc_json_content_type_description(enum wp_content_type_v1_type type) { | ||
210 | switch (type) { | ||
211 | case WP_CONTENT_TYPE_V1_TYPE_NONE: | ||
212 | return "none"; | ||
213 | case WP_CONTENT_TYPE_V1_TYPE_PHOTO: | ||
214 | return "photo"; | ||
215 | case WP_CONTENT_TYPE_V1_TYPE_VIDEO: | ||
216 | return "video"; | ||
217 | case WP_CONTENT_TYPE_V1_TYPE_GAME: | ||
218 | return "game"; | ||
219 | } | ||
220 | return NULL; | ||
221 | } | ||
222 | |||
173 | json_object *ipc_json_get_version(void) { | 223 | json_object *ipc_json_get_version(void) { |
174 | int major = 0, minor = 0, patch = 0; | 224 | int major = 0, minor = 0, patch = 0; |
175 | json_object *version = json_object_new_object(); | 225 | json_object *version = json_object_new_object(); |
@@ -238,27 +288,56 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name, | |||
238 | json_object_object_add(object, "focus", focus); | 288 | json_object_object_add(object, "focus", focus); |
239 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); | 289 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); |
240 | json_object_object_add(object, "sticky", json_object_new_boolean(false)); | 290 | json_object_object_add(object, "sticky", json_object_new_boolean(false)); |
291 | json_object_object_add(object, "floating", NULL); | ||
292 | json_object_object_add(object, "scratchpad_state", NULL); | ||
241 | 293 | ||
242 | return object; | 294 | return object; |
243 | } | 295 | } |
244 | 296 | ||
297 | static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) { | ||
298 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | ||
299 | json_object_object_add(object, "make", | ||
300 | json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); | ||
301 | json_object_object_add(object, "model", | ||
302 | json_object_new_string(wlr_output->model ? wlr_output->model : "Unknown")); | ||
303 | json_object_object_add(object, "serial", | ||
304 | json_object_new_string(wlr_output->serial ? wlr_output->serial : "Unknown")); | ||
305 | |||
306 | json_object *modes_array = json_object_new_array(); | ||
307 | struct wlr_output_mode *mode; | ||
308 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
309 | json_object *mode_object = json_object_new_object(); | ||
310 | json_object_object_add(mode_object, "width", | ||
311 | json_object_new_int(mode->width)); | ||
312 | json_object_object_add(mode_object, "height", | ||
313 | json_object_new_int(mode->height)); | ||
314 | json_object_object_add(mode_object, "refresh", | ||
315 | json_object_new_int(mode->refresh)); | ||
316 | json_object_array_add(modes_array, mode_object); | ||
317 | } | ||
318 | json_object_object_add(object, "modes", modes_array); | ||
319 | } | ||
320 | |||
245 | static void ipc_json_describe_output(struct sway_output *output, | 321 | static void ipc_json_describe_output(struct sway_output *output, |
246 | json_object *object) { | 322 | json_object *object) { |
323 | ipc_json_describe_wlr_output(output->wlr_output, object); | ||
324 | } | ||
325 | |||
326 | static void ipc_json_describe_enabled_output(struct sway_output *output, | ||
327 | json_object *object) { | ||
328 | ipc_json_describe_output(output, object); | ||
329 | |||
247 | struct wlr_output *wlr_output = output->wlr_output; | 330 | struct wlr_output *wlr_output = output->wlr_output; |
331 | json_object_object_add(object, "non_desktop", json_object_new_boolean(false)); | ||
248 | json_object_object_add(object, "active", json_object_new_boolean(true)); | 332 | json_object_object_add(object, "active", json_object_new_boolean(true)); |
249 | json_object_object_add(object, "dpms", | 333 | json_object_object_add(object, "dpms", |
250 | json_object_new_boolean(wlr_output->enabled)); | 334 | json_object_new_boolean(wlr_output->enabled)); |
251 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | 335 | json_object_object_add(object, "power", |
336 | json_object_new_boolean(wlr_output->enabled)); | ||
252 | json_object_object_add(object, "layout", json_object_new_string("output")); | 337 | json_object_object_add(object, "layout", json_object_new_string("output")); |
253 | json_object_object_add(object, "orientation", | 338 | json_object_object_add(object, "orientation", |
254 | json_object_new_string( | 339 | json_object_new_string( |
255 | ipc_json_orientation_description(L_NONE))); | 340 | ipc_json_orientation_description(L_NONE))); |
256 | json_object_object_add(object, "make", | ||
257 | json_object_new_string(wlr_output->make)); | ||
258 | json_object_object_add(object, "model", | ||
259 | json_object_new_string(wlr_output->model)); | ||
260 | json_object_object_add(object, "serial", | ||
261 | json_object_new_string(wlr_output->serial)); | ||
262 | json_object_object_add(object, "scale", | 341 | json_object_object_add(object, "scale", |
263 | json_object_new_double(wlr_output->scale)); | 342 | json_object_new_double(wlr_output->scale)); |
264 | json_object_object_add(object, "scale_filter", | 343 | json_object_object_add(object, "scale_filter", |
@@ -283,25 +362,26 @@ static void ipc_json_describe_output(struct sway_output *output, | |||
283 | json_object *modes_array = json_object_new_array(); | 362 | json_object *modes_array = json_object_new_array(); |
284 | struct wlr_output_mode *mode; | 363 | struct wlr_output_mode *mode; |
285 | wl_list_for_each(mode, &wlr_output->modes, link) { | 364 | wl_list_for_each(mode, &wlr_output->modes, link) { |
286 | json_object *mode_object = json_object_new_object(); | 365 | json_object *mode_object = |
287 | json_object_object_add(mode_object, "width", | 366 | ipc_json_output_mode_description(mode); |
288 | json_object_new_int(mode->width)); | ||
289 | json_object_object_add(mode_object, "height", | ||
290 | json_object_new_int(mode->height)); | ||
291 | json_object_object_add(mode_object, "refresh", | ||
292 | json_object_new_int(mode->refresh)); | ||
293 | json_object_array_add(modes_array, mode_object); | 367 | json_object_array_add(modes_array, mode_object); |
294 | } | 368 | } |
295 | 369 | ||
296 | json_object_object_add(object, "modes", modes_array); | 370 | json_object_object_add(object, "modes", modes_array); |
297 | 371 | ||
298 | json_object *current_mode_object = json_object_new_object(); | 372 | json_object *current_mode_object; |
299 | json_object_object_add(current_mode_object, "width", | 373 | if (wlr_output->current_mode != NULL) { |
300 | json_object_new_int(wlr_output->width)); | 374 | current_mode_object = |
301 | json_object_object_add(current_mode_object, "height", | 375 | ipc_json_output_mode_description(wlr_output->current_mode); |
302 | json_object_new_int(wlr_output->height)); | 376 | } else { |
303 | json_object_object_add(current_mode_object, "refresh", | 377 | current_mode_object = json_object_new_object(); |
304 | json_object_new_int(wlr_output->refresh)); | 378 | json_object_object_add(current_mode_object, "width", |
379 | json_object_new_int(wlr_output->width)); | ||
380 | json_object_object_add(current_mode_object, "height", | ||
381 | json_object_new_int(wlr_output->height)); | ||
382 | json_object_object_add(current_mode_object, "refresh", | ||
383 | json_object_new_int(wlr_output->refresh)); | ||
384 | } | ||
305 | json_object_object_add(object, "current_mode", current_mode_object); | 385 | json_object_object_add(object, "current_mode", current_mode_object); |
306 | 386 | ||
307 | struct sway_node *parent = node_get_parent(&output->node); | 387 | struct sway_node *parent = node_get_parent(&output->node); |
@@ -325,33 +405,15 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { | |||
325 | 405 | ||
326 | json_object *object = json_object_new_object(); | 406 | json_object *object = json_object_new_object(); |
327 | 407 | ||
408 | ipc_json_describe_output(output, object); | ||
409 | |||
410 | json_object_object_add(object, "non_desktop", json_object_new_boolean(false)); | ||
328 | json_object_object_add(object, "type", json_object_new_string("output")); | 411 | json_object_object_add(object, "type", json_object_new_string("output")); |
329 | json_object_object_add(object, "name", | 412 | json_object_object_add(object, "name", |
330 | json_object_new_string(wlr_output->name)); | 413 | json_object_new_string(wlr_output->name)); |
331 | json_object_object_add(object, "active", json_object_new_boolean(false)); | 414 | json_object_object_add(object, "active", json_object_new_boolean(false)); |
332 | json_object_object_add(object, "dpms", json_object_new_boolean(false)); | 415 | json_object_object_add(object, "dpms", json_object_new_boolean(false)); |
333 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | 416 | json_object_object_add(object, "power", json_object_new_boolean(false)); |
334 | json_object_object_add(object, "make", | ||
335 | json_object_new_string(wlr_output->make)); | ||
336 | json_object_object_add(object, "model", | ||
337 | json_object_new_string(wlr_output->model)); | ||
338 | json_object_object_add(object, "serial", | ||
339 | json_object_new_string(wlr_output->serial)); | ||
340 | |||
341 | json_object *modes_array = json_object_new_array(); | ||
342 | struct wlr_output_mode *mode; | ||
343 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
344 | json_object *mode_object = json_object_new_object(); | ||
345 | json_object_object_add(mode_object, "width", | ||
346 | json_object_new_int(mode->width)); | ||
347 | json_object_object_add(mode_object, "height", | ||
348 | json_object_new_int(mode->height)); | ||
349 | json_object_object_add(mode_object, "refresh", | ||
350 | json_object_new_int(mode->refresh)); | ||
351 | json_object_array_add(modes_array, mode_object); | ||
352 | } | ||
353 | |||
354 | json_object_object_add(object, "modes", modes_array); | ||
355 | 417 | ||
356 | json_object_object_add(object, "current_workspace", NULL); | 418 | json_object_object_add(object, "current_workspace", NULL); |
357 | 419 | ||
@@ -367,6 +429,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { | |||
367 | return object; | 429 | return object; |
368 | } | 430 | } |
369 | 431 | ||
432 | json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *output) { | ||
433 | struct wlr_output *wlr_output = output->wlr_output; | ||
434 | |||
435 | json_object *object = json_object_new_object(); | ||
436 | |||
437 | ipc_json_describe_wlr_output(wlr_output, object); | ||
438 | |||
439 | json_object_object_add(object, "non_desktop", json_object_new_boolean(true)); | ||
440 | json_object_object_add(object, "type", json_object_new_string("output")); | ||
441 | json_object_object_add(object, "name", | ||
442 | json_object_new_string(wlr_output->name)); | ||
443 | |||
444 | return object; | ||
445 | } | ||
446 | |||
370 | static json_object *ipc_json_describe_scratchpad_output(void) { | 447 | static json_object *ipc_json_describe_scratchpad_output(void) { |
371 | struct wlr_box box; | 448 | struct wlr_box box; |
372 | root_get_box(root, &box); | 449 | root_get_box(root, &box); |
@@ -453,7 +530,9 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, | |||
453 | 530 | ||
454 | static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { | 531 | static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { |
455 | enum sway_container_layout parent_layout = container_parent_layout(c); | 532 | enum sway_container_layout parent_layout = container_parent_layout(c); |
456 | bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; | 533 | list_t *siblings = container_get_siblings(c); |
534 | bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED) | ||
535 | && ((siblings && siblings->length > 1) || !config->hide_lone_tab); | ||
457 | if (((!tab_or_stack || container_is_floating(c)) && | 536 | if (((!tab_or_stack || container_is_floating(c)) && |
458 | c->current.border != B_NORMAL) || | 537 | c->current.border != B_NORMAL) || |
459 | c->pending.fullscreen_mode != FULLSCREEN_NONE || | 538 | c->pending.fullscreen_mode != FULLSCREEN_NONE || |
@@ -500,7 +579,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
500 | 579 | ||
501 | struct wlr_box window_box = { | 580 | struct wlr_box window_box = { |
502 | c->pending.content_x - c->pending.x, | 581 | c->pending.content_x - c->pending.x, |
503 | (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, | 582 | (c->current.border == B_PIXEL) ? c->pending.content_y - c->pending.y : 0, |
504 | c->pending.content_width, | 583 | c->pending.content_width, |
505 | c->pending.content_height | 584 | c->pending.content_height |
506 | }; | 585 | }; |
@@ -544,6 +623,16 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
544 | 623 | ||
545 | json_object_object_add(object, "idle_inhibitors", idle_inhibitors); | 624 | json_object_object_add(object, "idle_inhibitors", idle_inhibitors); |
546 | 625 | ||
626 | enum wp_content_type_v1_type content_type = WP_CONTENT_TYPE_V1_TYPE_NONE; | ||
627 | if (c->view->surface != NULL) { | ||
628 | content_type = wlr_surface_get_content_type_v1(server.content_type_manager_v1, | ||
629 | c->view->surface); | ||
630 | } | ||
631 | if (content_type != WP_CONTENT_TYPE_V1_TYPE_NONE) { | ||
632 | json_object_object_add(object, "content_type", | ||
633 | json_object_new_string(ipc_json_content_type_description(content_type))); | ||
634 | } | ||
635 | |||
547 | #if HAVE_XWAYLAND | 636 | #if HAVE_XWAYLAND |
548 | if (c->view->type == SWAY_VIEW_XWAYLAND) { | 637 | if (c->view->type == SWAY_VIEW_XWAYLAND) { |
549 | json_object_object_add(object, "window", | 638 | json_object_object_add(object, "window", |
@@ -588,7 +677,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
588 | static void ipc_json_describe_container(struct sway_container *c, json_object *object) { | 677 | static void ipc_json_describe_container(struct sway_container *c, json_object *object) { |
589 | json_object_object_add(object, "name", | 678 | json_object_object_add(object, "name", |
590 | c->title ? json_object_new_string(c->title) : NULL); | 679 | c->title ? json_object_new_string(c->title) : NULL); |
591 | if (container_is_floating(c)) { | 680 | bool floating = container_is_floating(c); |
681 | if (floating) { | ||
592 | json_object_object_add(object, "type", | 682 | json_object_object_add(object, "type", |
593 | json_object_new_string("floating_con")); | 683 | json_object_new_string("floating_con")); |
594 | } | 684 | } |
@@ -606,9 +696,17 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o | |||
606 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); | 696 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); |
607 | json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); | 697 | json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); |
608 | 698 | ||
699 | // sway doesn't track the floating reason, so we can't use "auto_on" or "user_off" | ||
700 | json_object_object_add(object, "floating", | ||
701 | json_object_new_string(floating ? "user_on" : "auto_off")); | ||
702 | |||
609 | json_object_object_add(object, "fullscreen_mode", | 703 | json_object_object_add(object, "fullscreen_mode", |
610 | json_object_new_int(c->pending.fullscreen_mode)); | 704 | json_object_new_int(c->pending.fullscreen_mode)); |
611 | 705 | ||
706 | // sway doesn't track if window was resized in scratchpad, so we can't use "changed" | ||
707 | json_object_object_add(object, "scratchpad_state", | ||
708 | json_object_new_string(!c->scratchpad ? "none" : "fresh")); | ||
709 | |||
612 | struct sway_node *parent = node_get_parent(&c->node); | 710 | struct sway_node *parent = node_get_parent(&c->node); |
613 | struct wlr_box parent_box = {0, 0, 0, 0}; | 711 | struct wlr_box parent_box = {0, 0, 0, 0}; |
614 | 712 | ||
@@ -706,7 +804,7 @@ json_object *ipc_json_describe_node(struct sway_node *node) { | |||
706 | case N_ROOT: | 804 | case N_ROOT: |
707 | break; | 805 | break; |
708 | case N_OUTPUT: | 806 | case N_OUTPUT: |
709 | ipc_json_describe_output(node->sway_output, object); | 807 | ipc_json_describe_enabled_output(node->sway_output, object); |
710 | break; | 808 | break; |
711 | case N_CONTAINER: | 809 | case N_CONTAINER: |
712 | ipc_json_describe_container(node->sway_container, object); | 810 | ipc_json_describe_container(node->sway_container, object); |
@@ -764,6 +862,7 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { | |||
764 | return object; | 862 | return object; |
765 | } | 863 | } |
766 | 864 | ||
865 | #if WLR_HAS_LIBINPUT_BACKEND | ||
767 | static json_object *describe_libinput_device(struct libinput_device *device) { | 866 | static json_object *describe_libinput_device(struct libinput_device *device) { |
768 | json_object *object = json_object_new_object(); | 867 | json_object *object = json_object_new_object(); |
769 | 868 | ||
@@ -847,6 +946,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
847 | case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: | 946 | case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: |
848 | accel_profile = "adaptive"; | 947 | accel_profile = "adaptive"; |
849 | break; | 948 | break; |
949 | #if HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM | ||
950 | case LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM: | ||
951 | accel_profile = "custom"; | ||
952 | break; | ||
953 | #endif | ||
850 | } | 954 | } |
851 | json_object_object_add(object, "accel_profile", | 955 | json_object_object_add(object, "accel_profile", |
852 | json_object_new_string(accel_profile)); | 956 | json_object_new_string(accel_profile)); |
@@ -926,6 +1030,17 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
926 | uint32_t button = libinput_device_config_scroll_get_button(device); | 1030 | uint32_t button = libinput_device_config_scroll_get_button(device); |
927 | json_object_object_add(object, "scroll_button", | 1031 | json_object_object_add(object, "scroll_button", |
928 | json_object_new_int(button)); | 1032 | json_object_new_int(button)); |
1033 | const char *lock = "unknown"; | ||
1034 | switch (libinput_device_config_scroll_get_button_lock(device)) { | ||
1035 | case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED: | ||
1036 | lock = "enabled"; | ||
1037 | break; | ||
1038 | case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED: | ||
1039 | lock = "disabled"; | ||
1040 | break; | ||
1041 | } | ||
1042 | json_object_object_add(object, "scroll_button_lock", | ||
1043 | json_object_new_string(lock)); | ||
929 | } | 1044 | } |
930 | } | 1045 | } |
931 | 1046 | ||
@@ -942,6 +1057,19 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
942 | json_object_object_add(object, "dwt", json_object_new_string(dwt)); | 1057 | json_object_object_add(object, "dwt", json_object_new_string(dwt)); |
943 | } | 1058 | } |
944 | 1059 | ||
1060 | if (libinput_device_config_dwtp_is_available(device)) { | ||
1061 | const char *dwtp = "unknown"; | ||
1062 | switch (libinput_device_config_dwtp_get_enabled(device)) { | ||
1063 | case LIBINPUT_CONFIG_DWTP_ENABLED: | ||
1064 | dwtp = "enabled"; | ||
1065 | break; | ||
1066 | case LIBINPUT_CONFIG_DWTP_DISABLED: | ||
1067 | dwtp = "disabled"; | ||
1068 | break; | ||
1069 | } | ||
1070 | json_object_object_add(object, "dwtp", json_object_new_string(dwtp)); | ||
1071 | } | ||
1072 | |||
945 | if (libinput_device_config_calibration_has_matrix(device)) { | 1073 | if (libinput_device_config_calibration_has_matrix(device)) { |
946 | float matrix[6]; | 1074 | float matrix[6]; |
947 | libinput_device_config_calibration_get_matrix(device, matrix); | 1075 | libinput_device_config_calibration_get_matrix(device, matrix); |
@@ -956,6 +1084,7 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
956 | 1084 | ||
957 | return object; | 1085 | return object; |
958 | } | 1086 | } |
1087 | #endif | ||
959 | 1088 | ||
960 | json_object *ipc_json_describe_input(struct sway_input_device *device) { | 1089 | json_object *ipc_json_describe_input(struct sway_input_device *device) { |
961 | if (!(sway_assert(device, "Device must not be null"))) { | 1090 | if (!(sway_assert(device, "Device must not be null"))) { |
@@ -968,19 +1097,21 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { | |||
968 | json_object_new_string(device->identifier)); | 1097 | json_object_new_string(device->identifier)); |
969 | json_object_object_add(object, "name", | 1098 | json_object_object_add(object, "name", |
970 | json_object_new_string(device->wlr_device->name)); | 1099 | json_object_new_string(device->wlr_device->name)); |
971 | json_object_object_add(object, "vendor", | ||
972 | json_object_new_int(device->wlr_device->vendor)); | ||
973 | json_object_object_add(object, "product", | ||
974 | json_object_new_int(device->wlr_device->product)); | ||
975 | json_object_object_add(object, "type", | 1100 | json_object_object_add(object, "type", |
976 | json_object_new_string( | 1101 | json_object_new_string( |
977 | input_device_get_type(device))); | 1102 | input_device_get_type(device))); |
978 | 1103 | ||
979 | if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { | 1104 | if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { |
980 | struct wlr_keyboard *keyboard = device->wlr_device->keyboard; | 1105 | struct wlr_keyboard *keyboard = |
1106 | wlr_keyboard_from_input_device(device->wlr_device); | ||
981 | struct xkb_keymap *keymap = keyboard->keymap; | 1107 | struct xkb_keymap *keymap = keyboard->keymap; |
982 | struct xkb_state *state = keyboard->xkb_state; | 1108 | struct xkb_state *state = keyboard->xkb_state; |
983 | 1109 | ||
1110 | json_object_object_add(object, "repeat_delay", | ||
1111 | json_object_new_int(keyboard->repeat_info.delay)); | ||
1112 | json_object_object_add(object, "repeat_rate", | ||
1113 | json_object_new_int(keyboard->repeat_info.rate)); | ||
1114 | |||
984 | json_object *layouts_arr = json_object_new_array(); | 1115 | json_object *layouts_arr = json_object_new_array(); |
985 | json_object_object_add(object, "xkb_layout_names", layouts_arr); | 1116 | json_object_object_add(object, "xkb_layout_names", layouts_arr); |
986 | 1117 | ||
@@ -1005,20 +1136,26 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { | |||
1005 | if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { | 1136 | if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { |
1006 | struct input_config *ic = input_device_get_config(device); | 1137 | struct input_config *ic = input_device_get_config(device); |
1007 | float scroll_factor = 1.0f; | 1138 | float scroll_factor = 1.0f; |
1008 | if (ic != NULL && !isnan(ic->scroll_factor) && | 1139 | if (ic != NULL && !isnan(ic->scroll_factor) && |
1009 | ic->scroll_factor != FLT_MIN) { | 1140 | ic->scroll_factor != FLT_MIN) { |
1010 | scroll_factor = ic->scroll_factor; | 1141 | scroll_factor = ic->scroll_factor; |
1011 | } | 1142 | } |
1012 | json_object_object_add(object, "scroll_factor", | 1143 | json_object_object_add(object, "scroll_factor", |
1013 | json_object_new_double(scroll_factor)); | 1144 | json_object_new_double(scroll_factor)); |
1014 | } | 1145 | } |
1015 | 1146 | ||
1147 | #if WLR_HAS_LIBINPUT_BACKEND | ||
1016 | if (wlr_input_device_is_libinput(device->wlr_device)) { | 1148 | if (wlr_input_device_is_libinput(device->wlr_device)) { |
1017 | struct libinput_device *libinput_dev; | 1149 | struct libinput_device *libinput_dev; |
1018 | libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); | 1150 | libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); |
1019 | json_object_object_add(object, "libinput", | 1151 | json_object_object_add(object, "libinput", |
1020 | describe_libinput_device(libinput_dev)); | 1152 | describe_libinput_device(libinput_dev)); |
1153 | json_object_object_add(object, "vendor", | ||
1154 | json_object_new_int(libinput_device_get_id_vendor(libinput_dev))); | ||
1155 | json_object_object_add(object, "product", | ||
1156 | json_object_new_int(libinput_device_get_id_product(libinput_dev))); | ||
1021 | } | 1157 | } |
1158 | #endif | ||
1022 | 1159 | ||
1023 | return object; | 1160 | return object; |
1024 | } | 1161 | } |
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index aad9a7b5..7f353c0e 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -1,5 +1,4 @@ | |||
1 | // See https://i3wm.org/docs/ipc.html for protocol information | 1 | // See https://i3wm.org/docs/ipc.html for protocol information |
2 | #define _POSIX_C_SOURCE 200112L | ||
3 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
4 | #include <assert.h> | 3 | #include <assert.h> |
5 | #include <errno.h> | 4 | #include <errno.h> |
@@ -56,7 +55,6 @@ struct ipc_client { | |||
56 | enum ipc_command_type pending_type; | 55 | enum ipc_command_type pending_type; |
57 | }; | 56 | }; |
58 | 57 | ||
59 | struct sockaddr_un *ipc_user_sockaddr(void); | ||
60 | int ipc_handle_connection(int fd, uint32_t mask, void *data); | 58 | int ipc_handle_connection(int fd, uint32_t mask, void *data); |
61 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); | 59 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); |
62 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); | 60 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); |
@@ -150,7 +148,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) { | |||
150 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { | 148 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { |
151 | (void) fd; | 149 | (void) fd; |
152 | struct sway_server *server = data; | 150 | struct sway_server *server = data; |
153 | sway_log(SWAY_DEBUG, "Event on IPC listening socket"); | ||
154 | assert(mask == WL_EVENT_READABLE); | 151 | assert(mask == WL_EVENT_READABLE); |
155 | 152 | ||
156 | int client_fd = accept(ipc_socket, NULL, NULL); | 153 | int client_fd = accept(ipc_socket, NULL, NULL); |
@@ -211,13 +208,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
211 | } | 208 | } |
212 | 209 | ||
213 | if (mask & WL_EVENT_HANGUP) { | 210 | if (mask & WL_EVENT_HANGUP) { |
214 | sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); | ||
215 | ipc_client_disconnect(client); | 211 | ipc_client_disconnect(client); |
216 | return 0; | 212 | return 0; |
217 | } | 213 | } |
218 | 214 | ||
219 | sway_log(SWAY_DEBUG, "Client %d readable", client->fd); | ||
220 | |||
221 | int read_available; | 215 | int read_available; |
222 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { | 216 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { |
223 | sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); | 217 | sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); |
@@ -513,6 +507,20 @@ void ipc_event_input(const char *change, struct sway_input_device *device) { | |||
513 | json_object_put(json); | 507 | json_object_put(json); |
514 | } | 508 | } |
515 | 509 | ||
510 | void ipc_event_output(void) { | ||
511 | if (!ipc_has_event_listeners(IPC_EVENT_OUTPUT)) { | ||
512 | return; | ||
513 | } | ||
514 | sway_log(SWAY_DEBUG, "Sending output event"); | ||
515 | |||
516 | json_object *json = json_object_new_object(); | ||
517 | json_object_object_add(json, "change", json_object_new_string("unspecified")); | ||
518 | |||
519 | const char *json_string = json_object_to_json_string(json); | ||
520 | ipc_send_event(json_string, IPC_EVENT_OUTPUT); | ||
521 | json_object_put(json); | ||
522 | } | ||
523 | |||
516 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | 524 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { |
517 | struct ipc_client *client = data; | 525 | struct ipc_client *client = data; |
518 | 526 | ||
@@ -523,7 +531,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
523 | } | 531 | } |
524 | 532 | ||
525 | if (mask & WL_EVENT_HANGUP) { | 533 | if (mask & WL_EVENT_HANGUP) { |
526 | sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); | ||
527 | ipc_client_disconnect(client); | 534 | ipc_client_disconnect(client); |
528 | return 0; | 535 | return 0; |
529 | } | 536 | } |
@@ -532,8 +539,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
532 | return 0; | 539 | return 0; |
533 | } | 540 | } |
534 | 541 | ||
535 | sway_log(SWAY_DEBUG, "Client %d writable", client->fd); | ||
536 | |||
537 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); | 542 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); |
538 | 543 | ||
539 | if (written == -1 && errno == EAGAIN) { | 544 | if (written == -1 && errno == EAGAIN) { |
@@ -687,11 +692,17 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
687 | } | 692 | } |
688 | struct sway_output *output; | 693 | struct sway_output *output; |
689 | wl_list_for_each(output, &root->all_outputs, link) { | 694 | wl_list_for_each(output, &root->all_outputs, link) { |
690 | if (!output->enabled && output != root->noop_output) { | 695 | if (!output->enabled && output != root->fallback_output) { |
691 | json_object_array_add(outputs, | 696 | json_object_array_add(outputs, |
692 | ipc_json_describe_disabled_output(output)); | 697 | ipc_json_describe_disabled_output(output)); |
693 | } | 698 | } |
694 | } | 699 | } |
700 | |||
701 | for (int i = 0; i < root->non_desktop_outputs->length; i++) { | ||
702 | struct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i]; | ||
703 | json_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output)); | ||
704 | } | ||
705 | |||
695 | const char *json_string = json_object_to_json_string(outputs); | 706 | const char *json_string = json_object_to_json_string(outputs); |
696 | ipc_send_reply(client, payload_type, json_string, | 707 | ipc_send_reply(client, payload_type, json_string, |
697 | (uint32_t)strlen(json_string)); | 708 | (uint32_t)strlen(json_string)); |
@@ -727,6 +738,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
727 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); | 738 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); |
728 | if (strcmp(event_type, "workspace") == 0) { | 739 | if (strcmp(event_type, "workspace") == 0) { |
729 | client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); | 740 | client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); |
741 | } else if (strcmp(event_type, "output") == 0) { | ||
742 | client->subscribed_events |= event_mask(IPC_EVENT_OUTPUT); | ||
730 | } else if (strcmp(event_type, "barconfig_update") == 0) { | 743 | } else if (strcmp(event_type, "barconfig_update") == 0) { |
731 | client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); | 744 | client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); |
732 | } else if (strcmp(event_type, "bar_state_update") == 0) { | 745 | } else if (strcmp(event_type, "bar_state_update") == 0) { |
@@ -911,7 +924,6 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
911 | 924 | ||
912 | exit_cleanup: | 925 | exit_cleanup: |
913 | free(buf); | 926 | free(buf); |
914 | return; | ||
915 | } | 927 | } |
916 | 928 | ||
917 | bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, | 929 | bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, |
@@ -955,7 +967,5 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ | |||
955 | ipc_client_handle_writable, client); | 967 | ipc_client_handle_writable, client); |
956 | } | 968 | } |
957 | 969 | ||
958 | sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s", | ||
959 | payload_type, client->fd, payload); | ||
960 | return true; | 970 | return true; |
961 | } | 971 | } |
diff --git a/sway/lock.c b/sway/lock.c new file mode 100644 index 00000000..289e8ca4 --- /dev/null +++ b/sway/lock.c | |||
@@ -0,0 +1,354 @@ | |||
1 | #include <assert.h> | ||
2 | #include <wlr/types/wlr_scene.h> | ||
3 | #include <wlr/types/wlr_session_lock_v1.h> | ||
4 | #include "log.h" | ||
5 | #include "sway/input/cursor.h" | ||
6 | #include "sway/input/keyboard.h" | ||
7 | #include "sway/input/seat.h" | ||
8 | #include "sway/layers.h" | ||
9 | #include "sway/output.h" | ||
10 | #include "sway/server.h" | ||
11 | |||
12 | struct sway_session_lock_output { | ||
13 | struct wlr_scene_tree *tree; | ||
14 | struct wlr_scene_rect *background; | ||
15 | struct sway_session_lock *lock; | ||
16 | |||
17 | struct sway_output *output; | ||
18 | |||
19 | struct wl_list link; // sway_session_lock::outputs | ||
20 | |||
21 | struct wl_listener destroy; | ||
22 | struct wl_listener commit; | ||
23 | |||
24 | struct wlr_session_lock_surface_v1 *surface; | ||
25 | |||
26 | // invalid if surface is NULL | ||
27 | struct wl_listener surface_destroy; | ||
28 | struct wl_listener surface_map; | ||
29 | }; | ||
30 | |||
31 | static void focus_surface(struct sway_session_lock *lock, | ||
32 | struct wlr_surface *focused) { | ||
33 | lock->focused = focused; | ||
34 | |||
35 | struct sway_seat *seat; | ||
36 | wl_list_for_each(seat, &server.input->seats, link) { | ||
37 | seat_set_focus_surface(seat, focused, false); | ||
38 | } | ||
39 | } | ||
40 | |||
41 | static void refocus_output(struct sway_session_lock_output *output) { | ||
42 | // Move the seat focus to another surface if one is available | ||
43 | if (output->lock->focused == output->surface->surface) { | ||
44 | struct wlr_surface *next_focus = NULL; | ||
45 | |||
46 | struct sway_session_lock_output *candidate; | ||
47 | wl_list_for_each(candidate, &output->lock->outputs, link) { | ||
48 | if (candidate == output || !candidate->surface) { | ||
49 | continue; | ||
50 | } | ||
51 | |||
52 | if (candidate->surface->surface->mapped) { | ||
53 | next_focus = candidate->surface->surface; | ||
54 | break; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | focus_surface(output->lock, next_focus); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | static void handle_surface_map(struct wl_listener *listener, void *data) { | ||
63 | struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map); | ||
64 | if (surf->lock->focused == NULL) { | ||
65 | focus_surface(surf->lock, surf->surface->surface); | ||
66 | } | ||
67 | cursor_rebase_all(); | ||
68 | } | ||
69 | |||
70 | static void handle_surface_destroy(struct wl_listener *listener, void *data) { | ||
71 | struct sway_session_lock_output *output = | ||
72 | wl_container_of(listener, output, surface_destroy); | ||
73 | refocus_output(output); | ||
74 | |||
75 | sway_assert(output->surface, "Trying to destroy a surface that the lock doesn't think exists"); | ||
76 | output->surface = NULL; | ||
77 | wl_list_remove(&output->surface_destroy.link); | ||
78 | wl_list_remove(&output->surface_map.link); | ||
79 | } | ||
80 | |||
81 | static void lock_output_reconfigure(struct sway_session_lock_output *output) { | ||
82 | int width = output->output->width; | ||
83 | int height = output->output->height; | ||
84 | |||
85 | wlr_scene_rect_set_size(output->background, width, height); | ||
86 | |||
87 | if (output->surface) { | ||
88 | wlr_session_lock_surface_v1_configure(output->surface, width, height); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | static void handle_new_surface(struct wl_listener *listener, void *data) { | ||
93 | struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface); | ||
94 | struct wlr_session_lock_surface_v1 *lock_surface = data; | ||
95 | struct sway_output *output = lock_surface->output->data; | ||
96 | |||
97 | sway_log(SWAY_DEBUG, "new lock layer surface"); | ||
98 | |||
99 | struct sway_session_lock_output *current_lock_output, *lock_output = NULL; | ||
100 | wl_list_for_each(current_lock_output, &lock->outputs, link) { | ||
101 | if (current_lock_output->output == output) { | ||
102 | lock_output = current_lock_output; | ||
103 | break; | ||
104 | } | ||
105 | } | ||
106 | sway_assert(lock_output, "Couldn't find output to lock"); | ||
107 | sway_assert(!lock_output->surface, "Tried to reassign a surface to an existing output"); | ||
108 | |||
109 | lock_output->surface = lock_surface; | ||
110 | |||
111 | wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface); | ||
112 | |||
113 | lock_output->surface_destroy.notify = handle_surface_destroy; | ||
114 | wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy); | ||
115 | lock_output->surface_map.notify = handle_surface_map; | ||
116 | wl_signal_add(&lock_surface->surface->events.map, &lock_output->surface_map); | ||
117 | |||
118 | lock_output_reconfigure(lock_output); | ||
119 | } | ||
120 | |||
121 | static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) { | ||
122 | if (output->surface) { | ||
123 | refocus_output(output); | ||
124 | wl_list_remove(&output->surface_destroy.link); | ||
125 | wl_list_remove(&output->surface_map.link); | ||
126 | } | ||
127 | |||
128 | wl_list_remove(&output->commit.link); | ||
129 | wl_list_remove(&output->destroy.link); | ||
130 | wl_list_remove(&output->link); | ||
131 | |||
132 | free(output); | ||
133 | } | ||
134 | |||
135 | static void lock_node_handle_destroy(struct wl_listener *listener, void *data) { | ||
136 | struct sway_session_lock_output *output = | ||
137 | wl_container_of(listener, output, destroy); | ||
138 | sway_session_lock_output_destroy(output); | ||
139 | } | ||
140 | |||
141 | static void lock_output_handle_commit(struct wl_listener *listener, void *data) { | ||
142 | struct wlr_output_event_commit *event = data; | ||
143 | struct sway_session_lock_output *output = | ||
144 | wl_container_of(listener, output, commit); | ||
145 | if (event->state->committed & ( | ||
146 | WLR_OUTPUT_STATE_MODE | | ||
147 | WLR_OUTPUT_STATE_SCALE | | ||
148 | WLR_OUTPUT_STATE_TRANSFORM)) { | ||
149 | lock_output_reconfigure(output); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | static struct sway_session_lock_output *session_lock_output_create( | ||
154 | struct sway_session_lock *lock, struct sway_output *output) { | ||
155 | struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output)); | ||
156 | if (!lock_output) { | ||
157 | sway_log(SWAY_ERROR, "failed to allocate a session lock output"); | ||
158 | return NULL; | ||
159 | } | ||
160 | |||
161 | struct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock); | ||
162 | if (!tree) { | ||
163 | sway_log(SWAY_ERROR, "failed to allocate a session lock output scene tree"); | ||
164 | free(lock_output); | ||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){ | ||
169 | lock->abandoned ? 1.f : 0.f, | ||
170 | 0.f, | ||
171 | 0.f, | ||
172 | 1.f, | ||
173 | }); | ||
174 | if (!background) { | ||
175 | sway_log(SWAY_ERROR, "failed to allocate a session lock output scene background"); | ||
176 | wlr_scene_node_destroy(&tree->node); | ||
177 | free(lock_output); | ||
178 | return NULL; | ||
179 | } | ||
180 | |||
181 | lock_output->output = output; | ||
182 | lock_output->tree = tree; | ||
183 | lock_output->background = background; | ||
184 | lock_output->lock = lock; | ||
185 | |||
186 | lock_output->destroy.notify = lock_node_handle_destroy; | ||
187 | wl_signal_add(&tree->node.events.destroy, &lock_output->destroy); | ||
188 | |||
189 | lock_output->commit.notify = lock_output_handle_commit; | ||
190 | wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit); | ||
191 | |||
192 | lock_output_reconfigure(lock_output); | ||
193 | |||
194 | wl_list_insert(&lock->outputs, &lock_output->link); | ||
195 | |||
196 | return lock_output; | ||
197 | } | ||
198 | |||
199 | static void sway_session_lock_destroy(struct sway_session_lock* lock) { | ||
200 | struct sway_session_lock_output *lock_output, *tmp_lock_output; | ||
201 | wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) { | ||
202 | // destroying the node will also destroy the whole lock output | ||
203 | wlr_scene_node_destroy(&lock_output->tree->node); | ||
204 | } | ||
205 | |||
206 | if (server.session_lock.lock == lock) { | ||
207 | server.session_lock.lock = NULL; | ||
208 | } | ||
209 | |||
210 | if (!lock->abandoned) { | ||
211 | wl_list_remove(&lock->destroy.link); | ||
212 | wl_list_remove(&lock->unlock.link); | ||
213 | wl_list_remove(&lock->new_surface.link); | ||
214 | } | ||
215 | |||
216 | free(lock); | ||
217 | } | ||
218 | |||
219 | static void handle_unlock(struct wl_listener *listener, void *data) { | ||
220 | struct sway_session_lock *lock = wl_container_of(listener, lock, unlock); | ||
221 | sway_log(SWAY_DEBUG, "session unlocked"); | ||
222 | |||
223 | sway_session_lock_destroy(lock); | ||
224 | |||
225 | struct sway_seat *seat; | ||
226 | wl_list_for_each(seat, &server.input->seats, link) { | ||
227 | // copied from seat_set_focus_layer -- deduplicate? | ||
228 | struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); | ||
229 | if (previous) { | ||
230 | // Hack to get seat to re-focus the return value of get_focus | ||
231 | seat_set_focus(seat, NULL); | ||
232 | seat_set_focus(seat, previous); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | // Triggers a refocus of the topmost surface layer if necessary | ||
237 | // TODO: Make layer surface focus per-output based on cursor position | ||
238 | for (int i = 0; i < root->outputs->length; ++i) { | ||
239 | struct sway_output *output = root->outputs->items[i]; | ||
240 | arrange_layers(output); | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static void handle_abandon(struct wl_listener *listener, void *data) { | ||
245 | struct sway_session_lock *lock = wl_container_of(listener, lock, destroy); | ||
246 | sway_log(SWAY_INFO, "session lock abandoned"); | ||
247 | |||
248 | struct sway_session_lock_output *lock_output; | ||
249 | wl_list_for_each(lock_output, &lock->outputs, link) { | ||
250 | wlr_scene_rect_set_color(lock_output->background, | ||
251 | (float[4]){ 1.f, 0.f, 0.f, 1.f }); | ||
252 | } | ||
253 | |||
254 | lock->abandoned = true; | ||
255 | wl_list_remove(&lock->destroy.link); | ||
256 | wl_list_remove(&lock->unlock.link); | ||
257 | wl_list_remove(&lock->new_surface.link); | ||
258 | } | ||
259 | |||
260 | static void handle_session_lock(struct wl_listener *listener, void *data) { | ||
261 | struct wlr_session_lock_v1 *lock = data; | ||
262 | struct wl_client *client = wl_resource_get_client(lock->resource); | ||
263 | |||
264 | if (server.session_lock.lock) { | ||
265 | if (server.session_lock.lock->abandoned) { | ||
266 | sway_log(SWAY_INFO, "Replacing abandoned lock"); | ||
267 | sway_session_lock_destroy(server.session_lock.lock); | ||
268 | } else { | ||
269 | sway_log(SWAY_ERROR, "Cannot lock an already locked session"); | ||
270 | wlr_session_lock_v1_destroy(lock); | ||
271 | return; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | struct sway_session_lock *sway_lock = calloc(1, sizeof(*sway_lock)); | ||
276 | if (!sway_lock) { | ||
277 | sway_log(SWAY_ERROR, "failed to allocate a session lock object"); | ||
278 | wlr_session_lock_v1_destroy(lock); | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | wl_list_init(&sway_lock->outputs); | ||
283 | |||
284 | sway_log(SWAY_DEBUG, "session locked"); | ||
285 | |||
286 | struct sway_seat *seat; | ||
287 | wl_list_for_each(seat, &server.input->seats, link) { | ||
288 | seat_unfocus_unless_client(seat, client); | ||
289 | } | ||
290 | |||
291 | struct sway_output *output; | ||
292 | wl_list_for_each(output, &root->all_outputs, link) { | ||
293 | sway_session_lock_add_output(sway_lock, output); | ||
294 | } | ||
295 | |||
296 | sway_lock->new_surface.notify = handle_new_surface; | ||
297 | wl_signal_add(&lock->events.new_surface, &sway_lock->new_surface); | ||
298 | sway_lock->unlock.notify = handle_unlock; | ||
299 | wl_signal_add(&lock->events.unlock, &sway_lock->unlock); | ||
300 | sway_lock->destroy.notify = handle_abandon; | ||
301 | wl_signal_add(&lock->events.destroy, &sway_lock->destroy); | ||
302 | |||
303 | wlr_session_lock_v1_send_locked(lock); | ||
304 | server.session_lock.lock = sway_lock; | ||
305 | } | ||
306 | |||
307 | static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { | ||
308 | // if the server shuts down while a lock is active, destroy the lock | ||
309 | if (server.session_lock.lock) { | ||
310 | sway_session_lock_destroy(server.session_lock.lock); | ||
311 | } | ||
312 | |||
313 | wl_list_remove(&server.session_lock.new_lock.link); | ||
314 | wl_list_remove(&server.session_lock.manager_destroy.link); | ||
315 | |||
316 | server.session_lock.manager = NULL; | ||
317 | } | ||
318 | |||
319 | void sway_session_lock_add_output(struct sway_session_lock *lock, | ||
320 | struct sway_output *output) { | ||
321 | struct sway_session_lock_output *lock_output = | ||
322 | session_lock_output_create(lock, output); | ||
323 | |||
324 | // if we run out of memory while trying to lock the screen, the best we | ||
325 | // can do is kill the sway process. Security conscious users will have | ||
326 | // the sway session fall back to a login shell. | ||
327 | if (!lock_output) { | ||
328 | sway_log(SWAY_ERROR, "aborting: failed to allocate a lock output"); | ||
329 | abort(); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | bool sway_session_lock_has_surface(struct sway_session_lock *lock, | ||
334 | struct wlr_surface *surface) { | ||
335 | struct sway_session_lock_output *lock_output; | ||
336 | wl_list_for_each(lock_output, &lock->outputs, link) { | ||
337 | if (lock_output->surface && lock_output->surface->surface == surface) { | ||
338 | return true; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | return false; | ||
343 | } | ||
344 | |||
345 | void sway_session_lock_init(void) { | ||
346 | server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); | ||
347 | |||
348 | server.session_lock.new_lock.notify = handle_session_lock; | ||
349 | server.session_lock.manager_destroy.notify = handle_session_lock_destroy; | ||
350 | wl_signal_add(&server.session_lock.manager->events.new_lock, | ||
351 | &server.session_lock.new_lock); | ||
352 | wl_signal_add(&server.session_lock.manager->events.destroy, | ||
353 | &server.session_lock.manager_destroy); | ||
354 | } | ||
diff --git a/sway/main.c b/sway/main.c index 0611e80b..1c4939aa 100644 --- a/sway/main.c +++ b/sway/main.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <getopt.h> | 1 | #include <getopt.h> |
3 | #include <pango/pangocairo.h> | 2 | #include <pango/pangocairo.h> |
4 | #include <signal.h> | 3 | #include <signal.h> |
@@ -6,6 +5,7 @@ | |||
6 | #include <stdio.h> | 5 | #include <stdio.h> |
7 | #include <stdlib.h> | 6 | #include <stdlib.h> |
8 | #include <string.h> | 7 | #include <string.h> |
8 | #include <sys/resource.h> | ||
9 | #include <sys/stat.h> | 9 | #include <sys/stat.h> |
10 | #include <sys/types.h> | 10 | #include <sys/types.h> |
11 | #include <sys/wait.h> | 11 | #include <sys/wait.h> |
@@ -27,6 +27,7 @@ | |||
27 | 27 | ||
28 | static bool terminate_request = false; | 28 | static bool terminate_request = false; |
29 | static int exit_value = 0; | 29 | static int exit_value = 0; |
30 | static struct rlimit original_nofile_rlimit = {0}; | ||
30 | struct sway_server server = {0}; | 31 | struct sway_server server = {0}; |
31 | struct sway_debug debug = {0}; | 32 | struct sway_debug debug = {0}; |
32 | 33 | ||
@@ -47,44 +48,6 @@ void sig_handler(int signal) { | |||
47 | sway_terminate(EXIT_SUCCESS); | 48 | sway_terminate(EXIT_SUCCESS); |
48 | } | 49 | } |
49 | 50 | ||
50 | void detect_proprietary(int allow_unsupported_gpu) { | ||
51 | FILE *f = fopen("/proc/modules", "r"); | ||
52 | if (!f) { | ||
53 | return; | ||
54 | } | ||
55 | char *line = NULL; | ||
56 | size_t line_size = 0; | ||
57 | while (getline(&line, &line_size, f) != -1) { | ||
58 | if (strncmp(line, "nvidia ", 7) == 0) { | ||
59 | if (allow_unsupported_gpu) { | ||
60 | sway_log(SWAY_ERROR, | ||
61 | "!!! Proprietary Nvidia drivers are in use !!!"); | ||
62 | } else { | ||
63 | sway_log(SWAY_ERROR, | ||
64 | "Proprietary Nvidia drivers are NOT supported. " | ||
65 | "Use Nouveau. To launch sway anyway, launch with " | ||
66 | "--my-next-gpu-wont-be-nvidia and DO NOT report issues."); | ||
67 | exit(EXIT_FAILURE); | ||
68 | } | ||
69 | break; | ||
70 | } | ||
71 | if (strstr(line, "fglrx")) { | ||
72 | if (allow_unsupported_gpu) { | ||
73 | sway_log(SWAY_ERROR, | ||
74 | "!!! Proprietary AMD drivers are in use !!!"); | ||
75 | } else { | ||
76 | sway_log(SWAY_ERROR, "Proprietary AMD drivers do NOT support " | ||
77 | "Wayland. Use radeon. To try anyway, launch sway with " | ||
78 | "--unsupported-gpu and DO NOT report issues."); | ||
79 | exit(EXIT_FAILURE); | ||
80 | } | ||
81 | break; | ||
82 | } | ||
83 | } | ||
84 | free(line); | ||
85 | fclose(f); | ||
86 | } | ||
87 | |||
88 | void run_as_ipc_client(char *command, char *socket_path) { | 51 | void run_as_ipc_client(char *command, char *socket_path) { |
89 | int socketfd = ipc_open_socket(socket_path); | 52 | int socketfd = ipc_open_socket(socket_path); |
90 | uint32_t len = strlen(command); | 53 | uint32_t len = strlen(command); |
@@ -148,33 +111,49 @@ static void log_kernel(void) { | |||
148 | pclose(f); | 111 | pclose(f); |
149 | } | 112 | } |
150 | 113 | ||
151 | 114 | static bool detect_suid(void) { | |
152 | static bool drop_permissions(void) { | 115 | if (geteuid() != 0 && getegid() != 0) { |
153 | if (getuid() != geteuid() || getgid() != getegid()) { | 116 | return false; |
154 | // Set the gid and uid in the correct order. | ||
155 | if (setgid(getgid()) != 0) { | ||
156 | sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start"); | ||
157 | return false; | ||
158 | } | ||
159 | if (setuid(getuid()) != 0) { | ||
160 | sway_log(SWAY_ERROR, "Unable to drop root user, refusing to start"); | ||
161 | return false; | ||
162 | } | ||
163 | } | 117 | } |
164 | if (setgid(0) != -1 || setuid(0) != -1) { | 118 | |
165 | sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to " | 119 | if (getuid() == geteuid() && getgid() == getegid()) { |
166 | "restore it after setuid), refusing to start"); | ||
167 | return false; | 120 | return false; |
168 | } | 121 | } |
122 | |||
123 | sway_log(SWAY_ERROR, "SUID operation is no longer supported, refusing to start. " | ||
124 | "This check will be removed in a future release."); | ||
169 | return true; | 125 | return true; |
170 | } | 126 | } |
171 | 127 | ||
128 | static void increase_nofile_limit(void) { | ||
129 | if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { | ||
130 | sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " | ||
131 | "getrlimit(NOFILE) failed"); | ||
132 | return; | ||
133 | } | ||
134 | |||
135 | struct rlimit new_rlimit = original_nofile_rlimit; | ||
136 | new_rlimit.rlim_cur = new_rlimit.rlim_max; | ||
137 | if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) { | ||
138 | sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " | ||
139 | "setrlimit(NOFILE) failed"); | ||
140 | sway_log(SWAY_INFO, "Running with %d max open files", | ||
141 | (int)original_nofile_rlimit.rlim_cur); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | void restore_nofile_limit(void) { | ||
146 | if (original_nofile_rlimit.rlim_cur == 0) { | ||
147 | return; | ||
148 | } | ||
149 | if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { | ||
150 | sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: " | ||
151 | "setrlimit(NOFILE) failed"); | ||
152 | } | ||
153 | } | ||
154 | |||
172 | void enable_debug_flag(const char *flag) { | 155 | void enable_debug_flag(const char *flag) { |
173 | if (strcmp(flag, "damage=highlight") == 0) { | 156 | if (strcmp(flag, "noatomic") == 0) { |
174 | debug.damage = DAMAGE_HIGHLIGHT; | ||
175 | } else if (strcmp(flag, "damage=rerender") == 0) { | ||
176 | debug.damage = DAMAGE_RERENDER; | ||
177 | } else if (strcmp(flag, "noatomic") == 0) { | ||
178 | debug.noatomic = true; | 157 | debug.noatomic = true; |
179 | } else if (strcmp(flag, "txn-wait") == 0) { | 158 | } else if (strcmp(flag, "txn-wait") == 0) { |
180 | debug.txn_wait = true; | 159 | debug.txn_wait = true; |
@@ -182,6 +161,8 @@ void enable_debug_flag(const char *flag) { | |||
182 | debug.txn_timings = true; | 161 | debug.txn_timings = true; |
183 | } else if (strncmp(flag, "txn-timeout=", 12) == 0) { | 162 | } else if (strncmp(flag, "txn-timeout=", 12) == 0) { |
184 | server.txn_timeout_ms = atoi(&flag[12]); | 163 | server.txn_timeout_ms = atoi(&flag[12]); |
164 | } else if (strcmp(flag, "legacy-wl-drm") == 0) { | ||
165 | debug.legacy_wl_drm = true; | ||
185 | } else { | 166 | } else { |
186 | sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); | 167 | sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); |
187 | } | 168 | } |
@@ -206,36 +187,35 @@ static void handle_wlr_log(enum wlr_log_importance importance, | |||
206 | _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); | 187 | _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); |
207 | } | 188 | } |
208 | 189 | ||
190 | static const struct option long_options[] = { | ||
191 | {"help", no_argument, NULL, 'h'}, | ||
192 | {"config", required_argument, NULL, 'c'}, | ||
193 | {"validate", no_argument, NULL, 'C'}, | ||
194 | {"debug", no_argument, NULL, 'd'}, | ||
195 | {"version", no_argument, NULL, 'v'}, | ||
196 | {"verbose", no_argument, NULL, 'V'}, | ||
197 | {"get-socketpath", no_argument, NULL, 'p'}, | ||
198 | {"unsupported-gpu", no_argument, NULL, 'u'}, | ||
199 | {0, 0, 0, 0} | ||
200 | }; | ||
201 | |||
202 | static const char usage[] = | ||
203 | "Usage: sway [options] [command]\n" | ||
204 | "\n" | ||
205 | " -h, --help Show help message and quit.\n" | ||
206 | " -c, --config <config> Specify a config file.\n" | ||
207 | " -C, --validate Check the validity of the config file, then exit.\n" | ||
208 | " -d, --debug Enables full logging, including debug information.\n" | ||
209 | " -v, --version Show the version number and quit.\n" | ||
210 | " -V, --verbose Enables more verbose logging.\n" | ||
211 | " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" | ||
212 | "\n"; | ||
213 | |||
209 | int main(int argc, char **argv) { | 214 | int main(int argc, char **argv) { |
210 | static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; | 215 | static bool verbose = false, debug = false, validate = false; |
211 | |||
212 | static const struct option long_options[] = { | ||
213 | {"help", no_argument, NULL, 'h'}, | ||
214 | {"config", required_argument, NULL, 'c'}, | ||
215 | {"validate", no_argument, NULL, 'C'}, | ||
216 | {"debug", no_argument, NULL, 'd'}, | ||
217 | {"version", no_argument, NULL, 'v'}, | ||
218 | {"verbose", no_argument, NULL, 'V'}, | ||
219 | {"get-socketpath", no_argument, NULL, 'p'}, | ||
220 | {"unsupported-gpu", no_argument, NULL, 'u'}, | ||
221 | {"my-next-gpu-wont-be-nvidia", no_argument, NULL, 'u'}, | ||
222 | {0, 0, 0, 0} | ||
223 | }; | ||
224 | 216 | ||
225 | char *config_path = NULL; | 217 | char *config_path = NULL; |
226 | 218 | ||
227 | const char* usage = | ||
228 | "Usage: sway [options] [command]\n" | ||
229 | "\n" | ||
230 | " -h, --help Show help message and quit.\n" | ||
231 | " -c, --config <config> Specify a config file.\n" | ||
232 | " -C, --validate Check the validity of the config file, then exit.\n" | ||
233 | " -d, --debug Enables full logging, including debug information.\n" | ||
234 | " -v, --version Show the version number and quit.\n" | ||
235 | " -V, --verbose Enables more verbose logging.\n" | ||
236 | " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" | ||
237 | "\n"; | ||
238 | |||
239 | int c; | 219 | int c; |
240 | while (1) { | 220 | while (1) { |
241 | int option_index = 0; | 221 | int option_index = 0; |
@@ -253,25 +233,25 @@ int main(int argc, char **argv) { | |||
253 | config_path = strdup(optarg); | 233 | config_path = strdup(optarg); |
254 | break; | 234 | break; |
255 | case 'C': // validate | 235 | case 'C': // validate |
256 | validate = 1; | 236 | validate = true; |
257 | break; | 237 | break; |
258 | case 'd': // debug | 238 | case 'd': // debug |
259 | debug = 1; | 239 | debug = true; |
260 | break; | 240 | break; |
261 | case 'D': // extended debug options | 241 | case 'D': // extended debug options |
262 | enable_debug_flag(optarg); | 242 | enable_debug_flag(optarg); |
263 | break; | 243 | break; |
264 | case 'u': | 244 | case 'u': |
265 | allow_unsupported_gpu = 1; | 245 | allow_unsupported_gpu = true; |
266 | break; | 246 | break; |
267 | case 'v': // version | 247 | case 'v': // version |
268 | printf("sway version " SWAY_VERSION "\n"); | 248 | printf("sway version " SWAY_VERSION "\n"); |
269 | exit(EXIT_SUCCESS); | 249 | exit(EXIT_SUCCESS); |
270 | break; | 250 | break; |
271 | case 'V': // verbose | 251 | case 'V': // verbose |
272 | verbose = 1; | 252 | verbose = true; |
273 | break; | 253 | break; |
274 | case 'p': ; // --get-socketpath | 254 | case 'p': // --get-socketpath |
275 | if (getenv("SWAYSOCK")) { | 255 | if (getenv("SWAYSOCK")) { |
276 | printf("%s\n", getenv("SWAYSOCK")); | 256 | printf("%s\n", getenv("SWAYSOCK")); |
277 | exit(EXIT_SUCCESS); | 257 | exit(EXIT_SUCCESS); |
@@ -286,6 +266,11 @@ int main(int argc, char **argv) { | |||
286 | } | 266 | } |
287 | } | 267 | } |
288 | 268 | ||
269 | // SUID operation is deprecated, so block it for now. | ||
270 | if (detect_suid()) { | ||
271 | exit(EXIT_FAILURE); | ||
272 | } | ||
273 | |||
289 | // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the | 274 | // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the |
290 | // clear error message (when not running as an IPC client). | 275 | // clear error message (when not running as an IPC client). |
291 | if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { | 276 | if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { |
@@ -312,7 +297,6 @@ int main(int argc, char **argv) { | |||
312 | log_kernel(); | 297 | log_kernel(); |
313 | log_distro(); | 298 | log_distro(); |
314 | log_env(); | 299 | log_env(); |
315 | detect_proprietary(allow_unsupported_gpu); | ||
316 | 300 | ||
317 | if (optind < argc) { // Behave as IPC client | 301 | if (optind < argc) { // Behave as IPC client |
318 | if (optind != 1) { | 302 | if (optind != 1) { |
@@ -325,9 +309,6 @@ int main(int argc, char **argv) { | |||
325 | "`sway -d 2>sway.log`."); | 309 | "`sway -d 2>sway.log`."); |
326 | exit(EXIT_FAILURE); | 310 | exit(EXIT_FAILURE); |
327 | } | 311 | } |
328 | if (!drop_permissions()) { | ||
329 | exit(EXIT_FAILURE); | ||
330 | } | ||
331 | char *socket_path = getenv("SWAYSOCK"); | 312 | char *socket_path = getenv("SWAYSOCK"); |
332 | if (!socket_path) { | 313 | if (!socket_path) { |
333 | sway_log(SWAY_ERROR, "Unable to retrieve socket path"); | 314 | sway_log(SWAY_ERROR, "Unable to retrieve socket path"); |
@@ -339,14 +320,7 @@ int main(int argc, char **argv) { | |||
339 | return 0; | 320 | return 0; |
340 | } | 321 | } |
341 | 322 | ||
342 | if (!server_privileged_prepare(&server)) { | 323 | increase_nofile_limit(); |
343 | return 1; | ||
344 | } | ||
345 | |||
346 | if (!drop_permissions()) { | ||
347 | server_fini(&server); | ||
348 | exit(EXIT_FAILURE); | ||
349 | } | ||
350 | 324 | ||
351 | // handle SIGTERM signals | 325 | // handle SIGTERM signals |
352 | signal(SIGTERM, sig_handler); | 326 | signal(SIGTERM, sig_handler); |
@@ -357,12 +331,14 @@ int main(int argc, char **argv) { | |||
357 | 331 | ||
358 | sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); | 332 | sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); |
359 | 333 | ||
360 | root = root_create(); | ||
361 | |||
362 | if (!server_init(&server)) { | 334 | if (!server_init(&server)) { |
363 | return 1; | 335 | return 1; |
364 | } | 336 | } |
365 | 337 | ||
338 | if (server.linux_dmabuf_v1) { | ||
339 | wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1); | ||
340 | } | ||
341 | |||
366 | if (validate) { | 342 | if (validate) { |
367 | bool valid = load_main_config(config_path, false, true); | 343 | bool valid = load_main_config(config_path, false, true); |
368 | free(config_path); | 344 | free(config_path); |
@@ -377,6 +353,8 @@ int main(int argc, char **argv) { | |||
377 | goto shutdown; | 353 | goto shutdown; |
378 | } | 354 | } |
379 | 355 | ||
356 | set_rr_scheduling(); | ||
357 | |||
380 | if (!server_start(&server)) { | 358 | if (!server_start(&server)) { |
381 | sway_terminate(EXIT_FAILURE); | 359 | sway_terminate(EXIT_FAILURE); |
382 | goto shutdown; | 360 | goto shutdown; |
diff --git a/sway/meson.build b/sway/meson.build index 1402db15..d937e425 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -5,25 +5,26 @@ sway_sources = files( | |||
5 | 'decoration.c', | 5 | 'decoration.c', |
6 | 'ipc-json.c', | 6 | 'ipc-json.c', |
7 | 'ipc-server.c', | 7 | 'ipc-server.c', |
8 | 'lock.c', | ||
8 | 'main.c', | 9 | 'main.c', |
10 | 'realtime.c', | ||
11 | 'scene_descriptor.c', | ||
9 | 'server.c', | 12 | 'server.c', |
13 | 'sway_text_node.c', | ||
10 | 'swaynag.c', | 14 | 'swaynag.c', |
11 | 'xdg_activation_v1.c', | 15 | 'xdg_activation_v1.c', |
12 | 'xdg_decoration.c', | 16 | 'xdg_decoration.c', |
13 | 17 | ||
14 | 'desktop/desktop.c', | ||
15 | 'desktop/idle_inhibit_v1.c', | 18 | 'desktop/idle_inhibit_v1.c', |
16 | 'desktop/layer_shell.c', | 19 | 'desktop/layer_shell.c', |
17 | 'desktop/output.c', | 20 | 'desktop/output.c', |
18 | 'desktop/render.c', | ||
19 | 'desktop/surface.c', | ||
20 | 'desktop/transaction.c', | 21 | 'desktop/transaction.c', |
21 | 'desktop/xdg_shell.c', | 22 | 'desktop/xdg_shell.c', |
23 | 'desktop/launcher.c', | ||
22 | 24 | ||
23 | 'input/input-manager.c', | 25 | 'input/input-manager.c', |
24 | 'input/cursor.c', | 26 | 'input/cursor.c', |
25 | 'input/keyboard.c', | 27 | 'input/keyboard.c', |
26 | 'input/libinput.c', | ||
27 | 'input/seat.c', | 28 | 'input/seat.c', |
28 | 'input/seatop_default.c', | 29 | 'input/seatop_default.c', |
29 | 'input/seatop_down.c', | 30 | 'input/seatop_down.c', |
@@ -65,6 +66,7 @@ sway_sources = files( | |||
65 | 'commands/force_focus_wrapping.c', | 66 | 'commands/force_focus_wrapping.c', |
66 | 'commands/fullscreen.c', | 67 | 'commands/fullscreen.c', |
67 | 'commands/gaps.c', | 68 | 'commands/gaps.c', |
69 | 'commands/gesture.c', | ||
68 | 'commands/hide_edge_borders.c', | 70 | 'commands/hide_edge_borders.c', |
69 | 'commands/inhibit_idle.c', | 71 | 'commands/inhibit_idle.c', |
70 | 'commands/kill.c', | 72 | 'commands/kill.c', |
@@ -83,6 +85,7 @@ sway_sources = files( | |||
83 | 'commands/nop.c', | 85 | 'commands/nop.c', |
84 | 'commands/output.c', | 86 | 'commands/output.c', |
85 | 'commands/popup_during_fullscreen.c', | 87 | 'commands/popup_during_fullscreen.c', |
88 | 'commands/primary_selection.c', | ||
86 | 'commands/reload.c', | 89 | 'commands/reload.c', |
87 | 'commands/rename.c', | 90 | 'commands/rename.c', |
88 | 'commands/resize.c', | 91 | 'commands/resize.c', |
@@ -154,6 +157,7 @@ sway_sources = files( | |||
154 | 'commands/input/drag.c', | 157 | 'commands/input/drag.c', |
155 | 'commands/input/drag_lock.c', | 158 | 'commands/input/drag_lock.c', |
156 | 'commands/input/dwt.c', | 159 | 'commands/input/dwt.c', |
160 | 'commands/input/dwtp.c', | ||
157 | 'commands/input/events.c', | 161 | 'commands/input/events.c', |
158 | 'commands/input/left_handed.c', | 162 | 'commands/input/left_handed.c', |
159 | 'commands/input/map_from_region.c', | 163 | 'commands/input/map_from_region.c', |
@@ -162,9 +166,11 @@ sway_sources = files( | |||
162 | 'commands/input/middle_emulation.c', | 166 | 'commands/input/middle_emulation.c', |
163 | 'commands/input/natural_scroll.c', | 167 | 'commands/input/natural_scroll.c', |
164 | 'commands/input/pointer_accel.c', | 168 | 'commands/input/pointer_accel.c', |
169 | 'commands/input/rotation_angle.c', | ||
165 | 'commands/input/repeat_delay.c', | 170 | 'commands/input/repeat_delay.c', |
166 | 'commands/input/repeat_rate.c', | 171 | 'commands/input/repeat_rate.c', |
167 | 'commands/input/scroll_button.c', | 172 | 'commands/input/scroll_button.c', |
173 | 'commands/input/scroll_button_lock.c', | ||
168 | 'commands/input/scroll_factor.c', | 174 | 'commands/input/scroll_factor.c', |
169 | 'commands/input/scroll_method.c', | 175 | 'commands/input/scroll_method.c', |
170 | 'commands/input/tap.c', | 176 | 'commands/input/tap.c', |
@@ -188,11 +194,14 @@ sway_sources = files( | |||
188 | 'commands/output/max_render_time.c', | 194 | 'commands/output/max_render_time.c', |
189 | 'commands/output/mode.c', | 195 | 'commands/output/mode.c', |
190 | 'commands/output/position.c', | 196 | 'commands/output/position.c', |
197 | 'commands/output/power.c', | ||
198 | 'commands/output/render_bit_depth.c', | ||
191 | 'commands/output/scale.c', | 199 | 'commands/output/scale.c', |
192 | 'commands/output/scale_filter.c', | 200 | 'commands/output/scale_filter.c', |
193 | 'commands/output/subpixel.c', | 201 | 'commands/output/subpixel.c', |
194 | 'commands/output/toggle.c', | 202 | 'commands/output/toggle.c', |
195 | 'commands/output/transform.c', | 203 | 'commands/output/transform.c', |
204 | 'commands/output/unplug.c', | ||
196 | 205 | ||
197 | 'tree/arrange.c', | 206 | 'tree/arrange.c', |
198 | 'tree/container.c', | 207 | 'tree/container.c', |
@@ -212,23 +221,27 @@ sway_deps = [ | |||
212 | libudev, | 221 | libudev, |
213 | math, | 222 | math, |
214 | pango, | 223 | pango, |
215 | pcre, | 224 | pcre2, |
216 | glesv2, | ||
217 | pixman, | 225 | pixman, |
218 | server_protos, | 226 | threads, |
219 | wayland_server, | 227 | wayland_server, |
220 | wlroots, | 228 | wlroots, |
221 | xkbcommon, | 229 | xkbcommon, |
230 | xcb, | ||
231 | xcb_icccm, | ||
222 | ] | 232 | ] |
223 | 233 | ||
224 | if have_xwayland | 234 | if have_xwayland |
225 | sway_sources += 'desktop/xwayland.c' | 235 | sway_sources += 'desktop/xwayland.c' |
226 | sway_deps += xcb | 236 | endif |
237 | |||
238 | if wlroots_features['libinput_backend'] | ||
239 | sway_sources += 'input/libinput.c' | ||
227 | endif | 240 | endif |
228 | 241 | ||
229 | executable( | 242 | executable( |
230 | 'sway', | 243 | 'sway', |
231 | sway_sources, | 244 | sway_sources + wl_protos_src, |
232 | include_directories: [sway_inc], | 245 | include_directories: [sway_inc], |
233 | dependencies: sway_deps, | 246 | dependencies: sway_deps, |
234 | link_with: [lib_sway_common], | 247 | link_with: [lib_sway_common], |
diff --git a/sway/realtime.c b/sway/realtime.c new file mode 100644 index 00000000..11154af0 --- /dev/null +++ b/sway/realtime.c | |||
@@ -0,0 +1,40 @@ | |||
1 | #include <sys/resource.h> | ||
2 | #include <sched.h> | ||
3 | #include <unistd.h> | ||
4 | #include <pthread.h> | ||
5 | #include "sway/server.h" | ||
6 | #include "log.h" | ||
7 | |||
8 | static void child_fork_callback(void) { | ||
9 | struct sched_param param; | ||
10 | |||
11 | param.sched_priority = 0; | ||
12 | |||
13 | int ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m); | ||
14 | if (ret != 0) { | ||
15 | sway_log(SWAY_ERROR, "Failed to reset scheduler policy on fork"); | ||
16 | } | ||
17 | } | ||
18 | |||
19 | void set_rr_scheduling(void) { | ||
20 | int prio = sched_get_priority_min(SCHED_RR); | ||
21 | int old_policy; | ||
22 | int ret; | ||
23 | struct sched_param param; | ||
24 | |||
25 | ret = pthread_getschedparam(pthread_self(), &old_policy, ¶m); | ||
26 | if (ret != 0) { | ||
27 | sway_log(SWAY_DEBUG, "Failed to get old scheduling priority"); | ||
28 | return; | ||
29 | } | ||
30 | |||
31 | param.sched_priority = prio; | ||
32 | |||
33 | ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); | ||
34 | if (ret != 0) { | ||
35 | sway_log(SWAY_INFO, "Failed to set scheduling priority to %d", prio); | ||
36 | return; | ||
37 | } | ||
38 | |||
39 | pthread_atfork(NULL, NULL, child_fork_callback); | ||
40 | } | ||
diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c new file mode 100644 index 00000000..a30d4664 --- /dev/null +++ b/sway/scene_descriptor.c | |||
@@ -0,0 +1,66 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <wlr/util/addon.h> | ||
3 | #include "log.h" | ||
4 | #include "sway/scene_descriptor.h" | ||
5 | |||
6 | struct scene_descriptor { | ||
7 | void *data; | ||
8 | struct wlr_addon addon; | ||
9 | }; | ||
10 | |||
11 | static const struct wlr_addon_interface addon_interface; | ||
12 | |||
13 | static struct scene_descriptor *scene_node_get_descriptor( | ||
14 | struct wlr_scene_node *node, enum sway_scene_descriptor_type type) { | ||
15 | struct wlr_addon *addon = wlr_addon_find(&node->addons, (void *)type, &addon_interface); | ||
16 | if (!addon) { | ||
17 | return NULL; | ||
18 | } | ||
19 | |||
20 | struct scene_descriptor *desc = wl_container_of(addon, desc, addon); | ||
21 | return desc; | ||
22 | } | ||
23 | |||
24 | static void descriptor_destroy(struct scene_descriptor *desc) { | ||
25 | wlr_addon_finish(&desc->addon); | ||
26 | free(desc); | ||
27 | } | ||
28 | |||
29 | void *scene_descriptor_try_get(struct wlr_scene_node *node, | ||
30 | enum sway_scene_descriptor_type type) { | ||
31 | struct scene_descriptor *desc = scene_node_get_descriptor(node, type); | ||
32 | if (!desc) { | ||
33 | return NULL; | ||
34 | } | ||
35 | |||
36 | return desc->data; | ||
37 | } | ||
38 | |||
39 | void scene_descriptor_destroy(struct wlr_scene_node *node, | ||
40 | enum sway_scene_descriptor_type type) { | ||
41 | struct scene_descriptor *desc = scene_node_get_descriptor(node, type); | ||
42 | descriptor_destroy(desc); | ||
43 | } | ||
44 | |||
45 | static void addon_handle_destroy(struct wlr_addon *addon) { | ||
46 | struct scene_descriptor *desc = wl_container_of(addon, desc, addon); | ||
47 | descriptor_destroy(desc); | ||
48 | } | ||
49 | |||
50 | static const struct wlr_addon_interface addon_interface = { | ||
51 | .name = "sway_scene_descriptor", | ||
52 | .destroy = addon_handle_destroy, | ||
53 | }; | ||
54 | |||
55 | bool scene_descriptor_assign(struct wlr_scene_node *node, | ||
56 | enum sway_scene_descriptor_type type, void *data) { | ||
57 | struct scene_descriptor *desc = calloc(1, sizeof(*desc)); | ||
58 | if (!desc) { | ||
59 | sway_log(SWAY_ERROR, "Could not allocate a scene descriptor"); | ||
60 | return false; | ||
61 | } | ||
62 | |||
63 | wlr_addon_init(&desc->addon, &node->addons, (void *)type, &addon_interface); | ||
64 | desc->data = data; | ||
65 | return true; | ||
66 | } | ||
diff --git a/sway/server.c b/sway/server.c index 2e5ab104..180d3a6b 100644 --- a/sway/server.c +++ b/sway/server.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -7,21 +6,35 @@ | |||
7 | #include <wlr/backend.h> | 6 | #include <wlr/backend.h> |
8 | #include <wlr/backend/headless.h> | 7 | #include <wlr/backend/headless.h> |
9 | #include <wlr/backend/multi.h> | 8 | #include <wlr/backend/multi.h> |
10 | #include <wlr/backend/noop.h> | ||
11 | #include <wlr/backend/session.h> | ||
12 | #include <wlr/config.h> | 9 | #include <wlr/config.h> |
10 | #include <wlr/render/allocator.h> | ||
13 | #include <wlr/render/wlr_renderer.h> | 11 | #include <wlr/render/wlr_renderer.h> |
14 | #include <wlr/types/wlr_compositor.h> | 12 | #include <wlr/types/wlr_compositor.h> |
13 | #include <wlr/types/wlr_content_type_v1.h> | ||
14 | #include <wlr/types/wlr_cursor_shape_v1.h> | ||
15 | #include <wlr/types/wlr_data_control_v1.h> | 15 | #include <wlr/types/wlr_data_control_v1.h> |
16 | #include <wlr/types/wlr_data_device.h> | ||
17 | #include <wlr/types/wlr_drm.h> | ||
16 | #include <wlr/types/wlr_export_dmabuf_v1.h> | 18 | #include <wlr/types/wlr_export_dmabuf_v1.h> |
19 | #include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h> | ||
20 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
21 | #include <wlr/types/wlr_fractional_scale_v1.h> | ||
17 | #include <wlr/types/wlr_gamma_control_v1.h> | 22 | #include <wlr/types/wlr_gamma_control_v1.h> |
18 | #include <wlr/types/wlr_idle.h> | 23 | #include <wlr/types/wlr_idle_notify_v1.h> |
19 | #include <wlr/types/wlr_layer_shell_v1.h> | 24 | #include <wlr/types/wlr_layer_shell_v1.h> |
25 | #include <wlr/types/wlr_linux_dmabuf_v1.h> | ||
26 | #include <wlr/types/wlr_output_management_v1.h> | ||
27 | #include <wlr/types/wlr_output_power_management_v1.h> | ||
20 | #include <wlr/types/wlr_pointer_constraints_v1.h> | 28 | #include <wlr/types/wlr_pointer_constraints_v1.h> |
29 | #include <wlr/types/wlr_presentation_time.h> | ||
21 | #include <wlr/types/wlr_primary_selection_v1.h> | 30 | #include <wlr/types/wlr_primary_selection_v1.h> |
22 | #include <wlr/types/wlr_relative_pointer_v1.h> | 31 | #include <wlr/types/wlr_relative_pointer_v1.h> |
23 | #include <wlr/types/wlr_screencopy_v1.h> | 32 | #include <wlr/types/wlr_screencopy_v1.h> |
33 | #include <wlr/types/wlr_security_context_v1.h> | ||
24 | #include <wlr/types/wlr_server_decoration.h> | 34 | #include <wlr/types/wlr_server_decoration.h> |
35 | #include <wlr/types/wlr_session_lock_v1.h> | ||
36 | #include <wlr/types/wlr_single_pixel_buffer_v1.h> | ||
37 | #include <wlr/types/wlr_subcompositor.h> | ||
25 | #include <wlr/types/wlr_tablet_v2.h> | 38 | #include <wlr/types/wlr_tablet_v2.h> |
26 | #include <wlr/types/wlr_viewporter.h> | 39 | #include <wlr/types/wlr_viewporter.h> |
27 | #include <wlr/types/wlr_xcursor_manager.h> | 40 | #include <wlr/types/wlr_xcursor_manager.h> |
@@ -31,6 +44,7 @@ | |||
31 | #include <wlr/types/wlr_xdg_foreign_v1.h> | 44 | #include <wlr/types/wlr_xdg_foreign_v1.h> |
32 | #include <wlr/types/wlr_xdg_foreign_v2.h> | 45 | #include <wlr/types/wlr_xdg_foreign_v2.h> |
33 | #include <wlr/types/wlr_xdg_output_v1.h> | 46 | #include <wlr/types/wlr_xdg_output_v1.h> |
47 | #include <xf86drm.h> | ||
34 | #include "config.h" | 48 | #include "config.h" |
35 | #include "list.h" | 49 | #include "list.h" |
36 | #include "log.h" | 50 | #include "log.h" |
@@ -39,41 +53,221 @@ | |||
39 | #include "sway/input/input-manager.h" | 53 | #include "sway/input/input-manager.h" |
40 | #include "sway/output.h" | 54 | #include "sway/output.h" |
41 | #include "sway/server.h" | 55 | #include "sway/server.h" |
56 | #include "sway/input/cursor.h" | ||
42 | #include "sway/tree/root.h" | 57 | #include "sway/tree/root.h" |
58 | |||
43 | #if HAVE_XWAYLAND | 59 | #if HAVE_XWAYLAND |
60 | #include <wlr/xwayland/shell.h> | ||
44 | #include "sway/xwayland.h" | 61 | #include "sway/xwayland.h" |
45 | #endif | 62 | #endif |
46 | 63 | ||
47 | bool server_privileged_prepare(struct sway_server *server) { | 64 | #if WLR_HAS_DRM_BACKEND |
48 | sway_log(SWAY_DEBUG, "Preparing Wayland server initialization"); | 65 | #include <wlr/types/wlr_drm_lease_v1.h> |
66 | #endif | ||
67 | |||
68 | #define SWAY_XDG_SHELL_VERSION 5 | ||
69 | #define SWAY_LAYER_SHELL_VERSION 4 | ||
70 | #define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 | ||
71 | |||
72 | bool allow_unsupported_gpu = false; | ||
73 | |||
74 | #if WLR_HAS_DRM_BACKEND | ||
75 | static void handle_drm_lease_request(struct wl_listener *listener, void *data) { | ||
76 | /* We only offer non-desktop outputs, but in the future we might want to do | ||
77 | * more logic here. */ | ||
78 | |||
79 | struct wlr_drm_lease_request_v1 *req = data; | ||
80 | struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req); | ||
81 | if (!lease) { | ||
82 | sway_log(SWAY_ERROR, "Failed to grant lease request"); | ||
83 | wlr_drm_lease_request_v1_reject(req); | ||
84 | } | ||
85 | } | ||
86 | #endif | ||
87 | |||
88 | static bool is_privileged(const struct wl_global *global) { | ||
89 | #if WLR_HAS_DRM_BACKEND | ||
90 | if (server.drm_lease_manager != NULL) { | ||
91 | struct wlr_drm_lease_device_v1 *drm_lease_dev; | ||
92 | wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) { | ||
93 | if (drm_lease_dev->global == global) { | ||
94 | return true; | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | #endif | ||
99 | |||
100 | return | ||
101 | global == server.output_manager_v1->global || | ||
102 | global == server.output_power_manager_v1->global || | ||
103 | global == server.input_method->global || | ||
104 | global == server.foreign_toplevel_list->global || | ||
105 | global == server.foreign_toplevel_manager->global || | ||
106 | global == server.data_control_manager_v1->global || | ||
107 | global == server.screencopy_manager_v1->global || | ||
108 | global == server.export_dmabuf_manager_v1->global || | ||
109 | global == server.security_context_manager_v1->global || | ||
110 | global == server.gamma_control_manager_v1->global || | ||
111 | global == server.layer_shell->global || | ||
112 | global == server.session_lock.manager->global || | ||
113 | global == server.input->keyboard_shortcuts_inhibit->global || | ||
114 | global == server.input->virtual_keyboard->global || | ||
115 | global == server.input->virtual_pointer->global || | ||
116 | global == server.input->transient_seat_manager->global; | ||
117 | } | ||
118 | |||
119 | static bool filter_global(const struct wl_client *client, | ||
120 | const struct wl_global *global, void *data) { | ||
121 | #if HAVE_XWAYLAND | ||
122 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | ||
123 | if (xwayland && global == xwayland->shell_v1->global) { | ||
124 | return xwayland->server != NULL && client == xwayland->server->client; | ||
125 | } | ||
126 | #endif | ||
127 | |||
128 | // Restrict usage of privileged protocols to unsandboxed clients | ||
129 | // TODO: add a way for users to configure an allow-list | ||
130 | const struct wlr_security_context_v1_state *security_context = | ||
131 | wlr_security_context_manager_v1_lookup_client( | ||
132 | server.security_context_manager_v1, (struct wl_client *)client); | ||
133 | if (is_privileged(global)) { | ||
134 | return security_context == NULL; | ||
135 | } | ||
136 | |||
137 | return true; | ||
138 | } | ||
139 | |||
140 | static void detect_proprietary(struct wlr_backend *backend, void *data) { | ||
141 | int drm_fd = wlr_backend_get_drm_fd(backend); | ||
142 | if (drm_fd < 0) { | ||
143 | return; | ||
144 | } | ||
145 | |||
146 | drmVersion *version = drmGetVersion(drm_fd); | ||
147 | if (version == NULL) { | ||
148 | sway_log(SWAY_ERROR, "drmGetVersion() failed"); | ||
149 | return; | ||
150 | } | ||
151 | |||
152 | bool is_unsupported = false; | ||
153 | if (strcmp(version->name, "nvidia-drm") == 0) { | ||
154 | is_unsupported = true; | ||
155 | sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); | ||
156 | if (!allow_unsupported_gpu) { | ||
157 | sway_log(SWAY_ERROR, "Use Nouveau instead"); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | if (strcmp(version->name, "evdi") == 0) { | ||
162 | is_unsupported = true; | ||
163 | sway_log(SWAY_ERROR, "!!! Proprietary DisplayLink drivers are in use !!!"); | ||
164 | } | ||
165 | |||
166 | if (!allow_unsupported_gpu && is_unsupported) { | ||
167 | sway_log(SWAY_ERROR, | ||
168 | "Proprietary drivers are NOT supported. To launch sway anyway, " | ||
169 | "launch with --unsupported-gpu and DO NOT report issues."); | ||
170 | exit(EXIT_FAILURE); | ||
171 | } | ||
172 | |||
173 | drmFreeVersion(version); | ||
174 | } | ||
175 | |||
176 | static void handle_renderer_lost(struct wl_listener *listener, void *data) { | ||
177 | struct sway_server *server = wl_container_of(listener, server, renderer_lost); | ||
178 | |||
179 | sway_log(SWAY_INFO, "Re-creating renderer after GPU reset"); | ||
180 | |||
181 | struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); | ||
182 | if (renderer == NULL) { | ||
183 | sway_log(SWAY_ERROR, "Unable to create renderer"); | ||
184 | return; | ||
185 | } | ||
186 | |||
187 | struct wlr_allocator *allocator = | ||
188 | wlr_allocator_autocreate(server->backend, renderer); | ||
189 | if (allocator == NULL) { | ||
190 | sway_log(SWAY_ERROR, "Unable to create allocator"); | ||
191 | wlr_renderer_destroy(renderer); | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | struct wlr_renderer *old_renderer = server->renderer; | ||
196 | struct wlr_allocator *old_allocator = server->allocator; | ||
197 | server->renderer = renderer; | ||
198 | server->allocator = allocator; | ||
199 | |||
200 | wl_list_remove(&server->renderer_lost.link); | ||
201 | wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); | ||
202 | |||
203 | wlr_compositor_set_renderer(server->compositor, renderer); | ||
204 | |||
205 | for (int i = 0; i < root->outputs->length; ++i) { | ||
206 | struct sway_output *output = root->outputs->items[i]; | ||
207 | wlr_output_init_render(output->wlr_output, | ||
208 | server->allocator, server->renderer); | ||
209 | } | ||
210 | |||
211 | wlr_allocator_destroy(old_allocator); | ||
212 | wlr_renderer_destroy(old_renderer); | ||
213 | } | ||
214 | |||
215 | bool server_init(struct sway_server *server) { | ||
216 | sway_log(SWAY_DEBUG, "Initializing Wayland server"); | ||
49 | server->wl_display = wl_display_create(); | 217 | server->wl_display = wl_display_create(); |
50 | server->wl_event_loop = wl_display_get_event_loop(server->wl_display); | 218 | server->wl_event_loop = wl_display_get_event_loop(server->wl_display); |
51 | server->backend = wlr_backend_autocreate(server->wl_display); | ||
52 | 219 | ||
220 | wl_display_set_global_filter(server->wl_display, filter_global, NULL); | ||
221 | |||
222 | root = root_create(server->wl_display); | ||
223 | |||
224 | server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session); | ||
53 | if (!server->backend) { | 225 | if (!server->backend) { |
54 | sway_log(SWAY_ERROR, "Unable to create backend"); | 226 | sway_log(SWAY_ERROR, "Unable to create backend"); |
55 | return false; | 227 | return false; |
56 | } | 228 | } |
57 | return true; | ||
58 | } | ||
59 | 229 | ||
60 | bool server_init(struct sway_server *server) { | 230 | wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL); |
61 | sway_log(SWAY_DEBUG, "Initializing Wayland server"); | 231 | |
232 | server->renderer = wlr_renderer_autocreate(server->backend); | ||
233 | if (!server->renderer) { | ||
234 | sway_log(SWAY_ERROR, "Failed to create renderer"); | ||
235 | return false; | ||
236 | } | ||
237 | |||
238 | server->renderer_lost.notify = handle_renderer_lost; | ||
239 | wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); | ||
62 | 240 | ||
63 | struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); | 241 | wlr_renderer_init_wl_shm(server->renderer, server->wl_display); |
64 | assert(renderer); | ||
65 | 242 | ||
66 | wlr_renderer_init_wl_display(renderer, server->wl_display); | 243 | if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { |
244 | server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( | ||
245 | server->wl_display, 4, server->renderer); | ||
246 | if (debug.legacy_wl_drm) { | ||
247 | wlr_drm_create(server->wl_display, server->renderer); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | server->allocator = wlr_allocator_autocreate(server->backend, | ||
252 | server->renderer); | ||
253 | if (!server->allocator) { | ||
254 | sway_log(SWAY_ERROR, "Failed to create allocator"); | ||
255 | return false; | ||
256 | } | ||
257 | |||
258 | server->compositor = wlr_compositor_create(server->wl_display, 6, | ||
259 | server->renderer); | ||
67 | 260 | ||
68 | server->compositor = wlr_compositor_create(server->wl_display, renderer); | 261 | wlr_subcompositor_create(server->wl_display); |
69 | server->compositor_new_surface.notify = handle_compositor_new_surface; | ||
70 | wl_signal_add(&server->compositor->events.new_surface, | ||
71 | &server->compositor_new_surface); | ||
72 | 262 | ||
73 | server->data_device_manager = | 263 | server->data_device_manager = |
74 | wlr_data_device_manager_create(server->wl_display); | 264 | wlr_data_device_manager_create(server->wl_display); |
75 | 265 | ||
76 | wlr_gamma_control_manager_v1_create(server->wl_display); | 266 | server->gamma_control_manager_v1 = |
267 | wlr_gamma_control_manager_v1_create(server->wl_display); | ||
268 | server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma; | ||
269 | wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma, | ||
270 | &server->gamma_control_set_gamma); | ||
77 | 271 | ||
78 | server->new_output.notify = handle_new_output; | 272 | server->new_output.notify = handle_new_output; |
79 | wl_signal_add(&server->backend->events.new_output, &server->new_output); | 273 | wl_signal_add(&server->backend->events.new_output, &server->new_output); |
@@ -83,19 +277,20 @@ bool server_init(struct sway_server *server) { | |||
83 | 277 | ||
84 | wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); | 278 | wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); |
85 | 279 | ||
86 | server->idle = wlr_idle_create(server->wl_display); | 280 | server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); |
87 | server->idle_inhibit_manager_v1 = | 281 | sway_idle_inhibit_manager_v1_init(); |
88 | sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle); | ||
89 | 282 | ||
90 | server->layer_shell = wlr_layer_shell_v1_create(server->wl_display); | 283 | server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, |
284 | SWAY_LAYER_SHELL_VERSION); | ||
91 | wl_signal_add(&server->layer_shell->events.new_surface, | 285 | wl_signal_add(&server->layer_shell->events.new_surface, |
92 | &server->layer_shell_surface); | 286 | &server->layer_shell_surface); |
93 | server->layer_shell_surface.notify = handle_layer_shell_surface; | 287 | server->layer_shell_surface.notify = handle_layer_shell_surface; |
94 | 288 | ||
95 | server->xdg_shell = wlr_xdg_shell_create(server->wl_display); | 289 | server->xdg_shell = wlr_xdg_shell_create(server->wl_display, |
96 | wl_signal_add(&server->xdg_shell->events.new_surface, | 290 | SWAY_XDG_SHELL_VERSION); |
97 | &server->xdg_shell_surface); | 291 | wl_signal_add(&server->xdg_shell->events.new_toplevel, |
98 | server->xdg_shell_surface.notify = handle_xdg_shell_surface; | 292 | &server->xdg_shell_toplevel); |
293 | server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel; | ||
99 | 294 | ||
100 | server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); | 295 | server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); |
101 | 296 | ||
@@ -126,8 +321,7 @@ bool server_init(struct sway_server *server) { | |||
126 | wl_signal_add(&server->pointer_constraints->events.new_constraint, | 321 | wl_signal_add(&server->pointer_constraints->events.new_constraint, |
127 | &server->pointer_constraint); | 322 | &server->pointer_constraint); |
128 | 323 | ||
129 | server->presentation = | 324 | wlr_presentation_create(server->wl_display, server->backend); |
130 | wlr_presentation_create(server->wl_display, server->backend); | ||
131 | 325 | ||
132 | server->output_manager_v1 = | 326 | server->output_manager_v1 = |
133 | wlr_output_manager_v1_create(server->wl_display); | 327 | wlr_output_manager_v1_create(server->wl_display); |
@@ -146,14 +340,35 @@ bool server_init(struct sway_server *server) { | |||
146 | &server->output_power_manager_set_mode); | 340 | &server->output_power_manager_set_mode); |
147 | server->input_method = wlr_input_method_manager_v2_create(server->wl_display); | 341 | server->input_method = wlr_input_method_manager_v2_create(server->wl_display); |
148 | server->text_input = wlr_text_input_manager_v3_create(server->wl_display); | 342 | server->text_input = wlr_text_input_manager_v3_create(server->wl_display); |
343 | server->foreign_toplevel_list = | ||
344 | wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION); | ||
149 | server->foreign_toplevel_manager = | 345 | server->foreign_toplevel_manager = |
150 | wlr_foreign_toplevel_manager_v1_create(server->wl_display); | 346 | wlr_foreign_toplevel_manager_v1_create(server->wl_display); |
151 | 347 | ||
152 | wlr_export_dmabuf_manager_v1_create(server->wl_display); | 348 | sway_session_lock_init(); |
153 | wlr_screencopy_manager_v1_create(server->wl_display); | 349 | |
154 | wlr_data_control_manager_v1_create(server->wl_display); | 350 | #if WLR_HAS_DRM_BACKEND |
155 | wlr_primary_selection_v1_device_manager_create(server->wl_display); | 351 | server->drm_lease_manager= |
352 | wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); | ||
353 | if (server->drm_lease_manager) { | ||
354 | server->drm_lease_request.notify = handle_drm_lease_request; | ||
355 | wl_signal_add(&server->drm_lease_manager->events.request, | ||
356 | &server->drm_lease_request); | ||
357 | } else { | ||
358 | sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); | ||
359 | sway_log(SWAY_INFO, "VR will not be available"); | ||
360 | } | ||
361 | #endif | ||
362 | |||
363 | server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display); | ||
364 | server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); | ||
365 | server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); | ||
366 | server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); | ||
156 | wlr_viewporter_create(server->wl_display); | 367 | wlr_viewporter_create(server->wl_display); |
368 | wlr_single_pixel_buffer_manager_v1_create(server->wl_display); | ||
369 | server->content_type_manager_v1 = | ||
370 | wlr_content_type_manager_v1_create(server->wl_display, 1); | ||
371 | wlr_fractional_scale_manager_v1_create(server->wl_display, 1); | ||
157 | 372 | ||
158 | struct wlr_xdg_foreign_registry *foreign_registry = | 373 | struct wlr_xdg_foreign_registry *foreign_registry = |
159 | wlr_xdg_foreign_registry_create(server->wl_display); | 374 | wlr_xdg_foreign_registry_create(server->wl_display); |
@@ -165,11 +380,22 @@ bool server_init(struct sway_server *server) { | |||
165 | xdg_activation_v1_handle_request_activate; | 380 | xdg_activation_v1_handle_request_activate; |
166 | wl_signal_add(&server->xdg_activation_v1->events.request_activate, | 381 | wl_signal_add(&server->xdg_activation_v1->events.request_activate, |
167 | &server->xdg_activation_v1_request_activate); | 382 | &server->xdg_activation_v1_request_activate); |
383 | server->xdg_activation_v1_new_token.notify = | ||
384 | xdg_activation_v1_handle_new_token; | ||
385 | wl_signal_add(&server->xdg_activation_v1->events.new_token, | ||
386 | &server->xdg_activation_v1_new_token); | ||
387 | |||
388 | struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = | ||
389 | wlr_cursor_shape_manager_v1_create(server->wl_display, 1); | ||
390 | server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; | ||
391 | wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape); | ||
392 | |||
393 | wl_list_init(&server->pending_launcher_ctxs); | ||
168 | 394 | ||
169 | // Avoid using "wayland-0" as display socket | 395 | // Avoid using "wayland-0" as display socket |
170 | char name_candidate[16]; | 396 | char name_candidate[16]; |
171 | for (int i = 1; i <= 32; ++i) { | 397 | for (unsigned int i = 1; i <= 32; ++i) { |
172 | sprintf(name_candidate, "wayland-%d", i); | 398 | snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i); |
173 | if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { | 399 | if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { |
174 | server->socket = strdup(name_candidate); | 400 | server->socket = strdup(name_candidate); |
175 | break; | 401 | break; |
@@ -182,20 +408,20 @@ bool server_init(struct sway_server *server) { | |||
182 | return false; | 408 | return false; |
183 | } | 409 | } |
184 | 410 | ||
185 | server->noop_backend = wlr_noop_backend_create(server->wl_display); | 411 | server->headless_backend = wlr_headless_backend_create(server->wl_event_loop); |
186 | |||
187 | struct wlr_output *wlr_output = wlr_noop_add_output(server->noop_backend); | ||
188 | root->noop_output = output_create(wlr_output); | ||
189 | |||
190 | server->headless_backend = | ||
191 | wlr_headless_backend_create_with_renderer(server->wl_display, renderer); | ||
192 | if (!server->headless_backend) { | 412 | if (!server->headless_backend) { |
193 | sway_log(SWAY_INFO, "Failed to create secondary headless backend, " | 413 | sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); |
194 | "starting without it"); | 414 | wlr_backend_destroy(server->backend); |
415 | return false; | ||
195 | } else { | 416 | } else { |
196 | wlr_multi_backend_add(server->backend, server->headless_backend); | 417 | wlr_multi_backend_add(server->backend, server->headless_backend); |
197 | } | 418 | } |
198 | 419 | ||
420 | struct wlr_output *wlr_output = | ||
421 | wlr_headless_add_output(server->headless_backend, 800, 600); | ||
422 | wlr_output_set_name(wlr_output, "FALLBACK"); | ||
423 | root->fallback_output = output_create(wlr_output); | ||
424 | |||
199 | // This may have been set already via -Dtxn-timeout | 425 | // This may have been set already via -Dtxn-timeout |
200 | if (!server->txn_timeout_ms) { | 426 | if (!server->txn_timeout_ms) { |
201 | server->txn_timeout_ms = 200; | 427 | server->txn_timeout_ms = 200; |
@@ -215,6 +441,7 @@ void server_fini(struct sway_server *server) { | |||
215 | wlr_xwayland_destroy(server->xwayland.wlr_xwayland); | 441 | wlr_xwayland_destroy(server->xwayland.wlr_xwayland); |
216 | #endif | 442 | #endif |
217 | wl_display_destroy_clients(server->wl_display); | 443 | wl_display_destroy_clients(server->wl_display); |
444 | wlr_backend_destroy(server->backend); | ||
218 | wl_display_destroy(server->wl_display); | 445 | wl_display_destroy(server->wl_display); |
219 | list_free(server->dirty_nodes); | 446 | list_free(server->dirty_nodes); |
220 | } | 447 | } |
@@ -245,6 +472,10 @@ bool server_start(struct sway_server *server) { | |||
245 | } | 472 | } |
246 | #endif | 473 | #endif |
247 | 474 | ||
475 | if (config->primary_selection) { | ||
476 | wlr_primary_selection_v1_device_manager_create(server->wl_display); | ||
477 | } | ||
478 | |||
248 | sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", | 479 | sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", |
249 | server->socket); | 480 | server->socket); |
250 | if (!wlr_backend_start(server->backend)) { | 481 | if (!wlr_backend_start(server->backend)) { |
@@ -252,6 +483,7 @@ bool server_start(struct sway_server *server) { | |||
252 | wlr_backend_destroy(server->backend); | 483 | wlr_backend_destroy(server->backend); |
253 | return false; | 484 | return false; |
254 | } | 485 | } |
486 | |||
255 | return true; | 487 | return true; |
256 | } | 488 | } |
257 | 489 | ||
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 80d08449..42e59d57 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd | |||
@@ -40,7 +40,7 @@ runtime. | |||
40 | *font* <font> | 40 | *font* <font> |
41 | Specifies the font to be used in the bar. _font_ should be specified as a | 41 | Specifies the font to be used in the bar. _font_ should be specified as a |
42 | pango font description. For more information on pango font descriptions, | 42 | pango font description. For more information on pango font descriptions, |
43 | see https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string | 43 | see https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description |
44 | 44 | ||
45 | *gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left> | 45 | *gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left> |
46 | Sets the gaps from the edge of the screen for the bar. Gaps can either be | 46 | Sets the gaps from the edge of the screen for the bar. Gaps can either be |
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 25f6de18..442311bb 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd | |||
@@ -121,11 +121,16 @@ The following commands may only be used in the configuration file. | |||
121 | 121 | ||
122 | *input* <identifier> map_from_region <X1xY1> <X2xY2> | 122 | *input* <identifier> map_from_region <X1xY1> <X2xY2> |
123 | Ignores inputs from this device that do not occur within the specified | 123 | Ignores inputs from this device that do not occur within the specified |
124 | region. Can be in millimeters (e.g. 10x20mm 20x40mm) or in terms of 0..1 | 124 | region. Can be in millimeters (e.g. 10x20mm 20x40mm) or the fraction of the |
125 | (e.g. 0.5x0.5 0.7x0.7). Not all devices support millimeters. Only meaningful | 125 | full available space in terms of 0..1 (e.g. 0.5x0.5 0.7x0.7). Not all |
126 | if the device is not a keyboard and provides events in absolute terms (such | 126 | devices support millimeters. Only meaningful if the device is not a |
127 | as a drawing tablet or touch screen - most pointers provide events relative | 127 | keyboard and provides events in absolute terms (such as a drawing tablet |
128 | to the previous frame). | 128 | or touch screen - most pointers provide events relative to the previous |
129 | frame). | ||
130 | |||
131 | Commonly used to maintain the aspect ratio of the input device and screen. | ||
132 | Cropping a 16:10 input region to match a 16:9 display can use 0x0 1x0.9 as | ||
133 | the argument. | ||
129 | 134 | ||
130 | ## LIBINPUT CONFIGURATION | 135 | ## LIBINPUT CONFIGURATION |
131 | 136 | ||
@@ -147,6 +152,10 @@ The following commands may only be used in the configuration file. | |||
147 | *input* <identifier> dwt enabled|disabled | 152 | *input* <identifier> dwt enabled|disabled |
148 | Enables or disables disable-while-typing for the specified input device. | 153 | Enables or disables disable-while-typing for the specified input device. |
149 | 154 | ||
155 | *input* <identifier> dwtp enabled|disabled | ||
156 | Enables or disables disable-while-trackpointing for the specified input | ||
157 | device. | ||
158 | |||
150 | *input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>] | 159 | *input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>] |
151 | Enables or disables send_events for specified input device. Disabling | 160 | Enables or disables send_events for specified input device. Disabling |
152 | send_events disables the input device. | 161 | send_events disables the input device. |
@@ -171,12 +180,19 @@ The following commands may only be used in the configuration file. | |||
171 | *input* <identifier> pointer_accel [<-1|1>] | 180 | *input* <identifier> pointer_accel [<-1|1>] |
172 | Changes the pointer acceleration for the specified input device. | 181 | Changes the pointer acceleration for the specified input device. |
173 | 182 | ||
183 | *input* <identifier> rotation_angle <angle> | ||
184 | Sets the rotation angle of the device to the given clockwise angle in | ||
185 | degrees. The angle must be between 0.0 (inclusive) and 360.0 (exclusive). | ||
186 | |||
174 | *input* <identifier> scroll_button disable|button[1-3,8,9]|<event-code-or-name> | 187 | *input* <identifier> scroll_button disable|button[1-3,8,9]|<event-code-or-name> |
175 | Sets the button used for scroll_method on_button_down. The button can | 188 | Sets the button used for scroll_method on_button_down. The button can |
176 | be given as an event name or code, which can be obtained from *libinput | 189 | be given as an event name or code, which can be obtained from *libinput |
177 | debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to | 190 | debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to |
178 | _disable_, it disables the scroll_method on_button_down. | 191 | _disable_, it disables the scroll_method on_button_down. |
179 | 192 | ||
193 | *input* <identifier> scroll_button_lock enabled|disabled | ||
194 | Enables or disables scroll button lock for specified input device. | ||
195 | |||
180 | *input* <identifier> scroll_factor <floating point value> | 196 | *input* <identifier> scroll_factor <floating point value> |
181 | Changes the scroll factor for the specified input device. Scroll speed will | 197 | Changes the scroll factor for the specified input device. Scroll speed will |
182 | be scaled by the given value, which must be non-negative. | 198 | be scaled by the given value, which must be non-negative. |
@@ -220,6 +236,8 @@ correct seat. | |||
220 | absolute coordinates (with respect to the global coordinate space). | 236 | absolute coordinates (with respect to the global coordinate space). |
221 | Specifying either value as 0 will not update that coordinate. | 237 | Specifying either value as 0 will not update that coordinate. |
222 | 238 | ||
239 | Deprecated: use the virtual-pointer Wayland protocol instead. | ||
240 | |||
223 | *seat* <seat> cursor press|release button[1-9]|<event-name-or-code> | 241 | *seat* <seat> cursor press|release button[1-9]|<event-name-or-code> |
224 | Simulate pressing (or releasing) the specified mouse button on the | 242 | Simulate pressing (or releasing) the specified mouse button on the |
225 | specified seat. The button can either be provided as a button event name or | 243 | specified seat. The button can either be provided as a button event name or |
@@ -228,12 +246,14 @@ correct seat. | |||
228 | event will be simulated, however _press_ and _release_ will be ignored and | 246 | event will be simulated, however _press_ and _release_ will be ignored and |
229 | both will occur. | 247 | both will occur. |
230 | 248 | ||
249 | Deprecated: use the virtual-pointer Wayland protocol instead. | ||
250 | |||
231 | *seat* <name> fallback true|false | 251 | *seat* <name> fallback true|false |
232 | Set this seat as the fallback seat. A fallback seat will attach any device | 252 | Set this seat as the fallback seat. A fallback seat will attach any device |
233 | not explicitly attached to another seat (similar to a "default" seat). | 253 | not explicitly attached to another seat (similar to a "default" seat). |
234 | 254 | ||
235 | *seat* <name> hide_cursor <timeout>|when-typing [enable|disable] | 255 | *seat* <name> hide_cursor <timeout>|when-typing [enable|disable] |
236 | Hides the cursor image after the specified event occured. | 256 | Hides the cursor image after the specified event occurred. |
237 | 257 | ||
238 | If _timeout_ is specified, then the cursor will be hidden after _timeout_ | 258 | If _timeout_ is specified, then the cursor will be hidden after _timeout_ |
239 | (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0 | 259 | (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0 |
@@ -243,18 +263,16 @@ correct seat. | |||
243 | If _when-typing_ is enabled, then the cursor will be hidden whenever a key | 263 | If _when-typing_ is enabled, then the cursor will be hidden whenever a key |
244 | is pressed. | 264 | is pressed. |
245 | 265 | ||
266 | Be aware that this setting can interfere with input handling in games and | ||
267 | certain types of software (Gimp, Blender etc) that rely on simultaneous | ||
268 | input from mouse and keyboard. | ||
269 | |||
246 | *seat* <name> idle_inhibit <sources...> | 270 | *seat* <name> idle_inhibit <sources...> |
247 | Sets the set of input event sources which can prevent the seat from | 271 | Sets the set of input event sources which can prevent the seat from |
248 | becoming idle, as a space separated list of source names. Valid names are | 272 | becoming idle, as a space separated list of source names. Valid names are |
249 | "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", | 273 | "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", |
250 | and "switch". The default behavior is to prevent idle on any event. | 274 | and "switch". The default behavior is to prevent idle on any event. |
251 | 275 | ||
252 | *seat* <name> idle_wake <sources...> | ||
253 | Sets the set of input event sources which can wake the seat from | ||
254 | its idle state, as a space separated list of source names. Valid names are | ||
255 | "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", | ||
256 | and "switch". The default behavior is to wake from idle on any event. | ||
257 | |||
258 | *seat* <name> keyboard_grouping none|smart | 276 | *seat* <name> keyboard_grouping none|smart |
259 | Set how the keyboards in the seat are grouped together. Currently, there | 277 | Set how the keyboards in the seat are grouped together. Currently, there |
260 | are two options. _none_ will disable all keyboard grouping. This will make | 278 | are two options. _none_ will disable all keyboard grouping. This will make |
@@ -277,7 +295,7 @@ correct seat. | |||
277 | whether future inhibitors are honoured by default, i.e. activated | 295 | whether future inhibitors are honoured by default, i.e. activated |
278 | automatically, the default being _enable_. When used at runtime, | 296 | automatically, the default being _enable_. When used at runtime, |
279 | _disable_ also disables any currently active inhibitors. _activate_, | 297 | _disable_ also disables any currently active inhibitors. _activate_, |
280 | _deactivate_ and _toggle_ are only useable at runtime and change the | 298 | _deactivate_ and _toggle_ are only usable at runtime and change the |
281 | state of a potentially existing inhibitor on the currently focused | 299 | state of a potentially existing inhibitor on the currently focused |
282 | window. This can be used with the current seat alias (_-_) to affect | 300 | window. This can be used with the current seat alias (_-_) to affect |
283 | only the currently focused window of the current seat. Subcommand | 301 | only the currently focused window of the current seat. Subcommand |
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 373e9dce..2f697248 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd | |||
@@ -213,7 +213,10 @@ following properties: | |||
213 | : Whether this output is active/enabled | 213 | : Whether this output is active/enabled |
214 | |- dpms | 214 | |- dpms |
215 | : boolean | 215 | : boolean |
216 | : Whether this output is on/off (via DPMS) | 216 | : (Deprecated, use _power_ instead) Whether this output is on/off (via DPMS) |
217 | |- power | ||
218 | : boolean | ||
219 | : Whether this output is on/off | ||
217 | |- primary | 220 | |- primary |
218 | : boolean | 221 | : boolean |
219 | : For i3 compatibility, this will be false. It does not make sense in Wayland | 222 | : For i3 compatibility, this will be false. It does not make sense in Wayland |
@@ -294,7 +297,7 @@ following properties: | |||
294 | Retrieve a JSON representation of the tree | 297 | Retrieve a JSON representation of the tree |
295 | 298 | ||
296 | *REPLY*++ | 299 | *REPLY*++ |
297 | An array of object the represent the current tree. Each object represents one | 300 | An array of objects that represent the current tree. Each object represents one |
298 | node and will have the following properties: | 301 | node and will have the following properties: |
299 | 302 | ||
300 | [- *PROPERTY* | 303 | [- *PROPERTY* |
@@ -334,8 +337,9 @@ node and will have the following properties: | |||
334 | this, but borders are included. | 337 | this, but borders are included. |
335 | |- window_rect | 338 | |- window_rect |
336 | : object | 339 | : object |
337 | : The geometry of the contents inside the node. The window decorations are | 340 | : The geometry of the content inside the node. These coordinates are relative |
338 | excluded from this calculation, but borders are included. | 341 | to the node itself. Window decorations and borders are outside the |
342 | _window_rect_ | ||
339 | |- deco_rect | 343 | |- deco_rect |
340 | : object | 344 | : object |
341 | : The geometry of the decorations for the node relative to the parent node | 345 | : The geometry of the decorations for the node relative to the parent node |
@@ -370,8 +374,14 @@ node and will have the following properties: | |||
370 | that can be used as an aid in submitting reproduction steps for bug reports | 374 | that can be used as an aid in submitting reproduction steps for bug reports |
371 | |- fullscreen_mode | 375 | |- fullscreen_mode |
372 | : integer | 376 | : integer |
373 | : (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means | 377 | : (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means |
374 | full workspace, and 2 means global fullscreen | 378 | full workspace, and 2 means global fullscreen |
379 | |- floating | ||
380 | : string | ||
381 | : Floating state of container. Can be either "auto_off" or "user_on" | ||
382 | |- scratchpad_state | ||
383 | : string | ||
384 | : Whether the window is in the scratchpad. Can be either "none" or "fresh" | ||
375 | |- app_id | 385 | |- app_id |
376 | : string | 386 | : string |
377 | : (Only views) For an xdg-shell view, the name of the application, if set. | 387 | : (Only views) For an xdg-shell view, the name of the application, if set. |
@@ -1036,7 +1046,7 @@ An object with a single string property containing the contents of the config | |||
1036 | *Example Reply:* | 1046 | *Example Reply:* |
1037 | ``` | 1047 | ``` |
1038 | { | 1048 | { |
1039 | "config": "set $mod Mod4\nbindsym $mod+q exit\n" | 1049 | "config": "set $mod Mod4\\nbindsym $mod+q exit\\n" |
1040 | } | 1050 | } |
1041 | ``` | 1051 | ``` |
1042 | 1052 | ||
@@ -1191,9 +1201,16 @@ following properties will be included for devices that support them: | |||
1191 | : int | 1201 | : int |
1192 | : The scroll button to use when _scroll_method_ is _on_button_down_. This | 1202 | : The scroll button to use when _scroll_method_ is _on_button_down_. This |
1193 | will be given as an input event code | 1203 | will be given as an input event code |
1204 | |- scroll_button_lock | ||
1205 | : string | ||
1206 | : Whether scroll button lock is enabled. It can be _enabled_ or _disabled_ | ||
1194 | |- dwt | 1207 | |- dwt |
1195 | : string | 1208 | : string |
1196 | : Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ | 1209 | : Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ |
1210 | |- dwtp | ||
1211 | : string | ||
1212 | : Whether disable-while-trackpointing is enabled. It can be _enabled_ or | ||
1213 | _disabled_ | ||
1197 | |- calibration_matrix | 1214 | |- calibration_matrix |
1198 | : array | 1215 | : array |
1199 | : An array of 6 floats representing the calibration matrix for absolute | 1216 | : An array of 6 floats representing the calibration matrix for absolute |
@@ -1233,7 +1250,8 @@ following properties will be included for devices that support them: | |||
1233 | "click_method": "button_areas", | 1250 | "click_method": "button_areas", |
1234 | "middle_emulation": "disabled", | 1251 | "middle_emulation": "disabled", |
1235 | "scroll_method": "edge", | 1252 | "scroll_method": "edge", |
1236 | "dwt": "enabled" | 1253 | "dwt": "enabled", |
1254 | "dwtp": "enabled" | ||
1237 | } | 1255 | } |
1238 | }, | 1256 | }, |
1239 | { | 1257 | { |
@@ -1360,7 +1378,8 @@ one seat. Each object has the following properties: | |||
1360 | "click_method": "button_areas", | 1378 | "click_method": "button_areas", |
1361 | "middle_emulation": "disabled", | 1379 | "middle_emulation": "disabled", |
1362 | "scroll_method": "edge", | 1380 | "scroll_method": "edge", |
1363 | "dwt": "enabled" | 1381 | "dwt": "enabled", |
1382 | "dwtp": "enabled" | ||
1364 | } | 1383 | } |
1365 | }, | 1384 | }, |
1366 | { | 1385 | { |
@@ -1436,6 +1455,9 @@ available: | |||
1436 | : workspace | 1455 | : workspace |
1437 | :[ Sent whenever an event involving a workspace occurs such as initialization | 1456 | :[ Sent whenever an event involving a workspace occurs such as initialization |
1438 | of a new workspace or a different workspace gains focus | 1457 | of a new workspace or a different workspace gains focus |
1458 | |- 0x80000001 | ||
1459 | : output | ||
1460 | : Sent when outputs are updated | ||
1439 | |- 0x80000002 | 1461 | |- 0x80000002 |
1440 | : mode | 1462 | : mode |
1441 | : Sent whenever the binding mode changes | 1463 | : Sent whenever the binding mode changes |
@@ -1556,6 +1578,20 @@ The following change types are currently available: | |||
1556 | } | 1578 | } |
1557 | ``` | 1579 | ``` |
1558 | 1580 | ||
1581 | ## 0x80000001. OUTPUT | ||
1582 | |||
1583 | Sent whenever an output is added, removed, or its configuration is changed. | ||
1584 | The event is a single object with the property _change_, which is a string | ||
1585 | containing the reason for the change. Currently, the only value for _change_ is | ||
1586 | _unspecified_. | ||
1587 | |||
1588 | *Example Event:* | ||
1589 | ``` | ||
1590 | { | ||
1591 | "change": "unspecified" | ||
1592 | } | ||
1593 | ``` | ||
1594 | |||
1559 | ## 0x80000002. MODE | 1595 | ## 0x80000002. MODE |
1560 | 1596 | ||
1561 | Sent whenever the binding mode changes. The event consists of a single object | 1597 | Sent whenever the binding mode changes. The event consists of a single object |
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 2828c841..7d088d5d 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd | |||
@@ -24,7 +24,7 @@ must be separated by one space. For example: | |||
24 | 24 | ||
25 | # COMMANDS | 25 | # COMMANDS |
26 | 26 | ||
27 | *output* <name> mode|resolution|res [--custom] <WIDTHxHEIGHT>[@<RATE>Hz] | 27 | *output* <name> mode|resolution|res [--custom] <width>x<height>[@<rate>Hz] |
28 | Configures the specified output to use the given mode. Modes are a | 28 | Configures the specified output to use the given mode. Modes are a |
29 | combination of width and height (in pixels) and a refresh rate that your | 29 | combination of width and height (in pixels) and a refresh rate that your |
30 | display can be configured to use. For a list of available modes for each | 30 | display can be configured to use. For a list of available modes for each |
@@ -40,6 +40,16 @@ must be separated by one space. For example: | |||
40 | 40 | ||
41 | output HDMI-A-1 mode 1920x1080@60Hz | 41 | output HDMI-A-1 mode 1920x1080@60Hz |
42 | 42 | ||
43 | *output* <name> modeline <clock> <hdisplay> <hsync_start> <hsync_end> <htotal> <vdisplay> <vsync_start> <vsync_end> <vtotal> <hsync> <vsync> | ||
44 | Configures the specified output to use the given modeline. It can be | ||
45 | generated using *cvt*(1) and *gtf*(1) commands. See *xorg.conf*(5). | ||
46 | Only supported on DRM backend. | ||
47 | |||
48 | Example: | ||
49 | |||
50 | output HDMI-A-1 modeline 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync | ||
51 | |||
52 | |||
43 | *output* <name> position|pos <X> <Y> | 53 | *output* <name> position|pos <X> <Y> |
44 | Places the specified output at the specific position in the global | 54 | Places the specified output at the specific position in the global |
45 | coordinate space. The cursor may only be moved between immediately | 55 | coordinate space. The cursor may only be moved between immediately |
@@ -62,13 +72,11 @@ must be separated by one space. For example: | |||
62 | 72 | ||
63 | *output* <name> scale <factor> | 73 | *output* <name> scale <factor> |
64 | Scales the specified output by the specified scale _factor_. An integer is | 74 | Scales the specified output by the specified scale _factor_. An integer is |
65 | recommended, but fractional values are also supported. If a fractional | 75 | recommended, but fractional values are also supported. You may be better |
66 | value are specified, be warned that it is not possible to faithfully | 76 | served by setting an integer scale factor and adjusting the font size of |
67 | represent the contents of your windows - they will be rendered at the next | 77 | your applications to taste. HiDPI isn't supported with Xwayland clients |
68 | highest integer scale factor and downscaled. You may be better served by | 78 | (windows will blur). A fractional scale may be slightly adjusted to match |
69 | setting an integer scale factor and adjusting the font size of your | 79 | requirements of the protocol. |
70 | applications to taste. HiDPI isn't supported with Xwayland clients (windows | ||
71 | will blur). | ||
72 | 80 | ||
73 | *output* <name> scale_filter linear|nearest|smart | 81 | *output* <name> scale_filter linear|nearest|smart |
74 | Indicates how to scale application buffers that are rendered at a scale | 82 | Indicates how to scale application buffers that are rendered at a scale |
@@ -109,12 +117,20 @@ must be separated by one space. For example: | |||
109 | Enables or disables the specified output (all outputs are enabled by | 117 | Enables or disables the specified output (all outputs are enabled by |
110 | default). | 118 | default). |
111 | 119 | ||
120 | As opposed to the _power_ command, the output will lose its current | ||
121 | workspace and windows. | ||
122 | |||
112 | *output* <name> toggle | 123 | *output* <name> toggle |
113 | Toggle the specified output. | 124 | Toggle the specified output. |
114 | 125 | ||
126 | *output* <name> power on|off|toggle | ||
127 | Turns on or off the specified output. | ||
128 | |||
129 | As opposed to the _enable_ and _disable_ commands, the output keeps its | ||
130 | current workspaces and windows. | ||
131 | |||
115 | *output* <name> dpms on|off|toggle | 132 | *output* <name> dpms on|off|toggle |
116 | Enables or disables the specified output via DPMS. To turn an output off | 133 | Deprecated. Alias for _power_. |
117 | (ie. blank the screen but keep workspaces as-is), one can set DPMS to off. | ||
118 | 134 | ||
119 | *output* <name> max_render_time off|<msec> | 135 | *output* <name> max_render_time off|<msec> |
120 | Controls when sway composites the output, as a positive number of | 136 | Controls when sway composites the output, as a positive number of |
@@ -147,6 +163,21 @@ must be separated by one space. For example: | |||
147 | adaptive sync can improve latency, but can cause flickering on some | 163 | adaptive sync can improve latency, but can cause flickering on some |
148 | hardware. | 164 | hardware. |
149 | 165 | ||
166 | *output* <name> render_bit_depth 8|10 | ||
167 | Controls the color channel bit depth at which frames are rendered; the | ||
168 | default is currently 8 bits per channel. | ||
169 | |||
170 | Setting higher values will not have an effect if hardware and software lack | ||
171 | support for such bit depths. Successfully increasing the render bit depth | ||
172 | will not necessarily increase the bit depth of the frames sent to a display. | ||
173 | An increased render bit depth may provide smoother rendering of gradients, | ||
174 | and screenshots which can more precisely store the colors of programs | ||
175 | which display high bit depth colors. | ||
176 | |||
177 | Warnings: this can break screenshot/screencast programs which have not been | ||
178 | updated to work with different bit depths. This command is experimental, | ||
179 | and may be removed or changed in the future. | ||
180 | |||
150 | # SEE ALSO | 181 | # SEE ALSO |
151 | 182 | ||
152 | *sway*(5) *sway-input*(5) | 183 | *sway*(5) *sway-input*(5) |
diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 8958c7e3..9f823947 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd | |||
@@ -176,6 +176,12 @@ set|plus|minus|toggle <amount> | |||
176 | *layout* default|splith|splitv|stacking|tabbed | 176 | *layout* default|splith|splitv|stacking|tabbed |
177 | Sets the layout mode of the focused container. | 177 | Sets the layout mode of the focused container. |
178 | 178 | ||
179 | When using the _stacking_ layout, only the focused window in the container is | ||
180 | displayed, with the opened windows' list on the top of the container. | ||
181 | |||
182 | The _tabbed_ layout is similar to _stacking_, but the windows’ list is vertically | ||
183 | split. | ||
184 | |||
179 | *layout* toggle [split|all] | 185 | *layout* toggle [split|all] |
180 | Cycles the layout mode of the focused container though a preset list of | 186 | Cycles the layout mode of the focused container though a preset list of |
181 | layouts. If no argument is given, then it cycles through stacking, tabbed | 187 | layouts. If no argument is given, then it cycles through stacking, tabbed |
@@ -210,15 +216,14 @@ set|plus|minus|toggle <amount> | |||
210 | further details. | 216 | further details. |
211 | 217 | ||
212 | *move* left|right|up|down [<px> px] | 218 | *move* left|right|up|down [<px> px] |
213 | Moves the focused container in the direction specified. If the container, | 219 | Moves the focused container in the direction specified. The optional _px_ |
214 | the optional _px_ argument specifies how many pixels to move the container. | 220 | argument specifies how many pixels to move the container. If unspecified, |
215 | If unspecified, the default is 10 pixels. Pixels are ignored when moving | 221 | the default is 10 pixels. Pixels are ignored when moving tiled containers. |
216 | tiled containers. | ||
217 | 222 | ||
218 | *move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ptt] | 223 | *move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt] |
219 | Moves the focused container to the specified position in the workspace. | 224 | Moves the focused container to the specified position in the workspace. |
220 | The position can be specified in pixels or percentage points, omitting | 225 | The position can be specified in pixels or percentage points, omitting |
221 | the unit defaults to pixels. If _absolute_ is used, the position is | 226 | the unit defaults to pixels. If _absolute_ is used, the position is |
222 | relative to all outputs. _absolute_ can not be used with percentage points. | 227 | relative to all outputs. _absolute_ can not be used with percentage points. |
223 | 228 | ||
224 | *move* [absolute] position center | 229 | *move* [absolute] position center |
@@ -319,8 +324,10 @@ set|plus|minus|toggle <amount> | |||
319 | established by the *seat* subcommand of the same name. See | 324 | established by the *seat* subcommand of the same name. See |
320 | *sway-input*(5) for more ways to affect inhibitors. | 325 | *sway-input*(5) for more ways to affect inhibitors. |
321 | 326 | ||
322 | *split* vertical|v|horizontal|h|toggle|t | 327 | *split* vertical|v|horizontal|h|none|n|toggle|t |
323 | Splits the current container, vertically or horizontally. When _toggle_ is | 328 | Splits the current container, vertically or horizontally. When _none_ is |
329 | specified, the effect of a previous split is undone if the current | ||
330 | container is the only child of a split parent. When _toggle_ is | ||
324 | specified, the current container is split opposite to the parent | 331 | specified, the current container is split opposite to the parent |
325 | container's layout. | 332 | container's layout. |
326 | 333 | ||
@@ -382,8 +389,8 @@ runtime. | |||
382 | for_window <criteria> move container to output <output> | 389 | for_window <criteria> move container to output <output> |
383 | 390 | ||
384 | *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ | 391 | *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ |
385 | [--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [Group<1-4>+]<key combo> \ | 392 | [--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \ |
386 | <command> | 393 | [Group<1-4>+]<key combo> <command> |
387 | Binds _key combo_ to execute the sway command _command_ when pressed. You | 394 | Binds _key combo_ to execute the sway command _command_ when pressed. You |
388 | may use XKB key names here (*wev*(1) is a good tool for discovering these). | 395 | may use XKB key names here (*wev*(1) is a good tool for discovering these). |
389 | With the flag _--release_, the command is executed when the key combo is | 396 | With the flag _--release_, the command is executed when the key combo is |
@@ -393,6 +400,12 @@ runtime. | |||
393 | only be available for that group. By default, if you overwrite a binding, | 400 | only be available for that group. By default, if you overwrite a binding, |
394 | swaynag will give you a warning. To silence this, use the _--no-warn_ flag. | 401 | swaynag will give you a warning. To silence this, use the _--no-warn_ flag. |
395 | 402 | ||
403 | For specifying modifier keys, you can use the XKB modifier names _Shift_, | ||
404 | _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock), | ||
405 | _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for | ||
406 | AltGr). In addition, you can use the aliases _Ctrl_ (for Control), _Alt_ | ||
407 | (for Alt), and _Super_ (for the Logo key). | ||
408 | |||
396 | Unless the flag _--locked_ is set, the command will not be run when a | 409 | Unless the flag _--locked_ is set, the command will not be run when a |
397 | screen locking program is active. If there is a matching binding with | 410 | screen locking program is active. If there is a matching binding with |
398 | and without _--locked_, the one with will be preferred when locked and the | 411 | and without _--locked_, the one with will be preferred when locked and the |
@@ -404,7 +417,7 @@ runtime. | |||
404 | a keyboard shortcuts inhibitor is active for the currently focused | 417 | a keyboard shortcuts inhibitor is active for the currently focused |
405 | window. Such inhibitors are usually requested by remote desktop and | 418 | window. Such inhibitors are usually requested by remote desktop and |
406 | virtualization software to enable the user to send keyboard shortcuts | 419 | virtualization software to enable the user to send keyboard shortcuts |
407 | to the remote or virtual session. The _--inhibited_ flag allows to | 420 | to the remote or virtual session. The _--inhibited_ flag allows one to |
408 | define bindings which will be exempt from pass-through to such | 421 | define bindings which will be exempt from pass-through to such |
409 | software. The same preference logic as for _--locked_ applies. | 422 | software. The same preference logic as for _--locked_ applies. |
410 | 423 | ||
@@ -447,7 +460,8 @@ runtime. | |||
447 | ``` | 460 | ``` |
448 | 461 | ||
449 | *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ | 462 | *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ |
450 | [--locked] [--input-device=<device>] [--no-warn] [Group<1-4>+]<code> <command> | 463 | [--locked] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \ |
464 | [Group<1-4>+]<code> <command> | ||
451 | is also available for binding with key/button codes instead of key/button names. | 465 | is also available for binding with key/button codes instead of key/button names. |
452 | 466 | ||
453 | *bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> | 467 | *bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> |
@@ -480,6 +494,62 @@ runtime. | |||
480 | bindswitch lid:toggle exec echo "Lid moved" | 494 | bindswitch lid:toggle exec echo "Lid moved" |
481 | ``` | 495 | ``` |
482 | 496 | ||
497 | *bindgesture* [--exact] [--input-device=<device>] [--no-warn] \ | ||
498 | <gesture>[:<fingers>][:directions] <command> | ||
499 | Binds _gesture_ to execute the sway command _command_ when detected. | ||
500 | Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally | ||
501 | can be limited to bind to a certain number of _fingers_ or, for a | ||
502 | _pinch_ or _swipe_ gesture, to certain _directions_. | ||
503 | |||
504 | [[ *type* | ||
505 | :[ *fingers* | ||
506 | :< *direction* | ||
507 | | hold | ||
508 | :- 1 - 5 | ||
509 | : none | ||
510 | | swipe | ||
511 | : 3 - 5 | ||
512 | : up, down, left, right | ||
513 | | pinch | ||
514 | : 2 - 5 | ||
515 | : all above + inward, outward, clockwise, counterclockwise | ||
516 | |||
517 | The _fingers_ can be limited to any sensible number or left empty to accept | ||
518 | any finger counts. | ||
519 | Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_, | ||
520 | _outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture. | ||
521 | Multiple directions can be combined by a plus. | ||
522 | |||
523 | If a _input-device_ is given, the binding will only be executed for | ||
524 | that input device and will be executed instead of any binding that is | ||
525 | generic to all devices. By default, if you overwrite a binding, | ||
526 | swaynag will give you a warning. To silence this, use the _--no-warn_ flag. | ||
527 | |||
528 | The _--exact_ flag can be used to ensure a binding only matches when exactly | ||
529 | all specified directions are matched and nothing more. If there is matching | ||
530 | binding with _--exact_, it will be preferred. | ||
531 | |||
532 | The priority for matching bindings is as follows: input device, then | ||
533 | exact matches followed by matches with the highest number of matching | ||
534 | directions. | ||
535 | |||
536 | Gestures executed while the pointer is above a bar are not handled by sway. | ||
537 | See the respective documentation, e.g. *bindgesture* in *sway-bar*(5). | ||
538 | |||
539 | Example: | ||
540 | ``` | ||
541 | # Allow switching between workspaces with left and right swipes | ||
542 | bindgesture swipe:right workspace prev | ||
543 | bindgesture swipe:left workspace next | ||
544 | |||
545 | # Allow container movements by pinching them | ||
546 | bindgesture pinch:inward+up move up | ||
547 | bindgesture pinch:inward+down move down | ||
548 | bindgesture pinch:inward+left move left | ||
549 | bindgesture pinch:inward+right move right | ||
550 | |||
551 | ``` | ||
552 | |||
483 | *client.background* <color> | 553 | *client.background* <color> |
484 | This command is ignored and is only present for i3 compatibility. | 554 | This command is ignored and is only present for i3 compatibility. |
485 | 555 | ||
@@ -497,6 +567,12 @@ runtime. | |||
497 | *client.focused_inactive* | 567 | *client.focused_inactive* |
498 | The most recently focused view within a container which is not focused. | 568 | The most recently focused view within a container which is not focused. |
499 | 569 | ||
570 | *client.focused_tab_title* | ||
571 | A view that has focused descendant container. | ||
572 | Tab or stack container title that is the parent of the focused container | ||
573 | but is not directly focused. Defaults to focused_inactive if not | ||
574 | specified and does not use the indicator and child_border colors. | ||
575 | |||
500 | *client.placeholder* | 576 | *client.placeholder* |
501 | Ignored (present for i3 compatibility). | 577 | Ignored (present for i3 compatibility). |
502 | 578 | ||
@@ -552,6 +628,12 @@ The default colors are: | |||
552 | : #ffffff | 628 | : #ffffff |
553 | : #484e50 | 629 | : #484e50 |
554 | : #5f676a | 630 | : #5f676a |
631 | | *focused_tab_title* | ||
632 | : #333333 | ||
633 | : #5f676a | ||
634 | : #ffffff | ||
635 | : n/a | ||
636 | : n/a | ||
555 | | *unfocused* | 637 | | *unfocused* |
556 | : #333333 | 638 | : #333333 |
557 | : #222222 | 639 | : #222222 |
@@ -573,7 +655,8 @@ The default colors are: | |||
573 | 655 | ||
574 | 656 | ||
575 | *default_border* normal|none|pixel [<n>] | 657 | *default_border* normal|none|pixel [<n>] |
576 | Set default border style for new tiled windows. | 658 | Set default border style for new tiled windows. Config reload won't affect |
659 | existing windows, only newly created ones after the reload. | ||
577 | 660 | ||
578 | *default_floating_border* normal|none|pixel [<n>] | 661 | *default_floating_border* normal|none|pixel [<n>] |
579 | Set default border style for new floating windows. This only applies to | 662 | Set default border style for new floating windows. This only applies to |
@@ -606,11 +689,11 @@ The default colors are: | |||
606 | after switching between workspaces. | 689 | after switching between workspaces. |
607 | 690 | ||
608 | *focus_on_window_activation* smart|urgent|focus|none | 691 | *focus_on_window_activation* smart|urgent|focus|none |
609 | This option determines what to do when an xwayland client requests | 692 | This option determines what to do when a client requests window activation. |
610 | window activation. If set to _urgent_, the urgent state will be set | 693 | If set to _urgent_, the urgent state will be set for that window. If set to |
611 | for that window. If set to _focus_, the window will become focused. | 694 | _focus_, the window will become focused. If set to _smart_, the window will |
612 | If set to _smart_, the window will become focused only if it is already | 695 | become focused only if it is already visible, otherwise the urgent state |
613 | visible, otherwise the urgent state will be set. Default is _urgent_. | 696 | will be set. Default is _urgent_. |
614 | 697 | ||
615 | *focus_wrapping* yes|no|force|workspace | 698 | *focus_wrapping* yes|no|force|workspace |
616 | This option determines what to do when attempting to focus over the edge | 699 | This option determines what to do when attempting to focus over the edge |
@@ -630,14 +713,14 @@ The default colors are: | |||
630 | should be used instead. Regardless of whether pango markup is enabled, | 713 | should be used instead. Regardless of whether pango markup is enabled, |
631 | _font_ should be specified as a pango font description. For more | 714 | _font_ should be specified as a pango font description. For more |
632 | information on pango font descriptions, see | 715 | information on pango font descriptions, see |
633 | https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string | 716 | https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description |
634 | 717 | ||
635 | *force_display_urgency_hint* <timeout> [ms] | 718 | *force_display_urgency_hint* <timeout> [ms] |
636 | If an application on another workspace sets an urgency hint, switching to this | 719 | If an application on another workspace sets an urgency hint, switching to this |
637 | workspace may lead to immediate focus of the application, which also means the | 720 | workspace may lead to immediate focus of the application, which also means the |
638 | window decoration color would be immediately resetted to *client.focused*. This | 721 | window decoration color would be immediately reset to *client.focused*. This |
639 | may make it unnecessarily hard to tell which window originally raised the | 722 | may make it unnecessarily hard to tell which window originally raised the |
640 | event. This option allows to set a _timeout_ in ms to delay the urgency hint reset. | 723 | event. This option allows one to set a _timeout_ in ms to delay the urgency hint reset. |
641 | 724 | ||
642 | *titlebar_border_thickness* <thickness> | 725 | *titlebar_border_thickness* <thickness> |
643 | Thickness of the titlebar border in pixels | 726 | Thickness of the titlebar border in pixels |
@@ -690,9 +773,10 @@ The default colors are: | |||
690 | borders will only be enabled if the workspace has more than one visible | 773 | borders will only be enabled if the workspace has more than one visible |
691 | child and gaps equal to zero. | 774 | child and gaps equal to zero. |
692 | 775 | ||
693 | *smart_gaps* on|off | 776 | *smart_gaps* on|off|toggle|inverse_outer |
694 | If smart_gaps are _on_ gaps will only be enabled if a workspace has more | 777 | If smart_gaps are _on_ gaps will only be enabled if a workspace has more |
695 | than one child. | 778 | than one child. If smart_gaps are _inverse_outer_ outer gaps will only |
779 | be enabled if a workspace has exactly one child. | ||
696 | 780 | ||
697 | *mark* --add|--replace [--toggle] <identifier> | 781 | *mark* --add|--replace [--toggle] <identifier> |
698 | Marks are arbitrary labels that can be used to identify certain windows and | 782 | Marks are arbitrary labels that can be used to identify certain windows and |
@@ -731,6 +815,10 @@ The default colors are: | |||
731 | dialog will not be rendered. If _leave_fullscreen_, the view will exit | 815 | dialog will not be rendered. If _leave_fullscreen_, the view will exit |
732 | fullscreen mode and the dialog will be rendered. | 816 | fullscreen mode and the dialog will be rendered. |
733 | 817 | ||
818 | *primary_selection* enabled|disabled | ||
819 | Enable or disable the primary selection clipboard. May only be configured | ||
820 | at launch. Default is _enabled_. | ||
821 | |||
734 | *set* $<name> <value> | 822 | *set* $<name> <value> |
735 | Sets variable $_name_ to _value_. You can use the new variable in the | 823 | Sets variable $_name_ to _value_. You can use the new variable in the |
736 | arguments of future commands. When the variable is used, it can be escaped | 824 | arguments of future commands. When the variable is used, it can be escaped |
@@ -771,6 +859,11 @@ The default colors are: | |||
771 | *unbindswitch* <switch>:<state> | 859 | *unbindswitch* <switch>:<state> |
772 | Removes a binding for when <switch> changes to <state>. | 860 | Removes a binding for when <switch> changes to <state>. |
773 | 861 | ||
862 | *unbindgesture* [--exact] [--input-device=<device>] \ | ||
863 | <gesture>[:<fingers>][:directions] | ||
864 | Removes a binding for the specified _gesture_, _fingers_ | ||
865 | and _directions_ combination. | ||
866 | |||
774 | *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ | 867 | *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ |
775 | [--to-code] [--input-device=<device>] <key combo> | 868 | [--to-code] [--input-device=<device>] <key combo> |
776 | Removes the binding for _key combo_ that was previously bound with the | 869 | Removes the binding for _key combo_ that was previously bound with the |
@@ -876,6 +969,9 @@ properties in practice for your applications. | |||
876 | 969 | ||
877 | The following attributes may be matched with: | 970 | The following attributes may be matched with: |
878 | 971 | ||
972 | *all* | ||
973 | Matches all windows. | ||
974 | |||
879 | *app_id* | 975 | *app_id* |
880 | Compare value against the app id. Can be a regular expression. If value is | 976 | Compare value against the app id. Can be a regular expression. If value is |
881 | \_\_focused\_\_, then the app id must be the same as that of the currently | 977 | \_\_focused\_\_, then the app id must be the same as that of the currently |
@@ -884,7 +980,8 @@ The following attributes may be matched with: | |||
884 | *class* | 980 | *class* |
885 | Compare value against the window class. Can be a regular expression. If | 981 | Compare value against the window class. Can be a regular expression. If |
886 | value is \_\_focused\_\_, then the window class must be the same as that of | 982 | value is \_\_focused\_\_, then the window class must be the same as that of |
887 | the currently focused window. _class_ are specific to X11 applications. | 983 | the currently focused window. _class_ are specific to X11 applications and |
984 | require XWayland. | ||
888 | 985 | ||
889 | *con_id* | 986 | *con_id* |
890 | Compare against the internal container ID, which you can find via IPC. If | 987 | Compare against the internal container ID, which you can find via IPC. If |
@@ -898,12 +995,14 @@ The following attributes may be matched with: | |||
898 | Matches floating windows. | 995 | Matches floating windows. |
899 | 996 | ||
900 | *id* | 997 | *id* |
901 | Compare value against the X11 window ID. Must be numeric. | 998 | Compare value against the X11 window ID. Must be numeric. id is specific to |
999 | X11 applications and requires XWayland. | ||
902 | 1000 | ||
903 | *instance* | 1001 | *instance* |
904 | Compare value against the window instance. Can be a regular expression. If | 1002 | Compare value against the window instance. Can be a regular expression. If |
905 | value is \_\_focused\_\_, then the window instance must be the same as that | 1003 | value is \_\_focused\_\_, then the window instance must be the same as that |
906 | of the currently focused window. | 1004 | of the currently focused window. instance is specific to X11 applications and |
1005 | requires XWayland. | ||
907 | 1006 | ||
908 | *pid* | 1007 | *pid* |
909 | Compare value against the window's process ID. Must be numeric. | 1008 | Compare value against the window's process ID. Must be numeric. |
@@ -928,12 +1027,14 @@ The following attributes may be matched with: | |||
928 | *window_role* | 1027 | *window_role* |
929 | Compare against the window role (WM_WINDOW_ROLE). Can be a regular | 1028 | Compare against the window role (WM_WINDOW_ROLE). Can be a regular |
930 | expression. If value is \_\_focused\_\_, then the window role must be the | 1029 | expression. If value is \_\_focused\_\_, then the window role must be the |
931 | same as that of the currently focused window. | 1030 | same as that of the currently focused window. window_role is specific to X11 |
1031 | applications and requires XWayland. | ||
932 | 1032 | ||
933 | *window_type* | 1033 | *window_type* |
934 | Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values | 1034 | Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values |
935 | are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, | 1035 | are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, |
936 | popup_menu, tooltip and notification. | 1036 | popup_menu, tooltip and notification. window_type is specific to X11 |
1037 | applications and requires XWayland. | ||
937 | 1038 | ||
938 | *workspace* | 1039 | *workspace* |
939 | Compare against the workspace name for this view. Can be a regular | 1040 | Compare against the workspace name for this view. Can be a regular |
diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c new file mode 100644 index 00000000..5eba53ba --- /dev/null +++ b/sway/sway_text_node.c | |||
@@ -0,0 +1,302 @@ | |||
1 | #include <drm_fourcc.h> | ||
2 | #include <stdio.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <string.h> | ||
5 | #include <wlr/types/wlr_buffer.h> | ||
6 | #include <wlr/interfaces/wlr_buffer.h> | ||
7 | #include "cairo_util.h" | ||
8 | #include "log.h" | ||
9 | #include "pango.h" | ||
10 | #include "sway/config.h" | ||
11 | #include "sway/sway_text_node.h" | ||
12 | |||
13 | struct cairo_buffer { | ||
14 | struct wlr_buffer base; | ||
15 | cairo_surface_t *surface; | ||
16 | cairo_t *cairo; | ||
17 | }; | ||
18 | |||
19 | static void cairo_buffer_handle_destroy(struct wlr_buffer *wlr_buffer) { | ||
20 | struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); | ||
21 | |||
22 | cairo_surface_destroy(buffer->surface); | ||
23 | cairo_destroy(buffer->cairo); | ||
24 | free(buffer); | ||
25 | } | ||
26 | |||
27 | static bool cairo_buffer_handle_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, | ||
28 | uint32_t flags, void **data, uint32_t *format, size_t *stride) { | ||
29 | struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); | ||
30 | *data = cairo_image_surface_get_data(buffer->surface); | ||
31 | *stride = cairo_image_surface_get_stride(buffer->surface); | ||
32 | *format = DRM_FORMAT_ARGB8888; | ||
33 | return true; | ||
34 | } | ||
35 | |||
36 | static void cairo_buffer_handle_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { | ||
37 | // This space is intentionally left blank | ||
38 | } | ||
39 | |||
40 | static const struct wlr_buffer_impl cairo_buffer_impl = { | ||
41 | .destroy = cairo_buffer_handle_destroy, | ||
42 | .begin_data_ptr_access = cairo_buffer_handle_begin_data_ptr_access, | ||
43 | .end_data_ptr_access = cairo_buffer_handle_end_data_ptr_access, | ||
44 | }; | ||
45 | |||
46 | struct text_buffer { | ||
47 | struct wlr_scene_buffer *buffer_node; | ||
48 | char *text; | ||
49 | struct sway_text_node props; | ||
50 | |||
51 | bool visible; | ||
52 | float scale; | ||
53 | enum wl_output_subpixel subpixel; | ||
54 | |||
55 | struct wl_listener outputs_update; | ||
56 | struct wl_listener destroy; | ||
57 | }; | ||
58 | |||
59 | static int get_text_width(struct sway_text_node *props) { | ||
60 | int width = props->width; | ||
61 | if (props->max_width) { | ||
62 | width = MIN(width, props->max_width); | ||
63 | } | ||
64 | return MAX(width, 0); | ||
65 | } | ||
66 | |||
67 | static void update_source_box(struct text_buffer *buffer) { | ||
68 | struct sway_text_node *props = &buffer->props; | ||
69 | struct wlr_fbox source_box = { | ||
70 | .x = 0, | ||
71 | .y = 0, | ||
72 | .width = ceil(get_text_width(props) * buffer->scale), | ||
73 | .height = ceil(props->height * buffer->scale), | ||
74 | }; | ||
75 | |||
76 | wlr_scene_buffer_set_source_box(buffer->buffer_node, &source_box); | ||
77 | } | ||
78 | |||
79 | static void render_backing_buffer(struct text_buffer *buffer) { | ||
80 | if (!buffer->visible) { | ||
81 | return; | ||
82 | } | ||
83 | |||
84 | float scale = buffer->scale; | ||
85 | int width = ceil(buffer->props.width * scale); | ||
86 | int height = ceil(buffer->props.height * scale); | ||
87 | float *color = (float *)&buffer->props.color; | ||
88 | float *background = (float *)&buffer->props.background; | ||
89 | PangoContext *pango = NULL; | ||
90 | |||
91 | cairo_font_options_t *fo = cairo_font_options_create(); | ||
92 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
93 | enum wl_output_subpixel subpixel = buffer->subpixel; | ||
94 | if (subpixel == WL_OUTPUT_SUBPIXEL_NONE || subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { | ||
95 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); | ||
96 | } else { | ||
97 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | ||
98 | cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(subpixel)); | ||
99 | } | ||
100 | |||
101 | cairo_surface_t *surface = cairo_image_surface_create( | ||
102 | CAIRO_FORMAT_ARGB32, width, height); | ||
103 | cairo_status_t status = cairo_surface_status(surface); | ||
104 | if (status != CAIRO_STATUS_SUCCESS) { | ||
105 | sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", | ||
106 | cairo_status_to_string(status)); | ||
107 | goto err; | ||
108 | } | ||
109 | |||
110 | struct cairo_buffer *cairo_buffer = calloc(1, sizeof(*cairo_buffer)); | ||
111 | if (!cairo_buffer) { | ||
112 | sway_log(SWAY_ERROR, "cairo_buffer allocation failed"); | ||
113 | goto err; | ||
114 | } | ||
115 | |||
116 | cairo_t *cairo = cairo_create(surface); | ||
117 | if (!cairo) { | ||
118 | sway_log(SWAY_ERROR, "cairo_create failed"); | ||
119 | free(cairo_buffer); | ||
120 | goto err; | ||
121 | } | ||
122 | |||
123 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
124 | cairo_set_font_options(cairo, fo); | ||
125 | pango = pango_cairo_create_context(cairo); | ||
126 | |||
127 | cairo_set_source_rgba(cairo, background[0], background[1], background[2], background[3]); | ||
128 | cairo_rectangle(cairo, 0, 0, width, height); | ||
129 | cairo_fill(cairo); | ||
130 | |||
131 | cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]); | ||
132 | cairo_move_to(cairo, 0, (config->font_baseline - buffer->props.baseline) * scale); | ||
133 | |||
134 | render_text(cairo, config->font_description, scale, buffer->props.pango_markup, | ||
135 | "%s", buffer->text); | ||
136 | |||
137 | cairo_surface_flush(surface); | ||
138 | |||
139 | wlr_buffer_init(&cairo_buffer->base, &cairo_buffer_impl, width, height); | ||
140 | cairo_buffer->surface = surface; | ||
141 | cairo_buffer->cairo = cairo; | ||
142 | |||
143 | wlr_scene_buffer_set_buffer(buffer->buffer_node, &cairo_buffer->base); | ||
144 | wlr_buffer_drop(&cairo_buffer->base); | ||
145 | update_source_box(buffer); | ||
146 | |||
147 | pixman_region32_t opaque; | ||
148 | pixman_region32_init(&opaque); | ||
149 | if (background[3] == 1) { | ||
150 | pixman_region32_union_rect(&opaque, &opaque, 0, 0, | ||
151 | buffer->props.width, buffer->props.height); | ||
152 | } | ||
153 | wlr_scene_buffer_set_opaque_region(buffer->buffer_node, &opaque); | ||
154 | pixman_region32_fini(&opaque); | ||
155 | |||
156 | err: | ||
157 | if (pango) g_object_unref(pango); | ||
158 | cairo_font_options_destroy(fo); | ||
159 | } | ||
160 | |||
161 | static void handle_outputs_update(struct wl_listener *listener, void *data) { | ||
162 | struct text_buffer *buffer = wl_container_of(listener, buffer, outputs_update); | ||
163 | struct wlr_scene_outputs_update_event *event = data; | ||
164 | |||
165 | float scale = 0; | ||
166 | enum wl_output_subpixel subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | ||
167 | |||
168 | for (size_t i = 0; i < event->size; i++) { | ||
169 | struct wlr_scene_output *output = event->active[i]; | ||
170 | if (subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { | ||
171 | subpixel = output->output->subpixel; | ||
172 | } else if (subpixel != output->output->subpixel) { | ||
173 | subpixel = WL_OUTPUT_SUBPIXEL_NONE; | ||
174 | } | ||
175 | |||
176 | if (scale != 0 && scale != output->output->scale) { | ||
177 | // drop down to gray scale if we encounter outputs with different | ||
178 | // scales or else we will have chromatic aberations | ||
179 | subpixel = WL_OUTPUT_SUBPIXEL_NONE; | ||
180 | } | ||
181 | |||
182 | if (scale < output->output->scale) { | ||
183 | scale = output->output->scale; | ||
184 | } | ||
185 | } | ||
186 | |||
187 | buffer->visible = event->size > 0; | ||
188 | |||
189 | if (scale != buffer->scale || subpixel != buffer->subpixel) { | ||
190 | buffer->scale = scale; | ||
191 | buffer->subpixel = subpixel; | ||
192 | render_backing_buffer(buffer); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
197 | struct text_buffer *buffer = wl_container_of(listener, buffer, destroy); | ||
198 | |||
199 | wl_list_remove(&buffer->outputs_update.link); | ||
200 | wl_list_remove(&buffer->destroy.link); | ||
201 | |||
202 | free(buffer->text); | ||
203 | free(buffer); | ||
204 | } | ||
205 | |||
206 | static void text_calc_size(struct text_buffer *buffer) { | ||
207 | struct sway_text_node *props = &buffer->props; | ||
208 | |||
209 | cairo_t *c = cairo_create(NULL); | ||
210 | if (!c) { | ||
211 | sway_log(SWAY_ERROR, "cairo_t allocation failed"); | ||
212 | return; | ||
213 | } | ||
214 | |||
215 | cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); | ||
216 | get_text_size(c, config->font_description, &props->width, NULL, | ||
217 | &props->baseline, 1, props->pango_markup, "%s", buffer->text); | ||
218 | cairo_destroy(c); | ||
219 | |||
220 | wlr_scene_buffer_set_dest_size(buffer->buffer_node, | ||
221 | get_text_width(props), props->height); | ||
222 | } | ||
223 | |||
224 | struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, | ||
225 | char *text, float color[4], bool pango_markup) { | ||
226 | struct text_buffer *buffer = calloc(1, sizeof(*buffer)); | ||
227 | if (!buffer) { | ||
228 | return NULL; | ||
229 | } | ||
230 | |||
231 | struct wlr_scene_buffer *node = wlr_scene_buffer_create(parent, NULL); | ||
232 | if (!node) { | ||
233 | free(buffer); | ||
234 | return NULL; | ||
235 | } | ||
236 | |||
237 | buffer->buffer_node = node; | ||
238 | buffer->props.node = &node->node; | ||
239 | buffer->text = strdup(text); | ||
240 | if (!buffer->text) { | ||
241 | free(buffer); | ||
242 | wlr_scene_node_destroy(&node->node); | ||
243 | return NULL; | ||
244 | } | ||
245 | |||
246 | buffer->props.height = config->font_height; | ||
247 | buffer->props.pango_markup = pango_markup; | ||
248 | memcpy(&buffer->props.color, color, sizeof(*color) * 4); | ||
249 | |||
250 | buffer->destroy.notify = handle_destroy; | ||
251 | wl_signal_add(&node->node.events.destroy, &buffer->destroy); | ||
252 | buffer->outputs_update.notify = handle_outputs_update; | ||
253 | wl_signal_add(&node->events.outputs_update, &buffer->outputs_update); | ||
254 | |||
255 | text_calc_size(buffer); | ||
256 | |||
257 | return &buffer->props; | ||
258 | } | ||
259 | |||
260 | void sway_text_node_set_color(struct sway_text_node *node, float color[4]) { | ||
261 | if (memcmp(&node->color, color, sizeof(*color) * 4) == 0) { | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | memcpy(&node->color, color, sizeof(*color) * 4); | ||
266 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
267 | |||
268 | render_backing_buffer(buffer); | ||
269 | } | ||
270 | |||
271 | void sway_text_node_set_text(struct sway_text_node *node, char *text) { | ||
272 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
273 | if (strcmp(buffer->text, text) == 0) { | ||
274 | return; | ||
275 | } | ||
276 | |||
277 | char *new_text = strdup(text); | ||
278 | if (!new_text) { | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | free(buffer->text); | ||
283 | buffer->text = new_text; | ||
284 | |||
285 | text_calc_size(buffer); | ||
286 | render_backing_buffer(buffer); | ||
287 | } | ||
288 | |||
289 | void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) { | ||
290 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
291 | buffer->props.max_width = max_width; | ||
292 | wlr_scene_buffer_set_dest_size(buffer->buffer_node, | ||
293 | get_text_width(&buffer->props), buffer->props.height); | ||
294 | update_source_box(buffer); | ||
295 | render_backing_buffer(buffer); | ||
296 | } | ||
297 | |||
298 | void sway_text_node_set_background(struct sway_text_node *node, float background[4]) { | ||
299 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
300 | memcpy(&node->background, background, sizeof(*background) * 4); | ||
301 | render_backing_buffer(buffer); | ||
302 | } | ||
diff --git a/sway/swaynag.c b/sway/swaynag.c index ba582989..bc5e23ea 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <signal.h> | 1 | #include <signal.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -64,6 +63,8 @@ bool swaynag_spawn(const char *swaynag_command, | |||
64 | sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); | 63 | sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); |
65 | goto failed; | 64 | goto failed; |
66 | } else if (pid == 0) { | 65 | } else if (pid == 0) { |
66 | restore_nofile_limit(); | ||
67 | |||
67 | pid = fork(); | 68 | pid = fork(); |
68 | if (pid < 0) { | 69 | if (pid < 0) { |
69 | sway_log_errno(SWAY_ERROR, "fork failed"); | 70 | sway_log_errno(SWAY_ERROR, "fork failed"); |
@@ -143,22 +144,16 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, | |||
143 | 144 | ||
144 | va_list args; | 145 | va_list args; |
145 | va_start(args, fmt); | 146 | va_start(args, fmt); |
146 | size_t length = vsnprintf(NULL, 0, fmt, args) + 1; | 147 | char *str = vformat_str(fmt, args); |
147 | va_end(args); | 148 | va_end(args); |
148 | 149 | if (!str) { | |
149 | char *temp = malloc(length + 1); | ||
150 | if (!temp) { | ||
151 | sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry."); | 150 | sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry."); |
152 | return; | 151 | return; |
153 | } | 152 | } |
154 | 153 | ||
155 | va_start(args, fmt); | 154 | write(swaynag->fd[1], str, strlen(str)); |
156 | vsnprintf(temp, length, fmt, args); | ||
157 | va_end(args); | ||
158 | |||
159 | write(swaynag->fd[1], temp, length); | ||
160 | 155 | ||
161 | free(temp); | 156 | free(str); |
162 | } | 157 | } |
163 | 158 | ||
164 | void swaynag_show(struct swaynag_instance *swaynag) { | 159 | void swaynag_show(struct swaynag_instance *swaynag) { |
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 4aa82c35..d4003fe6 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -264,6 +263,9 @@ void arrange_workspace(struct sway_workspace *workspace) { | |||
264 | area->width, area->height, area->x, area->y); | 263 | area->width, area->height, area->x, area->y); |
265 | 264 | ||
266 | bool first_arrange = workspace->width == 0 && workspace->height == 0; | 265 | bool first_arrange = workspace->width == 0 && workspace->height == 0; |
266 | struct wlr_box prev_box; | ||
267 | workspace_get_box(workspace, &prev_box); | ||
268 | |||
267 | double prev_x = workspace->x - workspace->current_gaps.left; | 269 | double prev_x = workspace->x - workspace->current_gaps.left; |
268 | double prev_y = workspace->y - workspace->current_gaps.top; | 270 | double prev_y = workspace->y - workspace->current_gaps.top; |
269 | workspace->width = area->width; | 271 | workspace->width = area->width; |
@@ -277,13 +279,14 @@ void arrange_workspace(struct sway_workspace *workspace) { | |||
277 | if (!first_arrange && (diff_x != 0 || diff_y != 0)) { | 279 | if (!first_arrange && (diff_x != 0 || diff_y != 0)) { |
278 | for (int i = 0; i < workspace->floating->length; ++i) { | 280 | for (int i = 0; i < workspace->floating->length; ++i) { |
279 | struct sway_container *floater = workspace->floating->items[i]; | 281 | struct sway_container *floater = workspace->floating->items[i]; |
280 | container_floating_translate(floater, diff_x, diff_y); | ||
281 | double center_x = floater->pending.x + floater->pending.width / 2; | ||
282 | double center_y = floater->pending.y + floater->pending.height / 2; | ||
283 | struct wlr_box workspace_box; | 282 | struct wlr_box workspace_box; |
284 | workspace_get_box(workspace, &workspace_box); | 283 | workspace_get_box(workspace, &workspace_box); |
285 | if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { | 284 | floating_fix_coordinates(floater, &prev_box, &workspace_box); |
286 | container_floating_move_to_center(floater); | 285 | // Set transformation for scratchpad windows. |
286 | if (floater->scratchpad) { | ||
287 | struct wlr_box output_box; | ||
288 | output_get_box(output, &output_box); | ||
289 | floater->transform = output_box; | ||
287 | } | 290 | } |
288 | } | 291 | } |
289 | } | 292 | } |
@@ -311,12 +314,13 @@ void arrange_output(struct sway_output *output) { | |||
311 | if (config->reloading) { | 314 | if (config->reloading) { |
312 | return; | 315 | return; |
313 | } | 316 | } |
314 | const struct wlr_box *output_box = wlr_output_layout_get_box( | 317 | struct wlr_box output_box; |
315 | root->output_layout, output->wlr_output); | 318 | wlr_output_layout_get_box(root->output_layout, |
316 | output->lx = output_box->x; | 319 | output->wlr_output, &output_box); |
317 | output->ly = output_box->y; | 320 | output->lx = output_box.x; |
318 | output->width = output_box->width; | 321 | output->ly = output_box.y; |
319 | output->height = output_box->height; | 322 | output->width = output_box.width; |
323 | output->height = output_box.height; | ||
320 | 324 | ||
321 | for (int i = 0; i < output->workspaces->length; ++i) { | 325 | for (int i = 0; i < output->workspaces->length; ++i) { |
322 | struct sway_workspace *workspace = output->workspaces->items[i]; | 326 | struct sway_workspace *workspace = output->workspaces->items[i]; |
@@ -328,12 +332,12 @@ void arrange_root(void) { | |||
328 | if (config->reloading) { | 332 | if (config->reloading) { |
329 | return; | 333 | return; |
330 | } | 334 | } |
331 | const struct wlr_box *layout_box = | 335 | struct wlr_box layout_box; |
332 | wlr_output_layout_get_box(root->output_layout, NULL); | 336 | wlr_output_layout_get_box(root->output_layout, NULL, &layout_box); |
333 | root->x = layout_box->x; | 337 | root->x = layout_box.x; |
334 | root->y = layout_box->y; | 338 | root->y = layout_box.y; |
335 | root->width = layout_box->width; | 339 | root->width = layout_box.width; |
336 | root->height = layout_box->height; | 340 | root->height = layout_box.height; |
337 | 341 | ||
338 | if (root->fullscreen_global) { | 342 | if (root->fullscreen_global) { |
339 | struct sway_container *fs = root->fullscreen_global; | 343 | struct sway_container *fs = root->fullscreen_global; |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 1e84e603..9224b4fb 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -1,29 +1,77 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <drm_fourcc.h> | 2 | #include <drm_fourcc.h> |
4 | #include <stdint.h> | 3 | #include <stdint.h> |
5 | #include <stdlib.h> | 4 | #include <stdlib.h> |
6 | #include <string.h> | ||
7 | #include <strings.h> | ||
8 | #include <wayland-server-core.h> | 5 | #include <wayland-server-core.h> |
6 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
7 | #include <wlr/types/wlr_linux_dmabuf_v1.h> | ||
9 | #include <wlr/types/wlr_output_layout.h> | 8 | #include <wlr/types/wlr_output_layout.h> |
10 | #include "cairo_util.h" | 9 | #include <wlr/types/wlr_subcompositor.h> |
11 | #include "pango.h" | 10 | #include "linux-dmabuf-unstable-v1-protocol.h" |
12 | #include "sway/config.h" | 11 | #include "sway/config.h" |
13 | #include "sway/desktop.h" | ||
14 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
15 | #include "sway/input/input-manager.h" | 13 | #include "sway/input/input-manager.h" |
16 | #include "sway/input/seat.h" | 14 | #include "sway/input/seat.h" |
17 | #include "sway/ipc-server.h" | 15 | #include "sway/ipc-server.h" |
16 | #include "sway/scene_descriptor.h" | ||
17 | #include "sway/sway_text_node.h" | ||
18 | #include "sway/output.h" | 18 | #include "sway/output.h" |
19 | #include "sway/server.h" | 19 | #include "sway/server.h" |
20 | #include "sway/tree/arrange.h" | 20 | #include "sway/tree/arrange.h" |
21 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
22 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
23 | #include "sway/xdg_decoration.h" | ||
23 | #include "list.h" | 24 | #include "list.h" |
24 | #include "log.h" | 25 | #include "log.h" |
25 | #include "stringop.h" | 26 | #include "stringop.h" |
26 | 27 | ||
28 | static void handle_output_enter( | ||
29 | struct wl_listener *listener, void *data) { | ||
30 | struct sway_container *con = wl_container_of( | ||
31 | listener, con, output_enter); | ||
32 | struct wlr_scene_output *output = data; | ||
33 | |||
34 | if (con->view->foreign_toplevel) { | ||
35 | wlr_foreign_toplevel_handle_v1_output_enter( | ||
36 | con->view->foreign_toplevel, output->output); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | static void handle_output_leave( | ||
41 | struct wl_listener *listener, void *data) { | ||
42 | struct sway_container *con = wl_container_of( | ||
43 | listener, con, output_leave); | ||
44 | struct wlr_scene_output *output = data; | ||
45 | |||
46 | if (con->view->foreign_toplevel) { | ||
47 | wlr_foreign_toplevel_handle_v1_output_leave( | ||
48 | con->view->foreign_toplevel, output->output); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | static bool handle_point_accepts_input( | ||
53 | struct wlr_scene_buffer *buffer, double *x, double *y) { | ||
54 | return false; | ||
55 | } | ||
56 | |||
57 | static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent, | ||
58 | bool *failed) { | ||
59 | if (*failed) { | ||
60 | return NULL; | ||
61 | } | ||
62 | |||
63 | // just pass in random values. These will be overwritten when | ||
64 | // they need to be used. | ||
65 | struct wlr_scene_rect *rect = wlr_scene_rect_create( | ||
66 | parent, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f}); | ||
67 | if (!rect) { | ||
68 | sway_log(SWAY_ERROR, "Failed to allocate a wlr_scene_rect"); | ||
69 | *failed = true; | ||
70 | } | ||
71 | |||
72 | return rect; | ||
73 | } | ||
74 | |||
27 | struct sway_container *container_create(struct sway_view *view) { | 75 | struct sway_container *container_create(struct sway_view *view) { |
28 | struct sway_container *c = calloc(1, sizeof(struct sway_container)); | 76 | struct sway_container *c = calloc(1, sizeof(struct sway_container)); |
29 | if (!c) { | 77 | if (!c) { |
@@ -31,23 +79,411 @@ struct sway_container *container_create(struct sway_view *view) { | |||
31 | return NULL; | 79 | return NULL; |
32 | } | 80 | } |
33 | node_init(&c->node, N_CONTAINER, c); | 81 | node_init(&c->node, N_CONTAINER, c); |
34 | c->pending.layout = L_NONE; | 82 | |
35 | c->view = view; | 83 | // Container tree structure |
36 | c->alpha = 1.0f; | 84 | // - scene tree |
85 | // - title bar | ||
86 | // - border | ||
87 | // - background | ||
88 | // - title text | ||
89 | // - marks text | ||
90 | // - border | ||
91 | // - border top/bottom/left/right | ||
92 | // - content_tree (we put the content node here so when we disable the | ||
93 | // border everything gets disabled. We only render the content iff there | ||
94 | // is a border as well) | ||
95 | // - buffer used for output enter/leave events for foreign_toplevel | ||
96 | bool failed = false; | ||
97 | c->scene_tree = alloc_scene_tree(root->staging, &failed); | ||
98 | |||
99 | c->title_bar.tree = alloc_scene_tree(c->scene_tree, &failed); | ||
100 | c->title_bar.border = alloc_scene_tree(c->title_bar.tree, &failed); | ||
101 | c->title_bar.background = alloc_scene_tree(c->title_bar.tree, &failed); | ||
102 | |||
103 | // for opacity purposes we need to carfully create the scene such that | ||
104 | // none of our rect nodes as well as text buffers don't overlap. To do | ||
105 | // this we have to create rects such that they go around text buffers | ||
106 | for (int i = 0; i < 4; i++) { | ||
107 | alloc_rect_node(c->title_bar.border, &failed); | ||
108 | } | ||
109 | |||
110 | for (int i = 0; i < 5; i++) { | ||
111 | alloc_rect_node(c->title_bar.background, &failed); | ||
112 | } | ||
113 | |||
114 | c->border.tree = alloc_scene_tree(c->scene_tree, &failed); | ||
115 | c->content_tree = alloc_scene_tree(c->border.tree, &failed); | ||
116 | |||
117 | if (view) { | ||
118 | // only containers with views can have borders | ||
119 | c->border.top = alloc_rect_node(c->border.tree, &failed); | ||
120 | c->border.bottom = alloc_rect_node(c->border.tree, &failed); | ||
121 | c->border.left = alloc_rect_node(c->border.tree, &failed); | ||
122 | c->border.right = alloc_rect_node(c->border.tree, &failed); | ||
123 | |||
124 | c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL); | ||
125 | if (!c->output_handler) { | ||
126 | sway_log(SWAY_ERROR, "Failed to allocate a scene node"); | ||
127 | failed = true; | ||
128 | } | ||
129 | |||
130 | if (!failed) { | ||
131 | c->output_enter.notify = handle_output_enter; | ||
132 | wl_signal_add(&c->output_handler->events.output_enter, | ||
133 | &c->output_enter); | ||
134 | c->output_leave.notify = handle_output_leave; | ||
135 | wl_signal_add(&c->output_handler->events.output_leave, | ||
136 | &c->output_leave); | ||
137 | c->output_handler->point_accepts_input = handle_point_accepts_input; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | if (!failed && !scene_descriptor_assign(&c->scene_tree->node, | ||
142 | SWAY_SCENE_DESC_CONTAINER, c)) { | ||
143 | failed = true; | ||
144 | } | ||
145 | |||
146 | if (failed) { | ||
147 | wlr_scene_node_destroy(&c->scene_tree->node); | ||
148 | free(c); | ||
149 | return NULL; | ||
150 | } | ||
37 | 151 | ||
38 | if (!view) { | 152 | if (!view) { |
39 | c->pending.children = create_list(); | 153 | c->pending.children = create_list(); |
40 | c->current.children = create_list(); | 154 | c->current.children = create_list(); |
41 | } | 155 | } |
156 | |||
157 | c->pending.layout = L_NONE; | ||
158 | c->view = view; | ||
159 | c->alpha = 1.0f; | ||
42 | c->marks = create_list(); | 160 | c->marks = create_list(); |
43 | c->outputs = create_list(); | ||
44 | 161 | ||
45 | wl_signal_init(&c->events.destroy); | 162 | wl_signal_init(&c->events.destroy); |
46 | wl_signal_emit(&root->events.new_node, &c->node); | 163 | wl_signal_emit_mutable(&root->events.new_node, &c->node); |
164 | |||
165 | container_update(c); | ||
47 | 166 | ||
48 | return c; | 167 | return c; |
49 | } | 168 | } |
50 | 169 | ||
170 | static bool container_is_focused(struct sway_container *con, void *data) { | ||
171 | return con->current.focused; | ||
172 | } | ||
173 | |||
174 | static bool container_has_focused_child(struct sway_container *con) { | ||
175 | return container_find_child(con, container_is_focused, NULL); | ||
176 | } | ||
177 | |||
178 | static bool container_is_current_parent_focused(struct sway_container *con) { | ||
179 | if (con->current.parent) { | ||
180 | struct sway_container *parent = con->current.parent; | ||
181 | return parent->current.focused || container_is_current_parent_focused(parent); | ||
182 | } else if (con->current.workspace) { | ||
183 | struct sway_workspace *ws = con->current.workspace; | ||
184 | return ws->current.focused; | ||
185 | } | ||
186 | |||
187 | return false; | ||
188 | } | ||
189 | |||
190 | static struct border_colors *container_get_current_colors( | ||
191 | struct sway_container *con) { | ||
192 | struct border_colors *colors; | ||
193 | |||
194 | bool urgent = con->view ? | ||
195 | view_is_urgent(con->view) : container_has_urgent_child(con); | ||
196 | struct sway_container *active_child; | ||
197 | |||
198 | if (con->current.parent) { | ||
199 | active_child = con->current.parent->current.focused_inactive_child; | ||
200 | } else if (con->current.workspace) { | ||
201 | active_child = con->current.workspace->current.focused_inactive_child; | ||
202 | } else { | ||
203 | active_child = NULL; | ||
204 | } | ||
205 | |||
206 | if (urgent) { | ||
207 | colors = &config->border_colors.urgent; | ||
208 | } else if (con->current.focused || container_is_current_parent_focused(con)) { | ||
209 | colors = &config->border_colors.focused; | ||
210 | } else if (config->has_focused_tab_title && container_has_focused_child(con)) { | ||
211 | colors = &config->border_colors.focused_tab_title; | ||
212 | } else if (con == active_child) { | ||
213 | colors = &config->border_colors.focused_inactive; | ||
214 | } else { | ||
215 | colors = &config->border_colors.unfocused; | ||
216 | } | ||
217 | |||
218 | return colors; | ||
219 | } | ||
220 | |||
221 | static bool container_is_current_floating(struct sway_container *container) { | ||
222 | if (!container->current.parent && container->current.workspace && | ||
223 | list_find(container->current.workspace->floating, container) != -1) { | ||
224 | return true; | ||
225 | } | ||
226 | if (container->scratchpad) { | ||
227 | return true; | ||
228 | } | ||
229 | return false; | ||
230 | } | ||
231 | |||
232 | // scene rect wants premultiplied colors | ||
233 | static void scene_rect_set_color(struct wlr_scene_rect *rect, | ||
234 | const float color[4], float opacity) { | ||
235 | const float premultiplied[] = { | ||
236 | color[0] * color[3] * opacity, | ||
237 | color[1] * color[3] * opacity, | ||
238 | color[2] * color[3] * opacity, | ||
239 | color[3] * opacity, | ||
240 | }; | ||
241 | |||
242 | wlr_scene_rect_set_color(rect, premultiplied); | ||
243 | } | ||
244 | |||
245 | void container_update(struct sway_container *con) { | ||
246 | struct border_colors *colors = container_get_current_colors(con); | ||
247 | list_t *siblings = NULL; | ||
248 | enum sway_container_layout layout = L_NONE; | ||
249 | float alpha = con->alpha; | ||
250 | |||
251 | if (con->current.parent) { | ||
252 | siblings = con->current.parent->current.children; | ||
253 | layout = con->current.parent->current.layout; | ||
254 | } else if (con->current.workspace) { | ||
255 | siblings = con->current.workspace->current.tiling; | ||
256 | layout = con->current.workspace->current.layout; | ||
257 | } | ||
258 | |||
259 | float bottom[4], right[4]; | ||
260 | memcpy(bottom, colors->child_border, sizeof(bottom)); | ||
261 | memcpy(right, colors->child_border, sizeof(right)); | ||
262 | |||
263 | if (!container_is_current_floating(con) && siblings && siblings->length == 1) { | ||
264 | if (layout == L_HORIZ) { | ||
265 | memcpy(right, colors->indicator, sizeof(right)); | ||
266 | } else if (layout == L_VERT) { | ||
267 | memcpy(bottom, colors->indicator, sizeof(bottom)); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | struct wlr_scene_node *node; | ||
272 | wl_list_for_each(node, &con->title_bar.border->children, link) { | ||
273 | struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); | ||
274 | scene_rect_set_color(rect, colors->border, alpha); | ||
275 | } | ||
276 | |||
277 | wl_list_for_each(node, &con->title_bar.background->children, link) { | ||
278 | struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); | ||
279 | scene_rect_set_color(rect, colors->background, alpha); | ||
280 | } | ||
281 | |||
282 | if (con->view) { | ||
283 | scene_rect_set_color(con->border.top, colors->child_border, alpha); | ||
284 | scene_rect_set_color(con->border.bottom, bottom, alpha); | ||
285 | scene_rect_set_color(con->border.left, colors->child_border, alpha); | ||
286 | scene_rect_set_color(con->border.right, right, alpha); | ||
287 | } | ||
288 | |||
289 | if (con->title_bar.title_text) { | ||
290 | sway_text_node_set_color(con->title_bar.title_text, colors->text); | ||
291 | sway_text_node_set_background(con->title_bar.title_text, colors->background); | ||
292 | } | ||
293 | |||
294 | if (con->title_bar.marks_text) { | ||
295 | sway_text_node_set_color(con->title_bar.marks_text, colors->text); | ||
296 | sway_text_node_set_background(con->title_bar.marks_text, colors->background); | ||
297 | } | ||
298 | } | ||
299 | |||
300 | void container_update_itself_and_parents(struct sway_container *con) { | ||
301 | container_update(con); | ||
302 | |||
303 | if (con->current.parent) { | ||
304 | container_update_itself_and_parents(con->current.parent); | ||
305 | } | ||
306 | } | ||
307 | |||
308 | static void update_rect_list(struct wlr_scene_tree *tree, pixman_region32_t *region) { | ||
309 | int len; | ||
310 | const pixman_box32_t *rects = pixman_region32_rectangles(region, &len); | ||
311 | |||
312 | wlr_scene_node_set_enabled(&tree->node, len > 0); | ||
313 | if (len == 0) { | ||
314 | return; | ||
315 | } | ||
316 | |||
317 | int i = 0; | ||
318 | struct wlr_scene_node *node; | ||
319 | wl_list_for_each(node, &tree->children, link) { | ||
320 | struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); | ||
321 | wlr_scene_node_set_enabled(&rect->node, i < len); | ||
322 | |||
323 | if (i < len) { | ||
324 | const pixman_box32_t *box = &rects[i++]; | ||
325 | wlr_scene_node_set_position(&rect->node, box->x1, box->y1); | ||
326 | wlr_scene_rect_set_size(rect, box->x2 - box->x1, box->y2 - box->y1); | ||
327 | } | ||
328 | } | ||
329 | } | ||
330 | |||
331 | void container_arrange_title_bar(struct sway_container *con) { | ||
332 | enum alignment title_align = config->title_align; | ||
333 | int marks_buffer_width = 0; | ||
334 | int width = con->title_width; | ||
335 | int height = container_titlebar_height(); | ||
336 | |||
337 | pixman_region32_t text_area; | ||
338 | pixman_region32_init(&text_area); | ||
339 | |||
340 | if (con->title_bar.marks_text) { | ||
341 | struct sway_text_node *node = con->title_bar.marks_text; | ||
342 | marks_buffer_width = node->width; | ||
343 | |||
344 | int h_padding; | ||
345 | if (title_align == ALIGN_RIGHT) { | ||
346 | h_padding = config->titlebar_h_padding; | ||
347 | } else { | ||
348 | h_padding = width - config->titlebar_h_padding - marks_buffer_width; | ||
349 | } | ||
350 | |||
351 | h_padding = MAX(h_padding, 0); | ||
352 | |||
353 | int alloc_width = MIN((int)node->width, | ||
354 | width - h_padding - config->titlebar_h_padding); | ||
355 | sway_text_node_set_max_width(node, alloc_width); | ||
356 | wlr_scene_node_set_position(node->node, | ||
357 | h_padding, (height - node->height) >> 1); | ||
358 | |||
359 | pixman_region32_union_rect(&text_area, &text_area, | ||
360 | node->node->x, node->node->y, alloc_width, node->height); | ||
361 | } | ||
362 | |||
363 | if (con->title_bar.title_text) { | ||
364 | struct sway_text_node *node = con->title_bar.title_text; | ||
365 | |||
366 | int h_padding; | ||
367 | if (title_align == ALIGN_RIGHT) { | ||
368 | h_padding = width - config->titlebar_h_padding - node->width; | ||
369 | } else if (title_align == ALIGN_CENTER) { | ||
370 | h_padding = ((int)width - marks_buffer_width - node->width) >> 1; | ||
371 | } else { | ||
372 | h_padding = config->titlebar_h_padding; | ||
373 | } | ||
374 | |||
375 | h_padding = MAX(h_padding, 0); | ||
376 | |||
377 | int alloc_width = MIN((int) node->width, | ||
378 | width - h_padding - config->titlebar_h_padding); | ||
379 | sway_text_node_set_max_width(node, alloc_width); | ||
380 | wlr_scene_node_set_position(node->node, | ||
381 | h_padding, (height - node->height) >> 1); | ||
382 | |||
383 | pixman_region32_union_rect(&text_area, &text_area, | ||
384 | node->node->x, node->node->y, alloc_width, node->height); | ||
385 | } | ||
386 | |||
387 | // silence pixman errors | ||
388 | if (width <= 0 || height <= 0) { | ||
389 | pixman_region32_fini(&text_area); | ||
390 | return; | ||
391 | } | ||
392 | |||
393 | pixman_region32_t background, border; | ||
394 | |||
395 | int thickness = config->titlebar_border_thickness; | ||
396 | pixman_region32_init_rect(&background, | ||
397 | thickness, thickness, | ||
398 | width - thickness * 2, height - thickness * 2); | ||
399 | pixman_region32_init_rect(&border, 0, 0, width, height); | ||
400 | pixman_region32_subtract(&border, &border, &background); | ||
401 | |||
402 | pixman_region32_subtract(&background, &background, &text_area); | ||
403 | pixman_region32_fini(&text_area); | ||
404 | |||
405 | update_rect_list(con->title_bar.background, &background); | ||
406 | pixman_region32_fini(&background); | ||
407 | |||
408 | update_rect_list(con->title_bar.border, &border); | ||
409 | pixman_region32_fini(&border); | ||
410 | |||
411 | container_update(con); | ||
412 | } | ||
413 | |||
414 | void container_update_marks(struct sway_container *con) { | ||
415 | char *buffer = NULL; | ||
416 | |||
417 | if (config->show_marks && con->marks->length) { | ||
418 | size_t len = 0; | ||
419 | for (int i = 0; i < con->marks->length; ++i) { | ||
420 | char *mark = con->marks->items[i]; | ||
421 | if (mark[0] != '_') { | ||
422 | len += strlen(mark) + 2; | ||
423 | } | ||
424 | } | ||
425 | buffer = calloc(len + 1, 1); | ||
426 | char *part = malloc(len + 1); | ||
427 | |||
428 | if (!sway_assert(buffer && part, "Unable to allocate memory")) { | ||
429 | free(buffer); | ||
430 | return; | ||
431 | } | ||
432 | |||
433 | for (int i = 0; i < con->marks->length; ++i) { | ||
434 | char *mark = con->marks->items[i]; | ||
435 | if (mark[0] != '_') { | ||
436 | snprintf(part, len + 1, "[%s]", mark); | ||
437 | strcat(buffer, part); | ||
438 | } | ||
439 | } | ||
440 | free(part); | ||
441 | } | ||
442 | |||
443 | if (!buffer) { | ||
444 | if (con->title_bar.marks_text) { | ||
445 | wlr_scene_node_destroy(con->title_bar.marks_text->node); | ||
446 | con->title_bar.marks_text = NULL; | ||
447 | } | ||
448 | } else if (!con->title_bar.marks_text) { | ||
449 | struct border_colors *colors = container_get_current_colors(con); | ||
450 | |||
451 | con->title_bar.marks_text = sway_text_node_create(con->title_bar.tree, | ||
452 | buffer, colors->text, false); | ||
453 | } else { | ||
454 | sway_text_node_set_text(con->title_bar.marks_text, buffer); | ||
455 | } | ||
456 | |||
457 | container_arrange_title_bar(con); | ||
458 | free(buffer); | ||
459 | } | ||
460 | |||
461 | void container_update_title_bar(struct sway_container *con) { | ||
462 | if (!con->formatted_title) { | ||
463 | return; | ||
464 | } | ||
465 | |||
466 | struct border_colors *colors = container_get_current_colors(con); | ||
467 | |||
468 | if (con->title_bar.title_text) { | ||
469 | wlr_scene_node_destroy(con->title_bar.title_text->node); | ||
470 | con->title_bar.title_text = NULL; | ||
471 | } | ||
472 | |||
473 | con->title_bar.title_text = sway_text_node_create(con->title_bar.tree, | ||
474 | con->formatted_title, colors->text, config->pango_markup); | ||
475 | |||
476 | // we always have to remake these text buffers completely for text font | ||
477 | // changes etc... | ||
478 | if (con->title_bar.marks_text) { | ||
479 | wlr_scene_node_destroy(con->title_bar.marks_text->node); | ||
480 | con->title_bar.marks_text = NULL; | ||
481 | } | ||
482 | |||
483 | container_update_marks(con); | ||
484 | container_arrange_title_bar(con); | ||
485 | } | ||
486 | |||
51 | void container_destroy(struct sway_container *con) { | 487 | void container_destroy(struct sway_container *con) { |
52 | if (!sway_assert(con->node.destroying, | 488 | if (!sway_assert(con->node.destroying, |
53 | "Tried to free container which wasn't marked as destroying")) { | 489 | "Tried to free container which wasn't marked as destroying")) { |
@@ -59,29 +495,21 @@ void container_destroy(struct sway_container *con) { | |||
59 | } | 495 | } |
60 | free(con->title); | 496 | free(con->title); |
61 | free(con->formatted_title); | 497 | free(con->formatted_title); |
62 | wlr_texture_destroy(con->title_focused); | ||
63 | wlr_texture_destroy(con->title_focused_inactive); | ||
64 | wlr_texture_destroy(con->title_unfocused); | ||
65 | wlr_texture_destroy(con->title_urgent); | ||
66 | list_free(con->pending.children); | 498 | list_free(con->pending.children); |
67 | list_free(con->current.children); | 499 | list_free(con->current.children); |
68 | list_free(con->outputs); | ||
69 | 500 | ||
70 | list_free_items_and_destroy(con->marks); | 501 | list_free_items_and_destroy(con->marks); |
71 | wlr_texture_destroy(con->marks_focused); | ||
72 | wlr_texture_destroy(con->marks_focused_inactive); | ||
73 | wlr_texture_destroy(con->marks_unfocused); | ||
74 | wlr_texture_destroy(con->marks_urgent); | ||
75 | 502 | ||
76 | if (con->view) { | 503 | if (con->view && con->view->container == con) { |
77 | if (con->view->container == con) { | 504 | con->view->container = NULL; |
78 | con->view->container = NULL; | 505 | wlr_scene_node_destroy(&con->output_handler->node); |
79 | } | ||
80 | if (con->view->destroying) { | 506 | if (con->view->destroying) { |
81 | view_destroy(con->view); | 507 | view_destroy(con->view); |
82 | } | 508 | } |
83 | } | 509 | } |
84 | 510 | ||
511 | scene_node_disown_children(con->content_tree); | ||
512 | wlr_scene_node_destroy(&con->scene_tree->node); | ||
85 | free(con); | 513 | free(con); |
86 | } | 514 | } |
87 | 515 | ||
@@ -98,7 +526,7 @@ void container_begin_destroy(struct sway_container *con) { | |||
98 | container_fullscreen_disable(con); | 526 | container_fullscreen_disable(con); |
99 | } | 527 | } |
100 | 528 | ||
101 | wl_signal_emit(&con->node.events.destroy, &con->node); | 529 | wl_signal_emit_mutable(&con->node.events.destroy, &con->node); |
102 | 530 | ||
103 | container_end_mouse_operation(con); | 531 | container_end_mouse_operation(con); |
104 | 532 | ||
@@ -168,267 +596,6 @@ struct sway_container *container_find_child(struct sway_container *container, | |||
168 | return NULL; | 596 | return NULL; |
169 | } | 597 | } |
170 | 598 | ||
171 | static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly, | ||
172 | struct wlr_surface **surface, double *sx, double *sy) { | ||
173 | if (!sway_assert(con->view, "Expected a view")) { | ||
174 | return NULL; | ||
175 | } | ||
176 | struct sway_view *view = con->view; | ||
177 | double view_sx = lx - con->surface_x + view->geometry.x; | ||
178 | double view_sy = ly - con->surface_y + view->geometry.y; | ||
179 | |||
180 | double _sx, _sy; | ||
181 | struct wlr_surface *_surface = NULL; | ||
182 | switch (view->type) { | ||
183 | #if HAVE_XWAYLAND | ||
184 | case SWAY_VIEW_XWAYLAND: | ||
185 | _surface = wlr_surface_surface_at(view->surface, | ||
186 | view_sx, view_sy, &_sx, &_sy); | ||
187 | break; | ||
188 | #endif | ||
189 | case SWAY_VIEW_XDG_SHELL: | ||
190 | _surface = wlr_xdg_surface_surface_at( | ||
191 | view->wlr_xdg_surface, | ||
192 | view_sx, view_sy, &_sx, &_sy); | ||
193 | break; | ||
194 | } | ||
195 | if (_surface) { | ||
196 | *sx = _sx; | ||
197 | *sy = _sy; | ||
198 | *surface = _surface; | ||
199 | return con; | ||
200 | } | ||
201 | return NULL; | ||
202 | } | ||
203 | |||
204 | /** | ||
205 | * container_at for a container with layout L_TABBED. | ||
206 | */ | ||
207 | static struct sway_container *container_at_tabbed(struct sway_node *parent, | ||
208 | double lx, double ly, | ||
209 | struct wlr_surface **surface, double *sx, double *sy) { | ||
210 | struct wlr_box box; | ||
211 | node_get_box(parent, &box); | ||
212 | if (lx < box.x || lx > box.x + box.width || | ||
213 | ly < box.y || ly > box.y + box.height) { | ||
214 | return NULL; | ||
215 | } | ||
216 | struct sway_seat *seat = input_manager_current_seat(); | ||
217 | list_t *children = node_get_children(parent); | ||
218 | if (!children->length) { | ||
219 | return NULL; | ||
220 | } | ||
221 | |||
222 | // Tab titles | ||
223 | int title_height = container_titlebar_height(); | ||
224 | if (ly < box.y + title_height) { | ||
225 | int tab_width = box.width / children->length; | ||
226 | int child_index = (lx - box.x) / tab_width; | ||
227 | if (child_index >= children->length) { | ||
228 | child_index = children->length - 1; | ||
229 | } | ||
230 | struct sway_container *child = children->items[child_index]; | ||
231 | return child; | ||
232 | } | ||
233 | |||
234 | // Surfaces | ||
235 | struct sway_node *current = seat_get_active_tiling_child(seat, parent); | ||
236 | return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * container_at for a container with layout L_STACKED. | ||
241 | */ | ||
242 | static struct sway_container *container_at_stacked(struct sway_node *parent, | ||
243 | double lx, double ly, | ||
244 | struct wlr_surface **surface, double *sx, double *sy) { | ||
245 | struct wlr_box box; | ||
246 | node_get_box(parent, &box); | ||
247 | if (lx < box.x || lx > box.x + box.width || | ||
248 | ly < box.y || ly > box.y + box.height) { | ||
249 | return NULL; | ||
250 | } | ||
251 | struct sway_seat *seat = input_manager_current_seat(); | ||
252 | list_t *children = node_get_children(parent); | ||
253 | |||
254 | // Title bars | ||
255 | int title_height = container_titlebar_height(); | ||
256 | if (title_height > 0) { | ||
257 | int child_index = (ly - box.y) / title_height; | ||
258 | if (child_index < children->length) { | ||
259 | struct sway_container *child = children->items[child_index]; | ||
260 | return child; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | // Surfaces | ||
265 | struct sway_node *current = seat_get_active_tiling_child(seat, parent); | ||
266 | return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; | ||
267 | } | ||
268 | |||
269 | /** | ||
270 | * container_at for a container with layout L_HORIZ or L_VERT. | ||
271 | */ | ||
272 | static struct sway_container *container_at_linear(struct sway_node *parent, | ||
273 | double lx, double ly, | ||
274 | struct wlr_surface **surface, double *sx, double *sy) { | ||
275 | list_t *children = node_get_children(parent); | ||
276 | for (int i = 0; i < children->length; ++i) { | ||
277 | struct sway_container *child = children->items[i]; | ||
278 | struct sway_container *container = | ||
279 | tiling_container_at(&child->node, lx, ly, surface, sx, sy); | ||
280 | if (container) { | ||
281 | return container; | ||
282 | } | ||
283 | } | ||
284 | return NULL; | ||
285 | } | ||
286 | |||
287 | static struct sway_container *floating_container_at(double lx, double ly, | ||
288 | struct wlr_surface **surface, double *sx, double *sy) { | ||
289 | // For outputs with floating containers that overhang the output bounds, | ||
290 | // those at the end of the output list appear on top of floating | ||
291 | // containers from other outputs, so iterate the list in reverse. | ||
292 | for (int i = root->outputs->length - 1; i >= 0; --i) { | ||
293 | struct sway_output *output = root->outputs->items[i]; | ||
294 | for (int j = 0; j < output->workspaces->length; ++j) { | ||
295 | struct sway_workspace *ws = output->workspaces->items[j]; | ||
296 | if (!workspace_is_visible(ws)) { | ||
297 | continue; | ||
298 | } | ||
299 | // Items at the end of the list are on top, so iterate the list in | ||
300 | // reverse. | ||
301 | for (int k = ws->floating->length - 1; k >= 0; --k) { | ||
302 | struct sway_container *floater = ws->floating->items[k]; | ||
303 | struct sway_container *container = | ||
304 | tiling_container_at(&floater->node, lx, ly, surface, sx, sy); | ||
305 | if (container) { | ||
306 | return container; | ||
307 | } | ||
308 | } | ||
309 | } | ||
310 | } | ||
311 | return NULL; | ||
312 | } | ||
313 | |||
314 | static struct sway_container *view_container_content_at(struct sway_node *parent, | ||
315 | double lx, double ly, | ||
316 | struct wlr_surface **surface, double *sx, double *sy) { | ||
317 | if (!sway_assert(node_is_view(parent), "Expected a view")) { | ||
318 | return NULL; | ||
319 | } | ||
320 | |||
321 | struct sway_container *container = parent->sway_container; | ||
322 | struct wlr_box box = { | ||
323 | .x = container->pending.content_x, | ||
324 | .y = container->pending.content_y, | ||
325 | .width = container->pending.content_width, | ||
326 | .height = container->pending.content_height, | ||
327 | }; | ||
328 | |||
329 | if (wlr_box_contains_point(&box, lx, ly)) { | ||
330 | surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); | ||
331 | return container; | ||
332 | } | ||
333 | |||
334 | return NULL; | ||
335 | } | ||
336 | |||
337 | static struct sway_container *view_container_at(struct sway_node *parent, | ||
338 | double lx, double ly, | ||
339 | struct wlr_surface **surface, double *sx, double *sy) { | ||
340 | if (!sway_assert(node_is_view(parent), "Expected a view")) { | ||
341 | return NULL; | ||
342 | } | ||
343 | |||
344 | struct sway_container *container = parent->sway_container; | ||
345 | struct wlr_box box = { | ||
346 | .x = container->pending.x, | ||
347 | .y = container->pending.y, | ||
348 | .width = container->pending.width, | ||
349 | .height = container->pending.height, | ||
350 | }; | ||
351 | |||
352 | if (wlr_box_contains_point(&box, lx, ly)) { | ||
353 | surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); | ||
354 | return container; | ||
355 | } | ||
356 | |||
357 | return NULL; | ||
358 | } | ||
359 | |||
360 | struct sway_container *tiling_container_at(struct sway_node *parent, | ||
361 | double lx, double ly, | ||
362 | struct wlr_surface **surface, double *sx, double *sy) { | ||
363 | if (node_is_view(parent)) { | ||
364 | return view_container_at(parent, lx, ly, surface, sx, sy); | ||
365 | } | ||
366 | if (!node_get_children(parent)) { | ||
367 | return NULL; | ||
368 | } | ||
369 | switch (node_get_layout(parent)) { | ||
370 | case L_HORIZ: | ||
371 | case L_VERT: | ||
372 | return container_at_linear(parent, lx, ly, surface, sx, sy); | ||
373 | case L_TABBED: | ||
374 | return container_at_tabbed(parent, lx, ly, surface, sx, sy); | ||
375 | case L_STACKED: | ||
376 | return container_at_stacked(parent, lx, ly, surface, sx, sy); | ||
377 | case L_NONE: | ||
378 | return NULL; | ||
379 | } | ||
380 | return NULL; | ||
381 | } | ||
382 | |||
383 | static bool surface_is_popup(struct wlr_surface *surface) { | ||
384 | if (wlr_surface_is_xdg_surface(surface)) { | ||
385 | struct wlr_xdg_surface *xdg_surface = | ||
386 | wlr_xdg_surface_from_wlr_surface(surface); | ||
387 | while (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) { | ||
388 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
389 | return true; | ||
390 | } | ||
391 | xdg_surface = xdg_surface->toplevel->parent; | ||
392 | } | ||
393 | return false; | ||
394 | } | ||
395 | |||
396 | return false; | ||
397 | } | ||
398 | |||
399 | struct sway_container *container_at(struct sway_workspace *workspace, | ||
400 | double lx, double ly, | ||
401 | struct wlr_surface **surface, double *sx, double *sy) { | ||
402 | struct sway_container *c; | ||
403 | |||
404 | struct sway_seat *seat = input_manager_current_seat(); | ||
405 | struct sway_container *focus = seat_get_focused_container(seat); | ||
406 | bool is_floating = focus && container_is_floating_or_child(focus); | ||
407 | // Focused view's popups | ||
408 | if (focus && focus->view) { | ||
409 | c = surface_at_view(focus, lx, ly, surface, sx, sy); | ||
410 | if (c && surface_is_popup(*surface)) { | ||
411 | return c; | ||
412 | } | ||
413 | *surface = NULL; | ||
414 | } | ||
415 | // Floating | ||
416 | if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) { | ||
417 | return c; | ||
418 | } | ||
419 | // Tiling (focused) | ||
420 | if (focus && focus->view && !is_floating) { | ||
421 | if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) { | ||
422 | return c; | ||
423 | } | ||
424 | } | ||
425 | // Tiling (non-focused) | ||
426 | if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { | ||
427 | return c; | ||
428 | } | ||
429 | return NULL; | ||
430 | } | ||
431 | |||
432 | void container_for_each_child(struct sway_container *container, | 599 | void container_for_each_child(struct sway_container *container, |
433 | void (*f)(struct sway_container *container, void *data), | 600 | void (*f)(struct sway_container *container, void *data), |
434 | void *data) { | 601 | void *data) { |
@@ -474,123 +641,6 @@ bool container_has_ancestor(struct sway_container *descendant, | |||
474 | return false; | 641 | return false; |
475 | } | 642 | } |
476 | 643 | ||
477 | void container_damage_whole(struct sway_container *container) { | ||
478 | for (int i = 0; i < root->outputs->length; ++i) { | ||
479 | struct sway_output *output = root->outputs->items[i]; | ||
480 | output_damage_whole_container(output, container); | ||
481 | } | ||
482 | } | ||
483 | |||
484 | /** | ||
485 | * Return the output which will be used for scale purposes. | ||
486 | * This is the most recently entered output. | ||
487 | */ | ||
488 | struct sway_output *container_get_effective_output(struct sway_container *con) { | ||
489 | if (con->outputs->length == 0) { | ||
490 | return NULL; | ||
491 | } | ||
492 | return con->outputs->items[con->outputs->length - 1]; | ||
493 | } | ||
494 | |||
495 | static void update_title_texture(struct sway_container *con, | ||
496 | struct wlr_texture **texture, struct border_colors *class) { | ||
497 | struct sway_output *output = container_get_effective_output(con); | ||
498 | if (!output) { | ||
499 | return; | ||
500 | } | ||
501 | if (*texture) { | ||
502 | wlr_texture_destroy(*texture); | ||
503 | *texture = NULL; | ||
504 | } | ||
505 | if (!con->formatted_title) { | ||
506 | return; | ||
507 | } | ||
508 | |||
509 | double scale = output->wlr_output->scale; | ||
510 | int width = 0; | ||
511 | int height = con->title_height * scale; | ||
512 | |||
513 | // We must use a non-nil cairo_t for cairo_set_font_options to work. | ||
514 | // Therefore, we cannot use cairo_create(NULL). | ||
515 | cairo_surface_t *dummy_surface = cairo_image_surface_create( | ||
516 | CAIRO_FORMAT_ARGB32, 0, 0); | ||
517 | cairo_t *c = cairo_create(dummy_surface); | ||
518 | cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); | ||
519 | cairo_font_options_t *fo = cairo_font_options_create(); | ||
520 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
521 | if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { | ||
522 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); | ||
523 | } else { | ||
524 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | ||
525 | cairo_font_options_set_subpixel_order(fo, | ||
526 | to_cairo_subpixel_order(output->wlr_output->subpixel)); | ||
527 | } | ||
528 | cairo_set_font_options(c, fo); | ||
529 | get_text_size(c, config->font, &width, NULL, NULL, scale, | ||
530 | config->pango_markup, "%s", con->formatted_title); | ||
531 | cairo_surface_destroy(dummy_surface); | ||
532 | cairo_destroy(c); | ||
533 | |||
534 | if (width == 0 || height == 0) { | ||
535 | return; | ||
536 | } | ||
537 | |||
538 | cairo_surface_t *surface = cairo_image_surface_create( | ||
539 | CAIRO_FORMAT_ARGB32, width, height); | ||
540 | cairo_t *cairo = cairo_create(surface); | ||
541 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
542 | cairo_set_font_options(cairo, fo); | ||
543 | cairo_font_options_destroy(fo); | ||
544 | cairo_set_source_rgba(cairo, class->background[0], class->background[1], | ||
545 | class->background[2], class->background[3]); | ||
546 | cairo_paint(cairo); | ||
547 | PangoContext *pango = pango_cairo_create_context(cairo); | ||
548 | cairo_set_source_rgba(cairo, class->text[0], class->text[1], | ||
549 | class->text[2], class->text[3]); | ||
550 | cairo_move_to(cairo, 0, 0); | ||
551 | |||
552 | pango_printf(cairo, config->font, scale, config->pango_markup, | ||
553 | "%s", con->formatted_title); | ||
554 | |||
555 | cairo_surface_flush(surface); | ||
556 | unsigned char *data = cairo_image_surface_get_data(surface); | ||
557 | int stride = cairo_image_surface_get_stride(surface); | ||
558 | struct wlr_renderer *renderer = wlr_backend_get_renderer( | ||
559 | output->wlr_output->backend); | ||
560 | *texture = wlr_texture_from_pixels( | ||
561 | renderer, DRM_FORMAT_ARGB8888, stride, width, height, data); | ||
562 | cairo_surface_destroy(surface); | ||
563 | g_object_unref(pango); | ||
564 | cairo_destroy(cairo); | ||
565 | } | ||
566 | |||
567 | void container_update_title_textures(struct sway_container *container) { | ||
568 | update_title_texture(container, &container->title_focused, | ||
569 | &config->border_colors.focused); | ||
570 | update_title_texture(container, &container->title_focused_inactive, | ||
571 | &config->border_colors.focused_inactive); | ||
572 | update_title_texture(container, &container->title_unfocused, | ||
573 | &config->border_colors.unfocused); | ||
574 | update_title_texture(container, &container->title_urgent, | ||
575 | &config->border_colors.urgent); | ||
576 | container_damage_whole(container); | ||
577 | } | ||
578 | |||
579 | void container_calculate_title_height(struct sway_container *container) { | ||
580 | if (!container->formatted_title) { | ||
581 | container->title_height = 0; | ||
582 | return; | ||
583 | } | ||
584 | cairo_t *cairo = cairo_create(NULL); | ||
585 | int height; | ||
586 | int baseline; | ||
587 | get_text_size(cairo, config->font, NULL, &height, &baseline, 1, | ||
588 | config->pango_markup, "%s", container->formatted_title); | ||
589 | cairo_destroy(cairo); | ||
590 | container->title_height = height; | ||
591 | container->title_baseline = baseline; | ||
592 | } | ||
593 | |||
594 | /** | 644 | /** |
595 | * Calculate and return the length of the tree representation. | 645 | * Calculate and return the length of the tree representation. |
596 | * An example tree representation is: V[Terminal, Firefox] | 646 | * An example tree representation is: V[Terminal, Firefox] |
@@ -656,8 +706,13 @@ void container_update_representation(struct sway_container *con) { | |||
656 | } | 706 | } |
657 | container_build_representation(con->pending.layout, con->pending.children, | 707 | container_build_representation(con->pending.layout, con->pending.children, |
658 | con->formatted_title); | 708 | con->formatted_title); |
659 | container_calculate_title_height(con); | 709 | |
660 | container_update_title_textures(con); | 710 | if (con->title_bar.title_text) { |
711 | sway_text_node_set_text(con->title_bar.title_text, con->formatted_title); | ||
712 | container_arrange_title_bar(con); | ||
713 | } else { | ||
714 | container_update_title_bar(con); | ||
715 | } | ||
661 | } | 716 | } |
662 | if (con->pending.parent) { | 717 | if (con->pending.parent) { |
663 | container_update_representation(con->pending.parent); | 718 | container_update_representation(con->pending.parent); |
@@ -688,12 +743,13 @@ void floating_calculate_constraints(int *min_width, int *max_width, | |||
688 | *min_height = config->floating_minimum_height; | 743 | *min_height = config->floating_minimum_height; |
689 | } | 744 | } |
690 | 745 | ||
691 | struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); | 746 | struct wlr_box box; |
747 | wlr_output_layout_get_box(root->output_layout, NULL, &box); | ||
692 | 748 | ||
693 | if (config->floating_maximum_width == -1) { // no maximum | 749 | if (config->floating_maximum_width == -1) { // no maximum |
694 | *max_width = INT_MAX; | 750 | *max_width = INT_MAX; |
695 | } else if (config->floating_maximum_width == 0) { // automatic | 751 | } else if (config->floating_maximum_width == 0) { // automatic |
696 | *max_width = box->width; | 752 | *max_width = box.width; |
697 | } else { | 753 | } else { |
698 | *max_width = config->floating_maximum_width; | 754 | *max_width = config->floating_maximum_width; |
699 | } | 755 | } |
@@ -701,13 +757,28 @@ void floating_calculate_constraints(int *min_width, int *max_width, | |||
701 | if (config->floating_maximum_height == -1) { // no maximum | 757 | if (config->floating_maximum_height == -1) { // no maximum |
702 | *max_height = INT_MAX; | 758 | *max_height = INT_MAX; |
703 | } else if (config->floating_maximum_height == 0) { // automatic | 759 | } else if (config->floating_maximum_height == 0) { // automatic |
704 | *max_height = box->height; | 760 | *max_height = box.height; |
705 | } else { | 761 | } else { |
706 | *max_height = config->floating_maximum_height; | 762 | *max_height = config->floating_maximum_height; |
707 | } | 763 | } |
708 | 764 | ||
709 | } | 765 | } |
710 | 766 | ||
767 | void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, struct wlr_box *new) { | ||
768 | if (!old->width || !old->height) { | ||
769 | // Fall back to centering on the workspace. | ||
770 | container_floating_move_to_center(con); | ||
771 | } else { | ||
772 | int rel_x = con->pending.x - old->x + (con->pending.width / 2); | ||
773 | int rel_y = con->pending.y - old->y + (con->pending.height / 2); | ||
774 | |||
775 | con->pending.x = new->x + (double)(rel_x * new->width) / old->width - (con->pending.width / 2); | ||
776 | con->pending.y = new->y + (double)(rel_y * new->height) / old->height - (con->pending.height / 2); | ||
777 | |||
778 | sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y); | ||
779 | } | ||
780 | } | ||
781 | |||
711 | static void floating_natural_resize(struct sway_container *con) { | 782 | static void floating_natural_resize(struct sway_container *con) { |
712 | int min_width, max_width, min_height, max_height; | 783 | int min_width, max_width, min_height, max_height; |
713 | floating_calculate_constraints(&min_width, &max_width, | 784 | floating_calculate_constraints(&min_width, &max_width, |
@@ -733,9 +804,9 @@ void container_floating_resize_and_center(struct sway_container *con) { | |||
733 | return; | 804 | return; |
734 | } | 805 | } |
735 | 806 | ||
736 | struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, | 807 | struct wlr_box ob; |
737 | ws->output->wlr_output); | 808 | wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output, &ob); |
738 | if (!ob) { | 809 | if (wlr_box_empty(&ob)) { |
739 | // On NOOP output. Will be called again when moved to an output | 810 | // On NOOP output. Will be called again when moved to an output |
740 | con->pending.x = 0; | 811 | con->pending.x = 0; |
741 | con->pending.y = 0; | 812 | con->pending.y = 0; |
@@ -747,8 +818,8 @@ void container_floating_resize_and_center(struct sway_container *con) { | |||
747 | floating_natural_resize(con); | 818 | floating_natural_resize(con); |
748 | if (!con->view) { | 819 | if (!con->view) { |
749 | if (con->pending.width > ws->width || con->pending.height > ws->height) { | 820 | if (con->pending.width > ws->width || con->pending.height > ws->height) { |
750 | con->pending.x = ob->x + (ob->width - con->pending.width) / 2; | 821 | con->pending.x = ob.x + (ob.width - con->pending.width) / 2; |
751 | con->pending.y = ob->y + (ob->height - con->pending.height) / 2; | 822 | con->pending.y = ob.y + (ob.height - con->pending.height) / 2; |
752 | } else { | 823 | } else { |
753 | con->pending.x = ws->x + (ws->width - con->pending.width) / 2; | 824 | con->pending.x = ws->x + (ws->width - con->pending.width) / 2; |
754 | con->pending.y = ws->y + (ws->height - con->pending.height) / 2; | 825 | con->pending.y = ws->y + (ws->height - con->pending.height) / 2; |
@@ -756,8 +827,8 @@ void container_floating_resize_and_center(struct sway_container *con) { | |||
756 | } else { | 827 | } else { |
757 | if (con->pending.content_width > ws->width | 828 | if (con->pending.content_width > ws->width |
758 | || con->pending.content_height > ws->height) { | 829 | || con->pending.content_height > ws->height) { |
759 | con->pending.content_x = ob->x + (ob->width - con->pending.content_width) / 2; | 830 | con->pending.content_x = ob.x + (ob.width - con->pending.content_width) / 2; |
760 | con->pending.content_y = ob->y + (ob->height - con->pending.content_height) / 2; | 831 | con->pending.content_y = ob.y + (ob.height - con->pending.content_height) / 2; |
761 | } else { | 832 | } else { |
762 | con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2; | 833 | con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2; |
763 | con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2; | 834 | con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2; |
@@ -779,11 +850,11 @@ void container_floating_set_default_size(struct sway_container *con) { | |||
779 | int min_width, max_width, min_height, max_height; | 850 | int min_width, max_width, min_height, max_height; |
780 | floating_calculate_constraints(&min_width, &max_width, | 851 | floating_calculate_constraints(&min_width, &max_width, |
781 | &min_height, &max_height); | 852 | &min_height, &max_height); |
782 | struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); | 853 | struct wlr_box box; |
783 | workspace_get_box(con->pending.workspace, box); | 854 | workspace_get_box(con->pending.workspace, &box); |
784 | 855 | ||
785 | double width = fmax(min_width, fmin(box->width * 0.5, max_width)); | 856 | double width = fmax(min_width, fmin(box.width * 0.5, max_width)); |
786 | double height = fmax(min_height, fmin(box->height * 0.75, max_height)); | 857 | double height = fmax(min_height, fmin(box.height * 0.75, max_height)); |
787 | if (!con->view) { | 858 | if (!con->view) { |
788 | con->pending.width = width; | 859 | con->pending.width = width; |
789 | con->pending.height = height; | 860 | con->pending.height = height; |
@@ -792,8 +863,6 @@ void container_floating_set_default_size(struct sway_container *con) { | |||
792 | con->pending.content_height = height; | 863 | con->pending.content_height = height; |
793 | container_set_geometry_from_content(con); | 864 | container_set_geometry_from_content(con); |
794 | } | 865 | } |
795 | |||
796 | free(box); | ||
797 | } | 866 | } |
798 | 867 | ||
799 | 868 | ||
@@ -835,7 +904,13 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
835 | if (container->view) { | 904 | if (container->view) { |
836 | view_set_tiled(container->view, false); | 905 | view_set_tiled(container->view, false); |
837 | if (container->view->using_csd) { | 906 | if (container->view->using_csd) { |
907 | container->saved_border = container->pending.border; | ||
838 | container->pending.border = B_CSD; | 908 | container->pending.border = B_CSD; |
909 | if (container->view->xdg_decoration) { | ||
910 | struct sway_xdg_decoration *deco = container->view->xdg_decoration; | ||
911 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | ||
912 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); | ||
913 | } | ||
839 | } | 914 | } |
840 | } | 915 | } |
841 | container_floating_set_default_size(container); | 916 | container_floating_set_default_size(container); |
@@ -873,6 +948,11 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
873 | view_set_tiled(container->view, true); | 948 | view_set_tiled(container->view, true); |
874 | if (container->view->using_csd) { | 949 | if (container->view->using_csd) { |
875 | container->pending.border = container->saved_border; | 950 | container->pending.border = container->saved_border; |
951 | if (container->view->xdg_decoration) { | ||
952 | struct sway_xdg_decoration *deco = container->view->xdg_decoration; | ||
953 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | ||
954 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); | ||
955 | } | ||
876 | } | 956 | } |
877 | } | 957 | } |
878 | container->width_fraction = 0; | 958 | container->width_fraction = 0; |
@@ -918,17 +998,6 @@ bool container_is_floating(struct sway_container *container) { | |||
918 | return false; | 998 | return false; |
919 | } | 999 | } |
920 | 1000 | ||
921 | bool container_is_current_floating(struct sway_container *container) { | ||
922 | if (!container->current.parent && container->current.workspace && | ||
923 | list_find(container->current.workspace->floating, container) != -1) { | ||
924 | return true; | ||
925 | } | ||
926 | if (container->scratchpad) { | ||
927 | return true; | ||
928 | } | ||
929 | return false; | ||
930 | } | ||
931 | |||
932 | void container_get_box(struct sway_container *container, struct wlr_box *box) { | 1001 | void container_get_box(struct sway_container *container, struct wlr_box *box) { |
933 | box->x = container->pending.x; | 1002 | box->x = container->pending.x; |
934 | box->y = container->pending.y; | 1003 | box->y = container->pending.y; |
@@ -1012,6 +1081,13 @@ void container_floating_move_to(struct sway_container *con, | |||
1012 | workspace_add_floating(new_workspace, con); | 1081 | workspace_add_floating(new_workspace, con); |
1013 | arrange_workspace(old_workspace); | 1082 | arrange_workspace(old_workspace); |
1014 | arrange_workspace(new_workspace); | 1083 | arrange_workspace(new_workspace); |
1084 | // If the moved container was a visible scratchpad container, then | ||
1085 | // update its transform. | ||
1086 | if (con->scratchpad) { | ||
1087 | struct wlr_box output_box; | ||
1088 | output_get_box(new_output, &output_box); | ||
1089 | con->transform = output_box; | ||
1090 | } | ||
1015 | workspace_detect_urgent(old_workspace); | 1091 | workspace_detect_urgent(old_workspace); |
1016 | workspace_detect_urgent(new_workspace); | 1092 | workspace_detect_urgent(new_workspace); |
1017 | } | 1093 | } |
@@ -1223,72 +1299,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) { | |||
1223 | return false; | 1299 | return false; |
1224 | } | 1300 | } |
1225 | 1301 | ||
1226 | static void surface_send_enter_iterator(struct wlr_surface *surface, | ||
1227 | int x, int y, void *data) { | ||
1228 | struct wlr_output *wlr_output = data; | ||
1229 | wlr_surface_send_enter(surface, wlr_output); | ||
1230 | } | ||
1231 | |||
1232 | static void surface_send_leave_iterator(struct wlr_surface *surface, | ||
1233 | int x, int y, void *data) { | ||
1234 | struct wlr_output *wlr_output = data; | ||
1235 | wlr_surface_send_leave(surface, wlr_output); | ||
1236 | } | ||
1237 | |||
1238 | void container_discover_outputs(struct sway_container *con) { | ||
1239 | struct wlr_box con_box = { | ||
1240 | .x = con->current.x, | ||
1241 | .y = con->current.y, | ||
1242 | .width = con->current.width, | ||
1243 | .height = con->current.height, | ||
1244 | }; | ||
1245 | struct sway_output *old_output = container_get_effective_output(con); | ||
1246 | |||
1247 | for (int i = 0; i < root->outputs->length; ++i) { | ||
1248 | struct sway_output *output = root->outputs->items[i]; | ||
1249 | struct wlr_box output_box; | ||
1250 | output_get_box(output, &output_box); | ||
1251 | struct wlr_box intersection; | ||
1252 | bool intersects = | ||
1253 | wlr_box_intersection(&intersection, &con_box, &output_box); | ||
1254 | int index = list_find(con->outputs, output); | ||
1255 | |||
1256 | if (intersects && index == -1) { | ||
1257 | // Send enter | ||
1258 | sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output); | ||
1259 | if (con->view) { | ||
1260 | view_for_each_surface(con->view, | ||
1261 | surface_send_enter_iterator, output->wlr_output); | ||
1262 | if (con->view->foreign_toplevel) { | ||
1263 | wlr_foreign_toplevel_handle_v1_output_enter( | ||
1264 | con->view->foreign_toplevel, output->wlr_output); | ||
1265 | } | ||
1266 | } | ||
1267 | list_add(con->outputs, output); | ||
1268 | } else if (!intersects && index != -1) { | ||
1269 | // Send leave | ||
1270 | sway_log(SWAY_DEBUG, "Container %p left output %p", con, output); | ||
1271 | if (con->view) { | ||
1272 | view_for_each_surface(con->view, | ||
1273 | surface_send_leave_iterator, output->wlr_output); | ||
1274 | if (con->view->foreign_toplevel) { | ||
1275 | wlr_foreign_toplevel_handle_v1_output_leave( | ||
1276 | con->view->foreign_toplevel, output->wlr_output); | ||
1277 | } | ||
1278 | } | ||
1279 | list_del(con->outputs, index); | ||
1280 | } | ||
1281 | } | ||
1282 | struct sway_output *new_output = container_get_effective_output(con); | ||
1283 | double old_scale = old_output && old_output->enabled ? | ||
1284 | old_output->wlr_output->scale : -1; | ||
1285 | double new_scale = new_output ? new_output->wlr_output->scale : -1; | ||
1286 | if (old_scale != new_scale) { | ||
1287 | container_update_title_textures(con); | ||
1288 | container_update_marks_textures(con); | ||
1289 | } | ||
1290 | } | ||
1291 | |||
1292 | enum sway_container_layout container_parent_layout(struct sway_container *con) { | 1302 | enum sway_container_layout container_parent_layout(struct sway_container *con) { |
1293 | if (con->pending.parent) { | 1303 | if (con->pending.parent) { |
1294 | return con->pending.parent->pending.layout; | 1304 | return con->pending.parent->pending.layout; |
@@ -1299,19 +1309,11 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) { | |||
1299 | return L_NONE; | 1309 | return L_NONE; |
1300 | } | 1310 | } |
1301 | 1311 | ||
1302 | enum sway_container_layout container_current_parent_layout( | ||
1303 | struct sway_container *con) { | ||
1304 | if (con->current.parent) { | ||
1305 | return con->current.parent->current.layout; | ||
1306 | } | ||
1307 | return con->current.workspace->current.layout; | ||
1308 | } | ||
1309 | |||
1310 | list_t *container_get_siblings(struct sway_container *container) { | 1312 | list_t *container_get_siblings(struct sway_container *container) { |
1311 | if (container->pending.parent) { | 1313 | if (container->pending.parent) { |
1312 | return container->pending.parent->pending.children; | 1314 | return container->pending.parent->pending.children; |
1313 | } | 1315 | } |
1314 | if (container_is_scratchpad_hidden(container)) { | 1316 | if (!container->pending.workspace) { |
1315 | return NULL; | 1317 | return NULL; |
1316 | } | 1318 | } |
1317 | if (list_find(container->pending.workspace->tiling, container) != -1) { | 1319 | if (list_find(container->pending.workspace->tiling, container) != -1) { |
@@ -1324,13 +1326,6 @@ int container_sibling_index(struct sway_container *child) { | |||
1324 | return list_find(container_get_siblings(child), child); | 1326 | return list_find(container_get_siblings(child), child); |
1325 | } | 1327 | } |
1326 | 1328 | ||
1327 | list_t *container_get_current_siblings(struct sway_container *container) { | ||
1328 | if (container->current.parent) { | ||
1329 | return container->current.parent->current.children; | ||
1330 | } | ||
1331 | return container->current.workspace->current.tiling; | ||
1332 | } | ||
1333 | |||
1334 | void container_handle_fullscreen_reparent(struct sway_container *con) { | 1329 | void container_handle_fullscreen_reparent(struct sway_container *con) { |
1335 | if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || | 1330 | if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || |
1336 | con->pending.workspace->fullscreen == con) { | 1331 | con->pending.workspace->fullscreen == con) { |
@@ -1545,7 +1540,7 @@ bool container_find_and_unmark(char *mark) { | |||
1545 | if (strcmp(con_mark, mark) == 0) { | 1540 | if (strcmp(con_mark, mark) == 0) { |
1546 | free(con_mark); | 1541 | free(con_mark); |
1547 | list_del(con->marks, i); | 1542 | list_del(con->marks, i); |
1548 | container_update_marks_textures(con); | 1543 | container_update_marks(con); |
1549 | ipc_event_window(con, "mark"); | 1544 | ipc_event_window(con, "mark"); |
1550 | return true; | 1545 | return true; |
1551 | } | 1546 | } |
@@ -1576,103 +1571,15 @@ void container_add_mark(struct sway_container *con, char *mark) { | |||
1576 | ipc_event_window(con, "mark"); | 1571 | ipc_event_window(con, "mark"); |
1577 | } | 1572 | } |
1578 | 1573 | ||
1579 | static void update_marks_texture(struct sway_container *con, | ||
1580 | struct wlr_texture **texture, struct border_colors *class) { | ||
1581 | struct sway_output *output = container_get_effective_output(con); | ||
1582 | if (!output) { | ||
1583 | return; | ||
1584 | } | ||
1585 | if (*texture) { | ||
1586 | wlr_texture_destroy(*texture); | ||
1587 | *texture = NULL; | ||
1588 | } | ||
1589 | if (!con->marks->length) { | ||
1590 | return; | ||
1591 | } | ||
1592 | |||
1593 | size_t len = 0; | ||
1594 | for (int i = 0; i < con->marks->length; ++i) { | ||
1595 | char *mark = con->marks->items[i]; | ||
1596 | if (mark[0] != '_') { | ||
1597 | len += strlen(mark) + 2; | ||
1598 | } | ||
1599 | } | ||
1600 | char *buffer = calloc(len + 1, 1); | ||
1601 | char *part = malloc(len + 1); | ||
1602 | |||
1603 | if (!sway_assert(buffer && part, "Unable to allocate memory")) { | ||
1604 | free(buffer); | ||
1605 | return; | ||
1606 | } | ||
1607 | |||
1608 | for (int i = 0; i < con->marks->length; ++i) { | ||
1609 | char *mark = con->marks->items[i]; | ||
1610 | if (mark[0] != '_') { | ||
1611 | sprintf(part, "[%s]", mark); | ||
1612 | strcat(buffer, part); | ||
1613 | } | ||
1614 | } | ||
1615 | free(part); | ||
1616 | |||
1617 | double scale = output->wlr_output->scale; | ||
1618 | int width = 0; | ||
1619 | int height = con->title_height * scale; | ||
1620 | |||
1621 | cairo_t *c = cairo_create(NULL); | ||
1622 | get_text_size(c, config->font, &width, NULL, NULL, scale, false, | ||
1623 | "%s", buffer); | ||
1624 | cairo_destroy(c); | ||
1625 | |||
1626 | if (width == 0 || height == 0) { | ||
1627 | return; | ||
1628 | } | ||
1629 | |||
1630 | cairo_surface_t *surface = cairo_image_surface_create( | ||
1631 | CAIRO_FORMAT_ARGB32, width, height); | ||
1632 | cairo_t *cairo = cairo_create(surface); | ||
1633 | cairo_set_source_rgba(cairo, class->background[0], class->background[1], | ||
1634 | class->background[2], class->background[3]); | ||
1635 | cairo_paint(cairo); | ||
1636 | PangoContext *pango = pango_cairo_create_context(cairo); | ||
1637 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
1638 | cairo_set_source_rgba(cairo, class->text[0], class->text[1], | ||
1639 | class->text[2], class->text[3]); | ||
1640 | cairo_move_to(cairo, 0, 0); | ||
1641 | |||
1642 | pango_printf(cairo, config->font, scale, false, "%s", buffer); | ||
1643 | |||
1644 | cairo_surface_flush(surface); | ||
1645 | unsigned char *data = cairo_image_surface_get_data(surface); | ||
1646 | int stride = cairo_image_surface_get_stride(surface); | ||
1647 | struct wlr_renderer *renderer = wlr_backend_get_renderer( | ||
1648 | output->wlr_output->backend); | ||
1649 | *texture = wlr_texture_from_pixels( | ||
1650 | renderer, DRM_FORMAT_ARGB8888, stride, width, height, data); | ||
1651 | cairo_surface_destroy(surface); | ||
1652 | g_object_unref(pango); | ||
1653 | cairo_destroy(cairo); | ||
1654 | free(buffer); | ||
1655 | } | ||
1656 | |||
1657 | void container_update_marks_textures(struct sway_container *con) { | ||
1658 | if (!config->show_marks) { | ||
1659 | return; | ||
1660 | } | ||
1661 | update_marks_texture(con, &con->marks_focused, | ||
1662 | &config->border_colors.focused); | ||
1663 | update_marks_texture(con, &con->marks_focused_inactive, | ||
1664 | &config->border_colors.focused_inactive); | ||
1665 | update_marks_texture(con, &con->marks_unfocused, | ||
1666 | &config->border_colors.unfocused); | ||
1667 | update_marks_texture(con, &con->marks_urgent, | ||
1668 | &config->border_colors.urgent); | ||
1669 | container_damage_whole(con); | ||
1670 | } | ||
1671 | |||
1672 | void container_raise_floating(struct sway_container *con) { | 1574 | void container_raise_floating(struct sway_container *con) { |
1673 | // Bring container to front by putting it at the end of the floating list. | 1575 | // Bring container to front by putting it at the end of the floating list. |
1674 | struct sway_container *floater = container_toplevel_ancestor(con); | 1576 | struct sway_container *floater = container_toplevel_ancestor(con); |
1675 | if (container_is_floating(floater) && floater->pending.workspace) { | 1577 | if (container_is_floating(floater) && floater->pending.workspace) { |
1578 | // it's okay to just raise the scene directly instead of waiting | ||
1579 | // for the transaction to go through. We won't be reconfiguring | ||
1580 | // surfaces | ||
1581 | wlr_scene_node_raise_to_top(&floater->scene_tree->node); | ||
1582 | |||
1676 | list_move_to_end(floater->pending.workspace->floating, floater); | 1583 | list_move_to_end(floater->pending.workspace->floating, floater); |
1677 | node_set_dirty(&floater->pending.workspace->node); | 1584 | node_set_dirty(&floater->pending.workspace->node); |
1678 | } | 1585 | } |
@@ -1756,3 +1663,177 @@ int container_squash(struct sway_container *con) { | |||
1756 | } | 1663 | } |
1757 | return change; | 1664 | return change; |
1758 | } | 1665 | } |
1666 | |||
1667 | static void swap_places(struct sway_container *con1, | ||
1668 | struct sway_container *con2) { | ||
1669 | struct sway_container *temp = malloc(sizeof(struct sway_container)); | ||
1670 | temp->pending.x = con1->pending.x; | ||
1671 | temp->pending.y = con1->pending.y; | ||
1672 | temp->pending.width = con1->pending.width; | ||
1673 | temp->pending.height = con1->pending.height; | ||
1674 | temp->width_fraction = con1->width_fraction; | ||
1675 | temp->height_fraction = con1->height_fraction; | ||
1676 | temp->pending.parent = con1->pending.parent; | ||
1677 | temp->pending.workspace = con1->pending.workspace; | ||
1678 | bool temp_floating = container_is_floating(con1); | ||
1679 | |||
1680 | con1->pending.x = con2->pending.x; | ||
1681 | con1->pending.y = con2->pending.y; | ||
1682 | con1->pending.width = con2->pending.width; | ||
1683 | con1->pending.height = con2->pending.height; | ||
1684 | con1->width_fraction = con2->width_fraction; | ||
1685 | con1->height_fraction = con2->height_fraction; | ||
1686 | |||
1687 | con2->pending.x = temp->pending.x; | ||
1688 | con2->pending.y = temp->pending.y; | ||
1689 | con2->pending.width = temp->pending.width; | ||
1690 | con2->pending.height = temp->pending.height; | ||
1691 | con2->width_fraction = temp->width_fraction; | ||
1692 | con2->height_fraction = temp->height_fraction; | ||
1693 | |||
1694 | int temp_index = container_sibling_index(con1); | ||
1695 | if (con2->pending.parent) { | ||
1696 | container_insert_child(con2->pending.parent, con1, | ||
1697 | container_sibling_index(con2)); | ||
1698 | } else if (container_is_floating(con2)) { | ||
1699 | workspace_add_floating(con2->pending.workspace, con1); | ||
1700 | } else { | ||
1701 | workspace_insert_tiling(con2->pending.workspace, con1, | ||
1702 | container_sibling_index(con2)); | ||
1703 | } | ||
1704 | if (temp->pending.parent) { | ||
1705 | container_insert_child(temp->pending.parent, con2, temp_index); | ||
1706 | } else if (temp_floating) { | ||
1707 | workspace_add_floating(temp->pending.workspace, con2); | ||
1708 | } else { | ||
1709 | workspace_insert_tiling(temp->pending.workspace, con2, temp_index); | ||
1710 | } | ||
1711 | |||
1712 | free(temp); | ||
1713 | } | ||
1714 | |||
1715 | static void swap_focus(struct sway_container *con1, | ||
1716 | struct sway_container *con2, struct sway_seat *seat, | ||
1717 | struct sway_container *focus) { | ||
1718 | if (focus == con1 || focus == con2) { | ||
1719 | struct sway_workspace *ws1 = con1->pending.workspace; | ||
1720 | struct sway_workspace *ws2 = con2->pending.workspace; | ||
1721 | enum sway_container_layout layout1 = container_parent_layout(con1); | ||
1722 | enum sway_container_layout layout2 = container_parent_layout(con2); | ||
1723 | if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { | ||
1724 | if (workspace_is_visible(ws2)) { | ||
1725 | seat_set_focus(seat, &con2->node); | ||
1726 | } | ||
1727 | seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); | ||
1728 | } else if (focus == con2 && (layout1 == L_TABBED | ||
1729 | || layout1 == L_STACKED)) { | ||
1730 | if (workspace_is_visible(ws1)) { | ||
1731 | seat_set_focus(seat, &con1->node); | ||
1732 | } | ||
1733 | seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); | ||
1734 | } else if (ws1 != ws2) { | ||
1735 | seat_set_focus_container(seat, focus == con1 ? con2 : con1); | ||
1736 | } else { | ||
1737 | seat_set_focus_container(seat, focus); | ||
1738 | } | ||
1739 | } else { | ||
1740 | seat_set_focus_container(seat, focus); | ||
1741 | } | ||
1742 | |||
1743 | if (root->fullscreen_global) { | ||
1744 | seat_set_focus(seat, | ||
1745 | seat_get_focus_inactive(seat, &root->fullscreen_global->node)); | ||
1746 | } | ||
1747 | } | ||
1748 | |||
1749 | void container_swap(struct sway_container *con1, struct sway_container *con2) { | ||
1750 | if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { | ||
1751 | return; | ||
1752 | } | ||
1753 | if (!sway_assert(!container_has_ancestor(con1, con2) | ||
1754 | && !container_has_ancestor(con2, con1), | ||
1755 | "Cannot swap ancestor and descendant")) { | ||
1756 | return; | ||
1757 | } | ||
1758 | |||
1759 | sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", | ||
1760 | con1->node.id, con2->node.id); | ||
1761 | |||
1762 | bool scratch1 = con1->scratchpad; | ||
1763 | bool hidden1 = container_is_scratchpad_hidden(con1); | ||
1764 | bool scratch2 = con2->scratchpad; | ||
1765 | bool hidden2 = container_is_scratchpad_hidden(con2); | ||
1766 | if (scratch1) { | ||
1767 | if (hidden1) { | ||
1768 | root_scratchpad_show(con1); | ||
1769 | } | ||
1770 | root_scratchpad_remove_container(con1); | ||
1771 | } | ||
1772 | if (scratch2) { | ||
1773 | if (hidden2) { | ||
1774 | root_scratchpad_show(con2); | ||
1775 | } | ||
1776 | root_scratchpad_remove_container(con2); | ||
1777 | } | ||
1778 | |||
1779 | enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode; | ||
1780 | if (fs1) { | ||
1781 | container_fullscreen_disable(con1); | ||
1782 | } | ||
1783 | enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; | ||
1784 | if (fs2) { | ||
1785 | container_fullscreen_disable(con2); | ||
1786 | } | ||
1787 | |||
1788 | struct sway_seat *seat = input_manager_current_seat(); | ||
1789 | struct sway_container *focus = seat_get_focused_container(seat); | ||
1790 | struct sway_workspace *vis1 = | ||
1791 | output_get_active_workspace(con1->pending.workspace->output); | ||
1792 | struct sway_workspace *vis2 = | ||
1793 | output_get_active_workspace(con2->pending.workspace->output); | ||
1794 | if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" | ||
1795 | "workspace. This should not happen")) { | ||
1796 | return; | ||
1797 | } | ||
1798 | |||
1799 | char *stored_prev_name = NULL; | ||
1800 | if (seat->prev_workspace_name) { | ||
1801 | stored_prev_name = strdup(seat->prev_workspace_name); | ||
1802 | } | ||
1803 | |||
1804 | swap_places(con1, con2); | ||
1805 | |||
1806 | if (!workspace_is_visible(vis1)) { | ||
1807 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); | ||
1808 | } | ||
1809 | if (!workspace_is_visible(vis2)) { | ||
1810 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); | ||
1811 | } | ||
1812 | |||
1813 | swap_focus(con1, con2, seat, focus); | ||
1814 | |||
1815 | if (stored_prev_name) { | ||
1816 | free(seat->prev_workspace_name); | ||
1817 | seat->prev_workspace_name = stored_prev_name; | ||
1818 | } | ||
1819 | |||
1820 | if (scratch1) { | ||
1821 | root_scratchpad_add_container(con2, NULL); | ||
1822 | if (!hidden1) { | ||
1823 | root_scratchpad_show(con2); | ||
1824 | } | ||
1825 | } | ||
1826 | if (scratch2) { | ||
1827 | root_scratchpad_add_container(con1, NULL); | ||
1828 | if (!hidden2) { | ||
1829 | root_scratchpad_show(con1); | ||
1830 | } | ||
1831 | } | ||
1832 | |||
1833 | if (fs1) { | ||
1834 | container_set_fullscreen(con2, fs1); | ||
1835 | } | ||
1836 | if (fs2) { | ||
1837 | container_set_fullscreen(con1, fs2); | ||
1838 | } | ||
1839 | } | ||
diff --git a/sway/tree/node.c b/sway/tree/node.c index bc7e2aa5..7aaf9762 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/output.h" | 1 | #include "sway/output.h" |
3 | #include "sway/server.h" | 2 | #include "sway/server.h" |
4 | #include "sway/tree/container.h" | 3 | #include "sway/tree/container.h" |
@@ -18,13 +17,13 @@ void node_init(struct sway_node *node, enum sway_node_type type, void *thing) { | |||
18 | const char *node_type_to_str(enum sway_node_type type) { | 17 | const char *node_type_to_str(enum sway_node_type type) { |
19 | switch (type) { | 18 | switch (type) { |
20 | case N_ROOT: | 19 | case N_ROOT: |
21 | return "N_ROOT"; | 20 | return "root"; |
22 | case N_OUTPUT: | 21 | case N_OUTPUT: |
23 | return "N_OUTPUT"; | 22 | return "output"; |
24 | case N_WORKSPACE: | 23 | case N_WORKSPACE: |
25 | return "N_WORKSPACE"; | 24 | return "workspace"; |
26 | case N_CONTAINER: | 25 | case N_CONTAINER: |
27 | return "N_CONTAINER"; | 26 | return "container"; |
28 | } | 27 | } |
29 | return ""; | 28 | return ""; |
30 | } | 29 | } |
@@ -159,3 +158,32 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { | |||
159 | } | 158 | } |
160 | return false; | 159 | return false; |
161 | } | 160 | } |
161 | |||
162 | void scene_node_disown_children(struct wlr_scene_tree *tree) { | ||
163 | // this function can be called as part of destruction code that will be invoked | ||
164 | // upon an allocation failure. Let's not crash on NULL due to an allocation error. | ||
165 | if (!tree) { | ||
166 | return; | ||
167 | } | ||
168 | |||
169 | struct wlr_scene_node *child, *tmp_child; | ||
170 | wl_list_for_each_safe(child, tmp_child, &tree->children, link) { | ||
171 | wlr_scene_node_reparent(child, root->staging); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, | ||
176 | bool *failed) { | ||
177 | // fallthrough | ||
178 | if (*failed) { | ||
179 | return NULL; | ||
180 | } | ||
181 | |||
182 | struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); | ||
183 | if (!tree) { | ||
184 | sway_log(SWAY_ERROR, "Failed to allocate a scene node"); | ||
185 | *failed = true; | ||
186 | } | ||
187 | |||
188 | return tree; | ||
189 | } | ||
diff --git a/sway/tree/output.c b/sway/tree/output.c index c095dce0..2d11195e 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -1,14 +1,13 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <ctype.h> | 2 | #include <ctype.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <strings.h> | 4 | #include <strings.h> |
6 | #include <wlr/types/wlr_output_damage.h> | ||
7 | #include "sway/ipc-server.h" | 5 | #include "sway/ipc-server.h" |
8 | #include "sway/layers.h" | 6 | #include "sway/layers.h" |
9 | #include "sway/output.h" | 7 | #include "sway/output.h" |
10 | #include "sway/tree/arrange.h" | 8 | #include "sway/tree/arrange.h" |
11 | #include "sway/tree/workspace.h" | 9 | #include "sway/tree/workspace.h" |
10 | #include "sway/server.h" | ||
12 | #include "log.h" | 11 | #include "log.h" |
13 | #include "util.h" | 12 | #include "util.h" |
14 | 13 | ||
@@ -56,8 +55,8 @@ static void restore_workspaces(struct sway_output *output) { | |||
56 | } | 55 | } |
57 | 56 | ||
58 | // Saved workspaces | 57 | // Saved workspaces |
59 | while (root->noop_output->workspaces->length) { | 58 | while (root->fallback_output->workspaces->length) { |
60 | struct sway_workspace *ws = root->noop_output->workspaces->items[0]; | 59 | struct sway_workspace *ws = root->fallback_output->workspaces->items[0]; |
61 | workspace_detach(ws); | 60 | workspace_detach(ws); |
62 | output_add_workspace(output, ws); | 61 | output_add_workspace(output, ws); |
63 | 62 | ||
@@ -87,26 +86,63 @@ static void restore_workspaces(struct sway_output *output) { | |||
87 | output_sort_workspaces(output); | 86 | output_sort_workspaces(output); |
88 | } | 87 | } |
89 | 88 | ||
89 | static void destroy_scene_layers(struct sway_output *output) { | ||
90 | wlr_scene_node_destroy(&output->fullscreen_background->node); | ||
91 | |||
92 | scene_node_disown_children(output->layers.tiling); | ||
93 | scene_node_disown_children(output->layers.fullscreen); | ||
94 | |||
95 | wlr_scene_node_destroy(&output->layers.shell_background->node); | ||
96 | wlr_scene_node_destroy(&output->layers.shell_bottom->node); | ||
97 | wlr_scene_node_destroy(&output->layers.tiling->node); | ||
98 | wlr_scene_node_destroy(&output->layers.fullscreen->node); | ||
99 | wlr_scene_node_destroy(&output->layers.shell_top->node); | ||
100 | wlr_scene_node_destroy(&output->layers.shell_overlay->node); | ||
101 | wlr_scene_node_destroy(&output->layers.session_lock->node); | ||
102 | } | ||
103 | |||
90 | struct sway_output *output_create(struct wlr_output *wlr_output) { | 104 | struct sway_output *output_create(struct wlr_output *wlr_output) { |
91 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); | 105 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); |
92 | node_init(&output->node, N_OUTPUT, output); | 106 | node_init(&output->node, N_OUTPUT, output); |
107 | |||
108 | bool failed = false; | ||
109 | output->layers.shell_background = alloc_scene_tree(root->staging, &failed); | ||
110 | output->layers.shell_bottom = alloc_scene_tree(root->staging, &failed); | ||
111 | output->layers.tiling = alloc_scene_tree(root->staging, &failed); | ||
112 | output->layers.fullscreen = alloc_scene_tree(root->staging, &failed); | ||
113 | output->layers.shell_top = alloc_scene_tree(root->staging, &failed); | ||
114 | output->layers.shell_overlay = alloc_scene_tree(root->staging, &failed); | ||
115 | output->layers.session_lock = alloc_scene_tree(root->staging, &failed); | ||
116 | |||
117 | if (!failed) { | ||
118 | output->fullscreen_background = wlr_scene_rect_create( | ||
119 | output->layers.fullscreen, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f}); | ||
120 | |||
121 | if (!output->fullscreen_background) { | ||
122 | sway_log(SWAY_ERROR, "Unable to allocate a background rect"); | ||
123 | failed = true; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | if (failed) { | ||
128 | destroy_scene_layers(output); | ||
129 | wlr_scene_output_destroy(output->scene_output); | ||
130 | free(output); | ||
131 | return NULL; | ||
132 | } | ||
133 | |||
93 | output->wlr_output = wlr_output; | 134 | output->wlr_output = wlr_output; |
94 | wlr_output->data = output; | 135 | wlr_output->data = output; |
95 | output->detected_subpixel = wlr_output->subpixel; | 136 | output->detected_subpixel = wlr_output->subpixel; |
96 | output->scale_filter = SCALE_FILTER_NEAREST; | 137 | output->scale_filter = SCALE_FILTER_NEAREST; |
97 | 138 | ||
98 | wl_signal_init(&output->events.destroy); | 139 | wl_signal_init(&output->events.disable); |
99 | 140 | ||
100 | wl_list_insert(&root->all_outputs, &output->link); | 141 | wl_list_insert(&root->all_outputs, &output->link); |
101 | 142 | ||
102 | output->workspaces = create_list(); | 143 | output->workspaces = create_list(); |
103 | output->current.workspaces = create_list(); | 144 | output->current.workspaces = create_list(); |
104 | 145 | ||
105 | size_t len = sizeof(output->layers) / sizeof(output->layers[0]); | ||
106 | for (size_t i = 0; i < len; ++i) { | ||
107 | wl_list_init(&output->layers[i]); | ||
108 | } | ||
109 | |||
110 | return output; | 146 | return output; |
111 | } | 147 | } |
112 | 148 | ||
@@ -146,7 +182,7 @@ void output_enable(struct sway_output *output) { | |||
146 | 182 | ||
147 | input_manager_configure_xcursor(); | 183 | input_manager_configure_xcursor(); |
148 | 184 | ||
149 | wl_signal_emit(&root->events.new_node, &output->node); | 185 | wl_signal_emit_mutable(&root->events.new_node, &output->node); |
150 | 186 | ||
151 | arrange_layers(output); | 187 | arrange_layers(output); |
152 | arrange_root(); | 188 | arrange_root(); |
@@ -192,7 +228,7 @@ static void output_evacuate(struct sway_output *output) { | |||
192 | new_output = fallback_output; | 228 | new_output = fallback_output; |
193 | } | 229 | } |
194 | if (!new_output) { | 230 | if (!new_output) { |
195 | new_output = root->noop_output; | 231 | new_output = root->fallback_output; |
196 | } | 232 | } |
197 | 233 | ||
198 | struct sway_workspace *new_output_ws = | 234 | struct sway_workspace *new_output_ws = |
@@ -238,20 +274,14 @@ void output_destroy(struct sway_output *output) { | |||
238 | "which is still referenced by transactions")) { | 274 | "which is still referenced by transactions")) { |
239 | return; | 275 | return; |
240 | } | 276 | } |
277 | |||
278 | destroy_scene_layers(output); | ||
241 | list_free(output->workspaces); | 279 | list_free(output->workspaces); |
242 | list_free(output->current.workspaces); | 280 | list_free(output->current.workspaces); |
243 | wl_event_source_remove(output->repaint_timer); | 281 | wl_event_source_remove(output->repaint_timer); |
244 | free(output); | 282 | free(output); |
245 | } | 283 | } |
246 | 284 | ||
247 | static void untrack_output(struct sway_container *con, void *data) { | ||
248 | struct sway_output *output = data; | ||
249 | int index = list_find(con->outputs, output); | ||
250 | if (index != -1) { | ||
251 | list_del(con->outputs, index); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | void output_disable(struct sway_output *output) { | 285 | void output_disable(struct sway_output *output) { |
256 | if (!sway_assert(output->enabled, "Expected an enabled output")) { | 286 | if (!sway_assert(output->enabled, "Expected an enabled output")) { |
257 | return; | 287 | return; |
@@ -262,23 +292,20 @@ void output_disable(struct sway_output *output) { | |||
262 | } | 292 | } |
263 | 293 | ||
264 | sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); | 294 | sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); |
265 | wl_signal_emit(&output->events.destroy, output); | 295 | wl_signal_emit_mutable(&output->events.disable, output); |
266 | 296 | ||
267 | output_evacuate(output); | 297 | output_evacuate(output); |
268 | 298 | ||
269 | root_for_each_container(untrack_output, output); | ||
270 | |||
271 | list_del(root->outputs, index); | 299 | list_del(root->outputs, index); |
272 | 300 | ||
273 | output->enabled = false; | 301 | output->enabled = false; |
274 | output->current_mode = NULL; | ||
275 | 302 | ||
276 | arrange_root(); | 303 | arrange_root(); |
277 | 304 | ||
278 | // Reconfigure all devices, since devices with map_to_output directives for | 305 | // Reconfigure all devices, since devices with map_to_output directives for |
279 | // an output that goes offline should stop sending events as long as the | 306 | // an output that goes offline should stop sending events as long as the |
280 | // output remains offline. | 307 | // output remains offline. |
281 | input_manager_configure_all_inputs(); | 308 | input_manager_configure_all_input_mappings(); |
282 | } | 309 | } |
283 | 310 | ||
284 | void output_begin_destroy(struct sway_output *output) { | 311 | void output_begin_destroy(struct sway_output *output) { |
@@ -286,13 +313,10 @@ void output_begin_destroy(struct sway_output *output) { | |||
286 | return; | 313 | return; |
287 | } | 314 | } |
288 | sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); | 315 | sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); |
316 | wl_signal_emit_mutable(&output->node.events.destroy, &output->node); | ||
289 | 317 | ||
290 | output->node.destroying = true; | 318 | output->node.destroying = true; |
291 | node_set_dirty(&output->node); | 319 | node_set_dirty(&output->node); |
292 | |||
293 | wl_list_remove(&output->link); | ||
294 | output->wlr_output->data = NULL; | ||
295 | output->wlr_output = NULL; | ||
296 | } | 320 | } |
297 | 321 | ||
298 | struct sway_output *output_from_wlr_output(struct wlr_output *output) { | 322 | struct sway_output *output_from_wlr_output(struct wlr_output *output) { |
@@ -304,10 +328,10 @@ struct sway_output *output_get_in_direction(struct sway_output *reference, | |||
304 | if (!sway_assert(direction, "got invalid direction: %d", direction)) { | 328 | if (!sway_assert(direction, "got invalid direction: %d", direction)) { |
305 | return NULL; | 329 | return NULL; |
306 | } | 330 | } |
307 | struct wlr_box *output_box = | 331 | struct wlr_box output_box; |
308 | wlr_output_layout_get_box(root->output_layout, reference->wlr_output); | 332 | wlr_output_layout_get_box(root->output_layout, reference->wlr_output, &output_box); |
309 | int lx = output_box->x + output_box->width / 2; | 333 | int lx = output_box.x + output_box.width / 2; |
310 | int ly = output_box->y + output_box->height / 2; | 334 | int ly = output_box.y + output_box.height / 2; |
311 | struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( | 335 | struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( |
312 | root->output_layout, direction, reference->wlr_output, lx, ly); | 336 | root->output_layout, direction, reference->wlr_output, lx, ly); |
313 | if (!wlr_adjacent) { | 337 | if (!wlr_adjacent) { |
@@ -393,6 +417,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) { | |||
393 | box->height = output->height; | 417 | box->height = output->height; |
394 | } | 418 | } |
395 | 419 | ||
420 | static void handle_destroy_non_desktop(struct wl_listener *listener, void *data) { | ||
421 | struct sway_output_non_desktop *output = | ||
422 | wl_container_of(listener, output, destroy); | ||
423 | |||
424 | sway_log(SWAY_DEBUG, "Destroying non-desktop output '%s'", output->wlr_output->name); | ||
425 | |||
426 | int index = list_find(root->non_desktop_outputs, output); | ||
427 | list_del(root->non_desktop_outputs, index); | ||
428 | |||
429 | wl_list_remove(&output->destroy.link); | ||
430 | |||
431 | free(output); | ||
432 | } | ||
433 | |||
434 | struct sway_output_non_desktop *output_non_desktop_create( | ||
435 | struct wlr_output *wlr_output) { | ||
436 | struct sway_output_non_desktop *output = | ||
437 | calloc(1, sizeof(struct sway_output_non_desktop)); | ||
438 | |||
439 | output->wlr_output = wlr_output; | ||
440 | |||
441 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | ||
442 | output->destroy.notify = handle_destroy_non_desktop; | ||
443 | |||
444 | return output; | ||
445 | } | ||
446 | |||
396 | enum sway_container_layout output_get_default_layout( | 447 | enum sway_container_layout output_get_default_layout( |
397 | struct sway_output *output) { | 448 | struct sway_output *output) { |
398 | if (config->default_orientation != L_NONE) { | 449 | if (config->default_orientation != L_NONE) { |
diff --git a/sway/tree/root.c b/sway/tree/root.c index dd4d8e33..ae3c3cb2 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c | |||
@@ -1,12 +1,14 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <wlr/types/wlr_output_layout.h> | 4 | #include <wlr/types/wlr_output_layout.h> |
5 | #include <wlr/types/wlr_scene.h> | ||
6 | #include <wlr/util/transform.h> | ||
6 | #include "sway/desktop/transaction.h" | 7 | #include "sway/desktop/transaction.h" |
7 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
8 | #include "sway/ipc-server.h" | 9 | #include "sway/ipc-server.h" |
9 | #include "sway/output.h" | 10 | #include "sway/output.h" |
11 | #include "sway/scene_descriptor.h" | ||
10 | #include "sway/tree/arrange.h" | 12 | #include "sway/tree/arrange.h" |
11 | #include "sway/tree/container.h" | 13 | #include "sway/tree/container.h" |
12 | #include "sway/tree/root.h" | 14 | #include "sway/tree/root.h" |
@@ -23,21 +25,60 @@ static void output_layout_handle_change(struct wl_listener *listener, | |||
23 | transaction_commit_dirty(); | 25 | transaction_commit_dirty(); |
24 | } | 26 | } |
25 | 27 | ||
26 | struct sway_root *root_create(void) { | 28 | struct sway_root *root_create(struct wl_display *wl_display) { |
27 | struct sway_root *root = calloc(1, sizeof(struct sway_root)); | 29 | struct sway_root *root = calloc(1, sizeof(struct sway_root)); |
28 | if (!root) { | 30 | if (!root) { |
29 | sway_log(SWAY_ERROR, "Unable to allocate sway_root"); | 31 | sway_log(SWAY_ERROR, "Unable to allocate sway_root"); |
30 | return NULL; | 32 | return NULL; |
31 | } | 33 | } |
34 | |||
35 | struct wlr_scene *root_scene = wlr_scene_create(); | ||
36 | if (!root_scene) { | ||
37 | sway_log(SWAY_ERROR, "Unable to allocate root scene node"); | ||
38 | free(root); | ||
39 | return NULL; | ||
40 | } | ||
41 | |||
32 | node_init(&root->node, N_ROOT, root); | 42 | node_init(&root->node, N_ROOT, root); |
33 | root->output_layout = wlr_output_layout_create(); | 43 | root->root_scene = root_scene; |
34 | wl_list_init(&root->all_outputs); | 44 | |
45 | bool failed = false; | ||
46 | root->staging = alloc_scene_tree(&root_scene->tree, &failed); | ||
47 | root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed); | ||
48 | |||
49 | root->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed); | ||
50 | root->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed); | ||
51 | root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed); | ||
52 | root->layers.floating = alloc_scene_tree(root->layer_tree, &failed); | ||
53 | root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed); | ||
54 | root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed); | ||
55 | root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed); | ||
35 | #if HAVE_XWAYLAND | 56 | #if HAVE_XWAYLAND |
36 | wl_list_init(&root->xwayland_unmanaged); | 57 | root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed); |
37 | #endif | 58 | #endif |
38 | wl_list_init(&root->drag_icons); | 59 | root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed); |
60 | root->layers.popup = alloc_scene_tree(root->layer_tree, &failed); | ||
61 | root->layers.seat = alloc_scene_tree(root->layer_tree, &failed); | ||
62 | root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed); | ||
63 | |||
64 | if (!failed && !scene_descriptor_assign(&root->layers.seat->node, | ||
65 | SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) { | ||
66 | failed = true; | ||
67 | } | ||
68 | |||
69 | if (failed) { | ||
70 | wlr_scene_node_destroy(&root_scene->tree.node); | ||
71 | free(root); | ||
72 | return NULL; | ||
73 | } | ||
74 | |||
75 | wlr_scene_node_set_enabled(&root->staging->node, false); | ||
76 | |||
77 | root->output_layout = wlr_output_layout_create(wl_display); | ||
78 | wl_list_init(&root->all_outputs); | ||
39 | wl_signal_init(&root->events.new_node); | 79 | wl_signal_init(&root->events.new_node); |
40 | root->outputs = create_list(); | 80 | root->outputs = create_list(); |
81 | root->non_desktop_outputs = create_list(); | ||
41 | root->scratchpad = create_list(); | 82 | root->scratchpad = create_list(); |
42 | 83 | ||
43 | root->output_layout_change.notify = output_layout_handle_change; | 84 | root->output_layout_change.notify = output_layout_handle_change; |
@@ -49,11 +90,22 @@ struct sway_root *root_create(void) { | |||
49 | void root_destroy(struct sway_root *root) { | 90 | void root_destroy(struct sway_root *root) { |
50 | wl_list_remove(&root->output_layout_change.link); | 91 | wl_list_remove(&root->output_layout_change.link); |
51 | list_free(root->scratchpad); | 92 | list_free(root->scratchpad); |
93 | list_free(root->non_desktop_outputs); | ||
52 | list_free(root->outputs); | 94 | list_free(root->outputs); |
53 | wlr_output_layout_destroy(root->output_layout); | 95 | wlr_scene_node_destroy(&root->root_scene->tree.node); |
54 | free(root); | 96 | free(root); |
55 | } | 97 | } |
56 | 98 | ||
99 | static void set_container_transform(struct sway_workspace *ws, | ||
100 | struct sway_container *con) { | ||
101 | struct sway_output *output = ws->output; | ||
102 | struct wlr_box box = {0}; | ||
103 | if (output) { | ||
104 | output_get_box(output, &box); | ||
105 | } | ||
106 | con->transform = box; | ||
107 | } | ||
108 | |||
57 | void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { | 109 | void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { |
58 | if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { | 110 | if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { |
59 | return; | 111 | return; |
@@ -62,6 +114,8 @@ void root_scratchpad_add_container(struct sway_container *con, struct sway_works | |||
62 | struct sway_container *parent = con->pending.parent; | 114 | struct sway_container *parent = con->pending.parent; |
63 | struct sway_workspace *workspace = con->pending.workspace; | 115 | struct sway_workspace *workspace = con->pending.workspace; |
64 | 116 | ||
117 | set_container_transform(workspace, con); | ||
118 | |||
65 | // Clear the fullscreen mode when sending to the scratchpad | 119 | // Clear the fullscreen mode when sending to the scratchpad |
66 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { | 120 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { |
67 | container_fullscreen_disable(con); | 121 | container_fullscreen_disable(con); |
@@ -131,7 +185,10 @@ void root_scratchpad_show(struct sway_container *con) { | |||
131 | // Show the container | 185 | // Show the container |
132 | if (old_ws) { | 186 | if (old_ws) { |
133 | container_detach(con); | 187 | container_detach(con); |
134 | workspace_consider_destroy(old_ws); | 188 | // Make sure the last inactive container on the old workspace is above |
189 | // the workspace itself in the focus stack. | ||
190 | struct sway_node *node = seat_get_focus_inactive(seat, &old_ws->node); | ||
191 | seat_set_raw_focus(seat, node); | ||
135 | } else { | 192 | } else { |
136 | // Act on the ancestor of scratchpad hidden split containers | 193 | // Act on the ancestor of scratchpad hidden split containers |
137 | while (con->pending.parent) { | 194 | while (con->pending.parent) { |
@@ -140,18 +197,18 @@ void root_scratchpad_show(struct sway_container *con) { | |||
140 | } | 197 | } |
141 | workspace_add_floating(new_ws, con); | 198 | workspace_add_floating(new_ws, con); |
142 | 199 | ||
143 | // Make sure the container's center point overlaps this workspace | 200 | if (new_ws->output) { |
144 | double center_lx = con->pending.x + con->pending.width / 2; | 201 | struct wlr_box output_box; |
145 | double center_ly = con->pending.y + con->pending.height / 2; | 202 | output_get_box(new_ws->output, &output_box); |
146 | 203 | floating_fix_coordinates(con, &con->transform, &output_box); | |
147 | struct wlr_box workspace_box; | ||
148 | workspace_get_box(new_ws, &workspace_box); | ||
149 | if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { | ||
150 | container_floating_resize_and_center(con); | ||
151 | } | 204 | } |
205 | set_container_transform(new_ws, con); | ||
152 | 206 | ||
153 | arrange_workspace(new_ws); | 207 | arrange_workspace(new_ws); |
154 | seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); | 208 | seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); |
209 | if (old_ws) { | ||
210 | workspace_consider_destroy(old_ws); | ||
211 | } | ||
155 | } | 212 | } |
156 | 213 | ||
157 | static void disable_fullscreen(struct sway_container *con, void *data) { | 214 | static void disable_fullscreen(struct sway_container *con, void *data) { |
@@ -171,6 +228,8 @@ void root_scratchpad_hide(struct sway_container *con) { | |||
171 | return; | 228 | return; |
172 | } | 229 | } |
173 | 230 | ||
231 | set_container_transform(con->pending.workspace, con); | ||
232 | |||
174 | disable_fullscreen(con, NULL); | 233 | disable_fullscreen(con, NULL); |
175 | container_for_each_child(con, disable_fullscreen, NULL); | 234 | container_for_each_child(con, disable_fullscreen, NULL); |
176 | container_detach(con); | 235 | container_detach(con); |
@@ -183,172 +242,6 @@ void root_scratchpad_hide(struct sway_container *con) { | |||
183 | ipc_event_window(con, "move"); | 242 | ipc_event_window(con, "move"); |
184 | } | 243 | } |
185 | 244 | ||
186 | struct pid_workspace { | ||
187 | pid_t pid; | ||
188 | char *workspace; | ||
189 | struct timespec time_added; | ||
190 | |||
191 | struct sway_output *output; | ||
192 | struct wl_listener output_destroy; | ||
193 | |||
194 | struct wl_list link; | ||
195 | }; | ||
196 | |||
197 | static struct wl_list pid_workspaces; | ||
198 | |||
199 | /** | ||
200 | * Get the pid of a parent process given the pid of a child process. | ||
201 | * | ||
202 | * Returns the parent pid or NULL if the parent pid cannot be determined. | ||
203 | */ | ||
204 | static pid_t get_parent_pid(pid_t child) { | ||
205 | pid_t parent = -1; | ||
206 | char file_name[100]; | ||
207 | char *buffer = NULL; | ||
208 | const char *sep = " "; | ||
209 | FILE *stat = NULL; | ||
210 | size_t buf_size = 0; | ||
211 | |||
212 | sprintf(file_name, "/proc/%d/stat", child); | ||
213 | |||
214 | if ((stat = fopen(file_name, "r"))) { | ||
215 | if (getline(&buffer, &buf_size, stat) != -1) { | ||
216 | strtok(buffer, sep); // pid | ||
217 | strtok(NULL, sep); // executable name | ||
218 | strtok(NULL, sep); // state | ||
219 | char *token = strtok(NULL, sep); // parent pid | ||
220 | parent = strtol(token, NULL, 10); | ||
221 | } | ||
222 | free(buffer); | ||
223 | fclose(stat); | ||
224 | } | ||
225 | |||
226 | if (parent) { | ||
227 | return (parent == child) ? -1 : parent; | ||
228 | } | ||
229 | |||
230 | return -1; | ||
231 | } | ||
232 | |||
233 | static void pid_workspace_destroy(struct pid_workspace *pw) { | ||
234 | wl_list_remove(&pw->output_destroy.link); | ||
235 | wl_list_remove(&pw->link); | ||
236 | free(pw->workspace); | ||
237 | free(pw); | ||
238 | } | ||
239 | |||
240 | struct sway_workspace *root_workspace_for_pid(pid_t pid) { | ||
241 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
242 | wl_list_init(&pid_workspaces); | ||
243 | return NULL; | ||
244 | } | ||
245 | |||
246 | struct sway_workspace *ws = NULL; | ||
247 | struct pid_workspace *pw = NULL; | ||
248 | |||
249 | sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); | ||
250 | |||
251 | do { | ||
252 | struct pid_workspace *_pw = NULL; | ||
253 | wl_list_for_each(_pw, &pid_workspaces, link) { | ||
254 | if (pid == _pw->pid) { | ||
255 | pw = _pw; | ||
256 | sway_log(SWAY_DEBUG, | ||
257 | "found pid_workspace for pid %d, workspace %s", | ||
258 | pid, pw->workspace); | ||
259 | goto found; | ||
260 | } | ||
261 | } | ||
262 | pid = get_parent_pid(pid); | ||
263 | } while (pid > 1); | ||
264 | found: | ||
265 | |||
266 | if (pw && pw->workspace) { | ||
267 | ws = workspace_by_name(pw->workspace); | ||
268 | |||
269 | if (!ws) { | ||
270 | sway_log(SWAY_DEBUG, | ||
271 | "Creating workspace %s for pid %d because it disappeared", | ||
272 | pw->workspace, pid); | ||
273 | |||
274 | struct sway_output *output = pw->output; | ||
275 | if (pw->output && !pw->output->enabled) { | ||
276 | sway_log(SWAY_DEBUG, | ||
277 | "Workspace output %s is disabled, trying another one", | ||
278 | pw->output->wlr_output->name); | ||
279 | output = NULL; | ||
280 | } | ||
281 | |||
282 | ws = workspace_create(output, pw->workspace); | ||
283 | } | ||
284 | |||
285 | pid_workspace_destroy(pw); | ||
286 | } | ||
287 | |||
288 | return ws; | ||
289 | } | ||
290 | |||
291 | static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { | ||
292 | struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); | ||
293 | pw->output = NULL; | ||
294 | wl_list_remove(&pw->output_destroy.link); | ||
295 | wl_list_init(&pw->output_destroy.link); | ||
296 | } | ||
297 | |||
298 | void root_record_workspace_pid(pid_t pid) { | ||
299 | sway_log(SWAY_DEBUG, "Recording workspace for process %d", pid); | ||
300 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
301 | wl_list_init(&pid_workspaces); | ||
302 | } | ||
303 | |||
304 | struct sway_seat *seat = input_manager_current_seat(); | ||
305 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | ||
306 | if (!ws) { | ||
307 | sway_log(SWAY_DEBUG, "Bailing out, no workspace"); | ||
308 | return; | ||
309 | } | ||
310 | struct sway_output *output = ws->output; | ||
311 | if (!output) { | ||
312 | sway_log(SWAY_DEBUG, "Bailing out, no output"); | ||
313 | return; | ||
314 | } | ||
315 | |||
316 | struct timespec now; | ||
317 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
318 | |||
319 | // Remove expired entries | ||
320 | static const int timeout = 60; | ||
321 | struct pid_workspace *old, *_old; | ||
322 | wl_list_for_each_safe(old, _old, &pid_workspaces, link) { | ||
323 | if (now.tv_sec - old->time_added.tv_sec >= timeout) { | ||
324 | pid_workspace_destroy(old); | ||
325 | } | ||
326 | } | ||
327 | |||
328 | struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); | ||
329 | pw->workspace = strdup(ws->name); | ||
330 | pw->output = output; | ||
331 | pw->pid = pid; | ||
332 | memcpy(&pw->time_added, &now, sizeof(struct timespec)); | ||
333 | pw->output_destroy.notify = pw_handle_output_destroy; | ||
334 | wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy); | ||
335 | wl_list_insert(&pid_workspaces, &pw->link); | ||
336 | } | ||
337 | |||
338 | void root_remove_workspace_pid(pid_t pid) { | ||
339 | if (!pid_workspaces.prev || !pid_workspaces.next) { | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | struct pid_workspace *pw, *tmp; | ||
344 | wl_list_for_each_safe(pw, tmp, &pid_workspaces, link) { | ||
345 | if (pid == pw->pid) { | ||
346 | pid_workspace_destroy(pw); | ||
347 | return; | ||
348 | } | ||
349 | } | ||
350 | } | ||
351 | |||
352 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), | 245 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), |
353 | void *data) { | 246 | void *data) { |
354 | for (int i = 0; i < root->outputs->length; ++i) { | 247 | for (int i = 0; i < root->outputs->length; ++i) { |
@@ -374,8 +267,8 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data), | |||
374 | } | 267 | } |
375 | 268 | ||
376 | // Saved workspaces | 269 | // Saved workspaces |
377 | for (int i = 0; i < root->noop_output->workspaces->length; ++i) { | 270 | for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { |
378 | struct sway_workspace *ws = root->noop_output->workspaces->items[i]; | 271 | struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; |
379 | workspace_for_each_container(ws, f, data); | 272 | workspace_for_each_container(ws, f, data); |
380 | } | 273 | } |
381 | } | 274 | } |
@@ -427,8 +320,8 @@ struct sway_container *root_find_container( | |||
427 | } | 320 | } |
428 | 321 | ||
429 | // Saved workspaces | 322 | // Saved workspaces |
430 | for (int i = 0; i < root->noop_output->workspaces->length; ++i) { | 323 | for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { |
431 | struct sway_workspace *ws = root->noop_output->workspaces->items[i]; | 324 | struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; |
432 | if ((result = workspace_find_container(ws, test, data))) { | 325 | if ((result = workspace_find_container(ws, test, data))) { |
433 | return result; | 326 | return result; |
434 | } | 327 | } |
@@ -443,17 +336,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) { | |||
443 | box->width = root->width; | 336 | box->width = root->width; |
444 | box->height = root->height; | 337 | box->height = root->height; |
445 | } | 338 | } |
446 | |||
447 | void root_rename_pid_workspaces(const char *old_name, const char *new_name) { | ||
448 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
449 | wl_list_init(&pid_workspaces); | ||
450 | } | ||
451 | |||
452 | struct pid_workspace *pw = NULL; | ||
453 | wl_list_for_each(pw, &pid_workspaces, link) { | ||
454 | if (strcmp(pw->workspace, old_name) == 0) { | ||
455 | free(pw->workspace); | ||
456 | pw->workspace = strdup(new_name); | ||
457 | } | ||
458 | } | ||
459 | } | ||
diff --git a/sway/tree/view.c b/sway/tree/view.c index fcdd06f7..35b4b73f 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -1,11 +1,13 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include <wayland-server-core.h> | 3 | #include <wayland-server-core.h> |
5 | #include <wlr/render/wlr_renderer.h> | 4 | #include <wlr/render/wlr_renderer.h> |
6 | #include <wlr/types/wlr_buffer.h> | 5 | #include <wlr/types/wlr_buffer.h> |
6 | #include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h> | ||
7 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
7 | #include <wlr/types/wlr_output_layout.h> | 8 | #include <wlr/types/wlr_output_layout.h> |
8 | #include <wlr/types/wlr_server_decoration.h> | 9 | #include <wlr/types/wlr_server_decoration.h> |
10 | #include <wlr/types/wlr_subcompositor.h> | ||
9 | #include <wlr/types/wlr_xdg_decoration_v1.h> | 11 | #include <wlr/types/wlr_xdg_decoration_v1.h> |
10 | #include "config.h" | 12 | #include "config.h" |
11 | #if HAVE_XWAYLAND | 13 | #if HAVE_XWAYLAND |
@@ -15,14 +17,16 @@ | |||
15 | #include "log.h" | 17 | #include "log.h" |
16 | #include "sway/criteria.h" | 18 | #include "sway/criteria.h" |
17 | #include "sway/commands.h" | 19 | #include "sway/commands.h" |
18 | #include "sway/desktop.h" | ||
19 | #include "sway/desktop/transaction.h" | 20 | #include "sway/desktop/transaction.h" |
20 | #include "sway/desktop/idle_inhibit_v1.h" | 21 | #include "sway/desktop/idle_inhibit_v1.h" |
22 | #include "sway/desktop/launcher.h" | ||
21 | #include "sway/input/cursor.h" | 23 | #include "sway/input/cursor.h" |
22 | #include "sway/ipc-server.h" | 24 | #include "sway/ipc-server.h" |
23 | #include "sway/output.h" | 25 | #include "sway/output.h" |
24 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/scene_descriptor.h" | ||
25 | #include "sway/server.h" | 28 | #include "sway/server.h" |
29 | #include "sway/sway_text_node.h" | ||
26 | #include "sway/tree/arrange.h" | 30 | #include "sway/tree/arrange.h" |
27 | #include "sway/tree/container.h" | 31 | #include "sway/tree/container.h" |
28 | #include "sway/tree/view.h" | 32 | #include "sway/tree/view.h" |
@@ -32,15 +36,29 @@ | |||
32 | #include "pango.h" | 36 | #include "pango.h" |
33 | #include "stringop.h" | 37 | #include "stringop.h" |
34 | 38 | ||
35 | void view_init(struct sway_view *view, enum sway_view_type type, | 39 | bool view_init(struct sway_view *view, enum sway_view_type type, |
36 | const struct sway_view_impl *impl) { | 40 | const struct sway_view_impl *impl) { |
41 | bool failed = false; | ||
42 | view->scene_tree = alloc_scene_tree(root->staging, &failed); | ||
43 | view->content_tree = alloc_scene_tree(view->scene_tree, &failed); | ||
44 | |||
45 | if (!failed && !scene_descriptor_assign(&view->scene_tree->node, | ||
46 | SWAY_SCENE_DESC_VIEW, view)) { | ||
47 | failed = true; | ||
48 | } | ||
49 | |||
50 | if (failed) { | ||
51 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
52 | return false; | ||
53 | } | ||
54 | |||
37 | view->type = type; | 55 | view->type = type; |
38 | view->impl = impl; | 56 | view->impl = impl; |
39 | view->executed_criteria = create_list(); | 57 | view->executed_criteria = create_list(); |
40 | wl_list_init(&view->saved_buffers); | ||
41 | view->allow_request_urgent = true; | 58 | view->allow_request_urgent = true; |
42 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; | 59 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; |
43 | wl_signal_init(&view->events.unmap); | 60 | wl_signal_init(&view->events.unmap); |
61 | return true; | ||
44 | } | 62 | } |
45 | 63 | ||
46 | void view_destroy(struct sway_view *view) { | 64 | void view_destroy(struct sway_view *view) { |
@@ -57,11 +75,10 @@ void view_destroy(struct sway_view *view) { | |||
57 | return; | 75 | return; |
58 | } | 76 | } |
59 | wl_list_remove(&view->events.unmap.listener_list); | 77 | wl_list_remove(&view->events.unmap.listener_list); |
60 | if (!wl_list_empty(&view->saved_buffers)) { | ||
61 | view_remove_saved_buffer(view); | ||
62 | } | ||
63 | list_free(view->executed_criteria); | 78 | list_free(view->executed_criteria); |
64 | 79 | ||
80 | view_assign_ctx(view, NULL); | ||
81 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
65 | free(view->title_format); | 82 | free(view->title_format); |
66 | 83 | ||
67 | if (view->impl->destroy) { | 84 | if (view->impl->destroy) { |
@@ -362,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
362 | } | 379 | } |
363 | } | 380 | } |
364 | 381 | ||
365 | void view_request_activate(struct sway_view *view) { | 382 | void view_request_activate(struct sway_view *view, struct sway_seat *seat) { |
366 | struct sway_workspace *ws = view->container->pending.workspace; | 383 | struct sway_workspace *ws = view->container->pending.workspace; |
367 | if (!ws) { // hidden scratchpad container | 384 | if (!seat) { |
368 | return; | 385 | seat = input_manager_current_seat(); |
369 | } | 386 | } |
370 | struct sway_seat *seat = input_manager_current_seat(); | ||
371 | 387 | ||
372 | switch (config->focus_on_window_activation) { | 388 | switch (config->focus_on_window_activation) { |
373 | case FOWA_SMART: | 389 | case FOWA_SMART: |
374 | if (workspace_is_visible(ws)) { | 390 | if (ws && workspace_is_visible(ws)) { |
375 | seat_set_focus_container(seat, view->container); | 391 | seat_set_focus_container(seat, view->container); |
392 | container_raise_floating(view->container); | ||
376 | } else { | 393 | } else { |
377 | view_set_urgent(view, true); | 394 | view_set_urgent(view, true); |
378 | } | 395 | } |
@@ -381,11 +398,23 @@ void view_request_activate(struct sway_view *view) { | |||
381 | view_set_urgent(view, true); | 398 | view_set_urgent(view, true); |
382 | break; | 399 | break; |
383 | case FOWA_FOCUS: | 400 | case FOWA_FOCUS: |
384 | seat_set_focus_container(seat, view->container); | 401 | if (container_is_scratchpad_hidden_or_child(view->container)) { |
402 | root_scratchpad_show(view->container); | ||
403 | } else { | ||
404 | seat_set_focus_container(seat, view->container); | ||
405 | container_raise_floating(view->container); | ||
406 | } | ||
385 | break; | 407 | break; |
386 | case FOWA_NONE: | 408 | case FOWA_NONE: |
387 | break; | 409 | break; |
388 | } | 410 | } |
411 | transaction_commit_dirty(); | ||
412 | } | ||
413 | |||
414 | void view_request_urgent(struct sway_view *view) { | ||
415 | if (config->focus_on_window_activation != FOWA_NONE) { | ||
416 | view_set_urgent(view, true); | ||
417 | } | ||
389 | } | 418 | } |
390 | 419 | ||
391 | void view_set_csd_from_server(struct sway_view *view, bool enabled) { | 420 | void view_set_csd_from_server(struct sway_view *view, bool enabled) { |
@@ -432,52 +461,6 @@ void view_close_popups(struct sway_view *view) { | |||
432 | } | 461 | } |
433 | } | 462 | } |
434 | 463 | ||
435 | void view_damage_from(struct sway_view *view) { | ||
436 | for (int i = 0; i < root->outputs->length; ++i) { | ||
437 | struct sway_output *output = root->outputs->items[i]; | ||
438 | output_damage_from_view(output, view); | ||
439 | } | ||
440 | } | ||
441 | |||
442 | void view_for_each_surface(struct sway_view *view, | ||
443 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
444 | if (!view->surface) { | ||
445 | return; | ||
446 | } | ||
447 | if (view->impl->for_each_surface) { | ||
448 | view->impl->for_each_surface(view, iterator, user_data); | ||
449 | } else { | ||
450 | wlr_surface_for_each_surface(view->surface, iterator, user_data); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | void view_for_each_popup_surface(struct sway_view *view, | ||
455 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
456 | if (!view->surface) { | ||
457 | return; | ||
458 | } | ||
459 | if (view->impl->for_each_popup_surface) { | ||
460 | view->impl->for_each_popup_surface(view, iterator, user_data); | ||
461 | } | ||
462 | } | ||
463 | |||
464 | static void view_subsurface_create(struct sway_view *view, | ||
465 | struct wlr_subsurface *subsurface); | ||
466 | |||
467 | static void view_init_subsurfaces(struct sway_view *view, | ||
468 | struct wlr_surface *surface); | ||
469 | |||
470 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
471 | struct wlr_surface *surface); | ||
472 | |||
473 | static void view_handle_surface_new_subsurface(struct wl_listener *listener, | ||
474 | void *data) { | ||
475 | struct sway_view *view = | ||
476 | wl_container_of(listener, view, surface_new_subsurface); | ||
477 | struct wlr_subsurface *subsurface = data; | ||
478 | view_subsurface_create(view, subsurface); | ||
479 | } | ||
480 | |||
481 | static bool view_has_executed_criteria(struct sway_view *view, | 464 | static bool view_has_executed_criteria(struct sway_view *view, |
482 | struct criteria *criteria) { | 465 | struct criteria *criteria) { |
483 | for (int i = 0; i < view->executed_criteria->length; ++i) { | 466 | for (int i = 0; i < view->executed_criteria->length; ++i) { |
@@ -519,7 +502,7 @@ static void view_populate_pid(struct sway_view *view) { | |||
519 | #if HAVE_XWAYLAND | 502 | #if HAVE_XWAYLAND |
520 | case SWAY_VIEW_XWAYLAND:; | 503 | case SWAY_VIEW_XWAYLAND:; |
521 | struct wlr_xwayland_surface *surf = | 504 | struct wlr_xwayland_surface *surf = |
522 | wlr_xwayland_surface_from_wlr_surface(view->surface); | 505 | wlr_xwayland_surface_try_from_wlr_surface(view->surface); |
523 | pid = surf->pid; | 506 | pid = surf->pid; |
524 | break; | 507 | break; |
525 | #endif | 508 | #endif |
@@ -532,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) { | |||
532 | view->pid = pid; | 515 | view->pid = pid; |
533 | } | 516 | } |
534 | 517 | ||
518 | void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) { | ||
519 | if (view->ctx) { | ||
520 | // This ctx has been replaced | ||
521 | launcher_ctx_destroy(view->ctx); | ||
522 | view->ctx = NULL; | ||
523 | } | ||
524 | if (ctx == NULL) { | ||
525 | return; | ||
526 | } | ||
527 | launcher_ctx_consume(ctx); | ||
528 | |||
529 | view->ctx = ctx; | ||
530 | } | ||
531 | |||
535 | static struct sway_workspace *select_workspace(struct sway_view *view) { | 532 | static struct sway_workspace *select_workspace(struct sway_view *view) { |
536 | struct sway_seat *seat = input_manager_current_seat(); | 533 | struct sway_seat *seat = input_manager_current_seat(); |
537 | 534 | ||
@@ -567,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
567 | } | 564 | } |
568 | list_free(criterias); | 565 | list_free(criterias); |
569 | if (ws) { | 566 | if (ws) { |
570 | root_remove_workspace_pid(view->pid); | 567 | view_assign_ctx(view, NULL); |
571 | return ws; | 568 | return ws; |
572 | } | 569 | } |
573 | 570 | ||
574 | // Check if there's a PID mapping | 571 | // Check if there's a PID mapping |
575 | ws = root_workspace_for_pid(view->pid); | 572 | ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL; |
576 | if (ws) { | 573 | if (ws) { |
574 | view_assign_ctx(view, NULL); | ||
577 | return ws; | 575 | return ws; |
578 | } | 576 | } |
579 | 577 | ||
@@ -591,6 +589,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
591 | return NULL; | 589 | return NULL; |
592 | } | 590 | } |
593 | 591 | ||
592 | static void update_ext_foreign_toplevel(struct sway_view *view) { | ||
593 | struct wlr_ext_foreign_toplevel_handle_v1_state toplevel_state = { | ||
594 | .app_id = view_get_app_id(view), | ||
595 | .title = view_get_title(view), | ||
596 | }; | ||
597 | wlr_ext_foreign_toplevel_handle_v1_update_state(view->ext_foreign_toplevel, &toplevel_state); | ||
598 | } | ||
599 | |||
594 | static bool should_focus(struct sway_view *view) { | 600 | static bool should_focus(struct sway_view *view) { |
595 | struct sway_seat *seat = input_manager_current_seat(); | 601 | struct sway_seat *seat = input_manager_current_seat(); |
596 | struct sway_container *prev_con = seat_get_focused_container(seat); | 602 | struct sway_container *prev_con = seat_get_focused_container(seat); |
@@ -716,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
716 | view_populate_pid(view); | 722 | view_populate_pid(view); |
717 | view->container = container_create(view); | 723 | view->container = container_create(view); |
718 | 724 | ||
725 | if (view->ctx == NULL) { | ||
726 | struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid); | ||
727 | if (ctx != NULL) { | ||
728 | view_assign_ctx(view, ctx); | ||
729 | } | ||
730 | } | ||
731 | |||
719 | // If there is a request to be opened fullscreen on a specific output, try | 732 | // If there is a request to be opened fullscreen on a specific output, try |
720 | // to honor that request. Otherwise, fallback to assigns, pid mappings, | 733 | // to honor that request. Otherwise, fallback to assigns, pid mappings, |
721 | // focused workspace, etc | 734 | // focused workspace, etc |
@@ -729,10 +742,36 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
729 | } | 742 | } |
730 | 743 | ||
731 | struct sway_seat *seat = input_manager_current_seat(); | 744 | struct sway_seat *seat = input_manager_current_seat(); |
732 | struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) | 745 | struct sway_node *node = |
733 | : seat_get_focus_inactive(seat, &root->node); | 746 | seat_get_focus_inactive(seat, ws ? &ws->node : &root->node); |
734 | struct sway_container *target_sibling = node->type == N_CONTAINER ? | 747 | struct sway_container *target_sibling = NULL; |
735 | node->sway_container : NULL; | 748 | if (node && node->type == N_CONTAINER) { |
749 | if (container_is_floating(node->sway_container)) { | ||
750 | // If we're about to launch the view into the floating container, then | ||
751 | // launch it as a tiled view instead. | ||
752 | if (ws) { | ||
753 | target_sibling = seat_get_focus_inactive_tiling(seat, ws); | ||
754 | if (target_sibling) { | ||
755 | struct sway_container *con = | ||
756 | seat_get_focus_inactive_view(seat, &target_sibling->node); | ||
757 | if (con) { | ||
758 | target_sibling = con; | ||
759 | } | ||
760 | } | ||
761 | } else { | ||
762 | ws = seat_get_last_known_workspace(seat); | ||
763 | } | ||
764 | } else { | ||
765 | target_sibling = node->sway_container; | ||
766 | } | ||
767 | } | ||
768 | |||
769 | struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = { | ||
770 | .app_id = view_get_app_id(view), | ||
771 | .title = view_get_title(view), | ||
772 | }; | ||
773 | view->ext_foreign_toplevel = | ||
774 | wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state); | ||
736 | 775 | ||
737 | view->foreign_toplevel = | 776 | view->foreign_toplevel = |
738 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); | 777 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); |
@@ -749,13 +788,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
749 | wl_signal_add(&view->foreign_toplevel->events.destroy, | 788 | wl_signal_add(&view->foreign_toplevel->events.destroy, |
750 | &view->foreign_destroy); | 789 | &view->foreign_destroy); |
751 | 790 | ||
752 | // If we're about to launch the view into the floating container, then | ||
753 | // launch it as a tiled view in the root of the workspace instead. | ||
754 | if (target_sibling && container_is_floating(target_sibling)) { | ||
755 | target_sibling = NULL; | ||
756 | ws = seat_get_last_known_workspace(seat); | ||
757 | } | ||
758 | |||
759 | struct sway_container *container = view->container; | 791 | struct sway_container *container = view->container; |
760 | if (target_sibling) { | 792 | if (target_sibling) { |
761 | container_add_sibling(target_sibling, container, 1); | 793 | container_add_sibling(target_sibling, container, 1); |
@@ -764,11 +796,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
764 | } | 796 | } |
765 | ipc_event_window(view->container, "new"); | 797 | ipc_event_window(view->container, "new"); |
766 | 798 | ||
767 | view_init_subsurfaces(view, wlr_surface); | ||
768 | wl_signal_add(&wlr_surface->events.new_subsurface, | ||
769 | &view->surface_new_subsurface); | ||
770 | view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; | ||
771 | |||
772 | if (decoration) { | 799 | if (decoration) { |
773 | view_update_csd_from_client(view, decoration); | 800 | view_update_csd_from_client(view, decoration); |
774 | } | 801 | } |
@@ -812,9 +839,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
812 | bool set_focus = should_focus(view); | 839 | bool set_focus = should_focus(view); |
813 | 840 | ||
814 | #if HAVE_XWAYLAND | 841 | #if HAVE_XWAYLAND |
815 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 842 | struct wlr_xwayland_surface *xsurface; |
816 | struct wlr_xwayland_surface *xsurface = | 843 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
817 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
818 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != | 844 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != |
819 | WLR_ICCCM_INPUT_MODEL_NONE; | 845 | WLR_ICCCM_INPUT_MODEL_NONE; |
820 | } | 846 | } |
@@ -824,6 +850,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
824 | input_manager_set_focus(&view->container->node); | 850 | input_manager_set_focus(&view->container->node); |
825 | } | 851 | } |
826 | 852 | ||
853 | if (view->ext_foreign_toplevel) { | ||
854 | update_ext_foreign_toplevel(view); | ||
855 | } | ||
856 | |||
827 | const char *app_id; | 857 | const char *app_id; |
828 | const char *class; | 858 | const char *class; |
829 | if ((app_id = view_get_app_id(view)) != NULL) { | 859 | if ((app_id = view_get_app_id(view)) != NULL) { |
@@ -834,15 +864,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
834 | } | 864 | } |
835 | 865 | ||
836 | void view_unmap(struct sway_view *view) { | 866 | void view_unmap(struct sway_view *view) { |
837 | wl_signal_emit(&view->events.unmap, view); | 867 | wl_signal_emit_mutable(&view->events.unmap, view); |
838 | 868 | ||
839 | wl_list_remove(&view->surface_new_subsurface.link); | 869 | view->executed_criteria->length = 0; |
840 | 870 | ||
841 | if (view->urgent_timer) { | 871 | if (view->urgent_timer) { |
842 | wl_event_source_remove(view->urgent_timer); | 872 | wl_event_source_remove(view->urgent_timer); |
843 | view->urgent_timer = NULL; | 873 | view->urgent_timer = NULL; |
844 | } | 874 | } |
845 | 875 | ||
876 | if (view->ext_foreign_toplevel) { | ||
877 | wlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel); | ||
878 | view->ext_foreign_toplevel = NULL; | ||
879 | } | ||
880 | |||
846 | if (view->foreign_toplevel) { | 881 | if (view->foreign_toplevel) { |
847 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); | 882 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); |
848 | view->foreign_toplevel = NULL; | 883 | view->foreign_toplevel = NULL; |
@@ -889,293 +924,47 @@ void view_update_size(struct sway_view *view) { | |||
889 | container_set_geometry_from_content(con); | 924 | container_set_geometry_from_content(con); |
890 | } | 925 | } |
891 | 926 | ||
892 | void view_center_surface(struct sway_view *view) { | 927 | void view_center_and_clip_surface(struct sway_view *view) { |
893 | struct sway_container *con = view->container; | 928 | struct sway_container *con = view->container; |
894 | // We always center the current coordinates rather than the next, as the | ||
895 | // geometry immediately affects the currently active rendering. | ||
896 | con->surface_x = fmax(con->current.content_x, con->current.content_x + | ||
897 | (con->current.content_width - view->geometry.width) / 2); | ||
898 | con->surface_y = fmax(con->current.content_y, con->current.content_y + | ||
899 | (con->current.content_height - view->geometry.height) / 2); | ||
900 | } | ||
901 | |||
902 | static const struct sway_view_child_impl subsurface_impl; | ||
903 | 929 | ||
904 | static void subsurface_get_root_coords(struct sway_view_child *child, | 930 | if (container_is_floating(con)) { |
905 | int *root_sx, int *root_sy) { | 931 | // We always center the current coordinates rather than the next, as the |
906 | struct wlr_surface *surface = child->surface; | 932 | // geometry immediately affects the currently active rendering. |
907 | *root_sx = -child->view->geometry.x; | 933 | int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); |
908 | *root_sy = -child->view->geometry.y; | 934 | int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); |
909 | 935 | ||
910 | if (child->parent && child->parent->impl && | 936 | wlr_scene_node_set_position(&view->content_tree->node, x, y); |
911 | child->parent->impl->get_root_coords) { | ||
912 | int sx, sy; | ||
913 | child->parent->impl->get_root_coords(child->parent, &sx, &sy); | ||
914 | *root_sx += sx; | ||
915 | *root_sy += sy; | ||
916 | } else { | 937 | } else { |
917 | while (surface && wlr_surface_is_subsurface(surface)) { | 938 | wlr_scene_node_set_position(&view->content_tree->node, 0, 0); |
918 | struct wlr_subsurface *subsurface = | ||
919 | wlr_subsurface_from_wlr_surface(surface); | ||
920 | if (subsurface == NULL) { | ||
921 | break; | ||
922 | } | ||
923 | *root_sx += subsurface->current.x; | ||
924 | *root_sy += subsurface->current.y; | ||
925 | surface = subsurface->parent; | ||
926 | } | ||
927 | } | 939 | } |
928 | } | ||
929 | 940 | ||
930 | static void subsurface_destroy(struct sway_view_child *child) { | 941 | // only make sure to clip the content if there is content to clip |
931 | if (!sway_assert(child->impl == &subsurface_impl, | 942 | if (!wl_list_empty(&con->view->content_tree->children)) { |
932 | "Expected a subsurface")) { | 943 | wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){ |
933 | return; | 944 | .x = con->view->geometry.x, |
934 | } | 945 | .y = con->view->geometry.y, |
935 | struct sway_subsurface *subsurface = (struct sway_subsurface *)child; | 946 | .width = con->current.content_width, |
936 | wl_list_remove(&subsurface->destroy.link); | 947 | .height = con->current.content_height, |
937 | free(subsurface); | 948 | }); |
938 | } | ||
939 | |||
940 | static const struct sway_view_child_impl subsurface_impl = { | ||
941 | .get_root_coords = subsurface_get_root_coords, | ||
942 | .destroy = subsurface_destroy, | ||
943 | }; | ||
944 | |||
945 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
946 | void *data) { | ||
947 | struct sway_subsurface *subsurface = | ||
948 | wl_container_of(listener, subsurface, destroy); | ||
949 | struct sway_view_child *child = &subsurface->child; | ||
950 | view_child_destroy(child); | ||
951 | } | ||
952 | |||
953 | static void view_child_damage(struct sway_view_child *child, bool whole); | ||
954 | |||
955 | static void view_subsurface_create(struct sway_view *view, | ||
956 | struct wlr_subsurface *wlr_subsurface) { | ||
957 | struct sway_subsurface *subsurface = | ||
958 | calloc(1, sizeof(struct sway_subsurface)); | ||
959 | if (subsurface == NULL) { | ||
960 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
961 | return; | ||
962 | } | ||
963 | view_child_init(&subsurface->child, &subsurface_impl, view, | ||
964 | wlr_subsurface->surface); | ||
965 | |||
966 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
967 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
968 | |||
969 | subsurface->child.mapped = true; | ||
970 | |||
971 | view_child_damage(&subsurface->child, true); | ||
972 | } | ||
973 | |||
974 | static void view_child_subsurface_create(struct sway_view_child *child, | ||
975 | struct wlr_subsurface *wlr_subsurface) { | ||
976 | struct sway_subsurface *subsurface = | ||
977 | calloc(1, sizeof(struct sway_subsurface)); | ||
978 | if (subsurface == NULL) { | ||
979 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
980 | return; | ||
981 | } | ||
982 | subsurface->child.parent = child; | ||
983 | wl_list_insert(&child->children, &subsurface->child.link); | ||
984 | view_child_init(&subsurface->child, &subsurface_impl, child->view, | ||
985 | wlr_subsurface->surface); | ||
986 | |||
987 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
988 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
989 | |||
990 | subsurface->child.mapped = true; | ||
991 | |||
992 | view_child_damage(&subsurface->child, true); | ||
993 | } | ||
994 | |||
995 | static bool view_child_is_mapped(struct sway_view_child *child) { | ||
996 | while (child) { | ||
997 | if (!child->mapped) { | ||
998 | return false; | ||
999 | } | ||
1000 | child = child->parent; | ||
1001 | } | ||
1002 | return true; | ||
1003 | } | ||
1004 | |||
1005 | static void view_child_damage(struct sway_view_child *child, bool whole) { | ||
1006 | if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) { | ||
1007 | return; | ||
1008 | } | ||
1009 | int sx, sy; | ||
1010 | child->impl->get_root_coords(child, &sx, &sy); | ||
1011 | desktop_damage_surface(child->surface, | ||
1012 | child->view->container->pending.content_x + sx, | ||
1013 | child->view->container->pending.content_y + sy, whole); | ||
1014 | } | ||
1015 | |||
1016 | static void view_child_handle_surface_commit(struct wl_listener *listener, | ||
1017 | void *data) { | ||
1018 | struct sway_view_child *child = | ||
1019 | wl_container_of(listener, child, surface_commit); | ||
1020 | view_child_damage(child, false); | ||
1021 | } | ||
1022 | |||
1023 | static void view_child_handle_surface_new_subsurface( | ||
1024 | struct wl_listener *listener, void *data) { | ||
1025 | struct sway_view_child *child = | ||
1026 | wl_container_of(listener, child, surface_new_subsurface); | ||
1027 | struct wlr_subsurface *subsurface = data; | ||
1028 | view_child_subsurface_create(child, subsurface); | ||
1029 | } | ||
1030 | |||
1031 | static void view_child_handle_surface_destroy(struct wl_listener *listener, | ||
1032 | void *data) { | ||
1033 | struct sway_view_child *child = | ||
1034 | wl_container_of(listener, child, surface_destroy); | ||
1035 | view_child_destroy(child); | ||
1036 | } | ||
1037 | |||
1038 | static void view_init_subsurfaces(struct sway_view *view, | ||
1039 | struct wlr_surface *surface) { | ||
1040 | struct wlr_subsurface *subsurface; | ||
1041 | wl_list_for_each(subsurface, &surface->subsurfaces_below, parent_link) { | ||
1042 | view_subsurface_create(view, subsurface); | ||
1043 | } | ||
1044 | wl_list_for_each(subsurface, &surface->subsurfaces_above, parent_link) { | ||
1045 | view_subsurface_create(view, subsurface); | ||
1046 | } | ||
1047 | } | ||
1048 | |||
1049 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
1050 | struct wlr_surface *surface) { | ||
1051 | struct wlr_subsurface *subsurface; | ||
1052 | wl_list_for_each(subsurface, &surface->subsurfaces_below, parent_link) { | ||
1053 | view_child_subsurface_create(view_child, subsurface); | ||
1054 | } | ||
1055 | wl_list_for_each(subsurface, &surface->subsurfaces_above, parent_link) { | ||
1056 | view_child_subsurface_create(view_child, subsurface); | ||
1057 | } | ||
1058 | } | ||
1059 | |||
1060 | static void view_child_handle_surface_map(struct wl_listener *listener, | ||
1061 | void *data) { | ||
1062 | struct sway_view_child *child = | ||
1063 | wl_container_of(listener, child, surface_map); | ||
1064 | child->mapped = true; | ||
1065 | view_child_damage(child, true); | ||
1066 | } | ||
1067 | |||
1068 | static void view_child_handle_surface_unmap(struct wl_listener *listener, | ||
1069 | void *data) { | ||
1070 | struct sway_view_child *child = | ||
1071 | wl_container_of(listener, child, surface_unmap); | ||
1072 | view_child_damage(child, true); | ||
1073 | child->mapped = false; | ||
1074 | } | ||
1075 | |||
1076 | static void view_child_handle_view_unmap(struct wl_listener *listener, | ||
1077 | void *data) { | ||
1078 | struct sway_view_child *child = | ||
1079 | wl_container_of(listener, child, view_unmap); | ||
1080 | view_child_damage(child, true); | ||
1081 | child->mapped = false; | ||
1082 | } | ||
1083 | |||
1084 | void view_child_init(struct sway_view_child *child, | ||
1085 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
1086 | struct wlr_surface *surface) { | ||
1087 | child->impl = impl; | ||
1088 | child->view = view; | ||
1089 | child->surface = surface; | ||
1090 | wl_list_init(&child->children); | ||
1091 | |||
1092 | wl_signal_add(&surface->events.commit, &child->surface_commit); | ||
1093 | child->surface_commit.notify = view_child_handle_surface_commit; | ||
1094 | wl_signal_add(&surface->events.new_subsurface, | ||
1095 | &child->surface_new_subsurface); | ||
1096 | child->surface_new_subsurface.notify = | ||
1097 | view_child_handle_surface_new_subsurface; | ||
1098 | wl_signal_add(&surface->events.destroy, &child->surface_destroy); | ||
1099 | child->surface_destroy.notify = view_child_handle_surface_destroy; | ||
1100 | |||
1101 | // Not all child views have a map/unmap event | ||
1102 | child->surface_map.notify = view_child_handle_surface_map; | ||
1103 | wl_list_init(&child->surface_map.link); | ||
1104 | child->surface_unmap.notify = view_child_handle_surface_unmap; | ||
1105 | wl_list_init(&child->surface_unmap.link); | ||
1106 | |||
1107 | wl_signal_add(&view->events.unmap, &child->view_unmap); | ||
1108 | child->view_unmap.notify = view_child_handle_view_unmap; | ||
1109 | |||
1110 | struct sway_workspace *workspace = child->view->container->pending.workspace; | ||
1111 | if (workspace) { | ||
1112 | wlr_surface_send_enter(child->surface, workspace->output->wlr_output); | ||
1113 | } | ||
1114 | |||
1115 | view_child_init_subsurfaces(child, surface); | ||
1116 | } | ||
1117 | |||
1118 | void view_child_destroy(struct sway_view_child *child) { | ||
1119 | if (view_child_is_mapped(child) && child->view->container != NULL) { | ||
1120 | view_child_damage(child, true); | ||
1121 | } | ||
1122 | |||
1123 | if (child->parent != NULL) { | ||
1124 | wl_list_remove(&child->link); | ||
1125 | child->parent = NULL; | ||
1126 | } | ||
1127 | |||
1128 | struct sway_view_child *subchild, *tmpchild; | ||
1129 | wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { | ||
1130 | wl_list_remove(&subchild->link); | ||
1131 | subchild->parent = NULL; | ||
1132 | // The subchild lost its parent link, so it cannot see that the parent | ||
1133 | // is unmapped. Unmap it directly. | ||
1134 | subchild->mapped = false; | ||
1135 | } | ||
1136 | |||
1137 | wl_list_remove(&child->surface_commit.link); | ||
1138 | wl_list_remove(&child->surface_destroy.link); | ||
1139 | wl_list_remove(&child->surface_map.link); | ||
1140 | wl_list_remove(&child->surface_unmap.link); | ||
1141 | wl_list_remove(&child->view_unmap.link); | ||
1142 | wl_list_remove(&child->surface_new_subsurface.link); | ||
1143 | |||
1144 | if (child->impl && child->impl->destroy) { | ||
1145 | child->impl->destroy(child); | ||
1146 | } else { | ||
1147 | free(child); | ||
1148 | } | 949 | } |
1149 | } | 950 | } |
1150 | 951 | ||
1151 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | 952 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { |
1152 | if (wlr_surface_is_xdg_surface(wlr_surface)) { | 953 | struct wlr_xdg_surface *xdg_surface; |
1153 | struct wlr_xdg_surface *xdg_surface = | 954 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { |
1154 | wlr_xdg_surface_from_wlr_surface(wlr_surface); | ||
1155 | if (xdg_surface == NULL) { | ||
1156 | return NULL; | ||
1157 | } | ||
1158 | return view_from_wlr_xdg_surface(xdg_surface); | 955 | return view_from_wlr_xdg_surface(xdg_surface); |
1159 | } | 956 | } |
1160 | #if HAVE_XWAYLAND | 957 | #if HAVE_XWAYLAND |
1161 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 958 | struct wlr_xwayland_surface *xsurface; |
1162 | struct wlr_xwayland_surface *xsurface = | 959 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
1163 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
1164 | if (xsurface == NULL) { | ||
1165 | return NULL; | ||
1166 | } | ||
1167 | return view_from_wlr_xwayland_surface(xsurface); | 960 | return view_from_wlr_xwayland_surface(xsurface); |
1168 | } | 961 | } |
1169 | #endif | 962 | #endif |
1170 | if (wlr_surface_is_subsurface(wlr_surface)) { | 963 | struct wlr_subsurface *subsurface; |
1171 | struct wlr_subsurface *subsurface = | 964 | if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) { |
1172 | wlr_subsurface_from_wlr_surface(wlr_surface); | ||
1173 | if (subsurface == NULL) { | ||
1174 | return NULL; | ||
1175 | } | ||
1176 | return view_from_wlr_surface(subsurface->parent); | 965 | return view_from_wlr_surface(subsurface->parent); |
1177 | } | 966 | } |
1178 | if (wlr_surface_is_layer_surface(wlr_surface)) { | 967 | if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { |
1179 | return NULL; | 968 | return NULL; |
1180 | } | 969 | } |
1181 | 970 | ||
@@ -1256,6 +1045,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { | |||
1256 | return len; | 1045 | return len; |
1257 | } | 1046 | } |
1258 | 1047 | ||
1048 | void view_update_app_id(struct sway_view *view) { | ||
1049 | const char *app_id = view_get_app_id(view); | ||
1050 | |||
1051 | if (view->foreign_toplevel && app_id) { | ||
1052 | wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id); | ||
1053 | } | ||
1054 | |||
1055 | if (view->ext_foreign_toplevel) { | ||
1056 | update_ext_foreign_toplevel(view); | ||
1057 | } | ||
1058 | } | ||
1059 | |||
1259 | void view_update_title(struct sway_view *view, bool force) { | 1060 | void view_update_title(struct sway_view *view, bool force) { |
1260 | const char *title = view_get_title(view); | 1061 | const char *title = view_get_title(view); |
1261 | 1062 | ||
@@ -1271,31 +1072,41 @@ void view_update_title(struct sway_view *view, bool force) { | |||
1271 | 1072 | ||
1272 | free(view->container->title); | 1073 | free(view->container->title); |
1273 | free(view->container->formatted_title); | 1074 | free(view->container->formatted_title); |
1274 | if (title) { | 1075 | |
1275 | size_t len = parse_title_format(view, NULL); | 1076 | size_t len = parse_title_format(view, NULL); |
1077 | |||
1078 | if (len) { | ||
1276 | char *buffer = calloc(len + 1, sizeof(char)); | 1079 | char *buffer = calloc(len + 1, sizeof(char)); |
1277 | if (!sway_assert(buffer, "Unable to allocate title string")) { | 1080 | if (!sway_assert(buffer, "Unable to allocate title string")) { |
1278 | return; | 1081 | return; |
1279 | } | 1082 | } |
1280 | parse_title_format(view, buffer); | ||
1281 | 1083 | ||
1282 | view->container->title = strdup(title); | 1084 | parse_title_format(view, buffer); |
1283 | view->container->formatted_title = buffer; | 1085 | view->container->formatted_title = buffer; |
1284 | } else { | 1086 | } else { |
1285 | view->container->title = NULL; | ||
1286 | view->container->formatted_title = NULL; | 1087 | view->container->formatted_title = NULL; |
1287 | } | 1088 | } |
1288 | container_calculate_title_height(view->container); | 1089 | |
1289 | config_update_font_height(false); | 1090 | view->container->title = title ? strdup(title) : NULL; |
1290 | 1091 | ||
1291 | // Update title after the global font height is updated | 1092 | // Update title after the global font height is updated |
1292 | container_update_title_textures(view->container); | 1093 | if (view->container->title_bar.title_text && len) { |
1094 | sway_text_node_set_text(view->container->title_bar.title_text, | ||
1095 | view->container->formatted_title); | ||
1096 | container_arrange_title_bar(view->container); | ||
1097 | } else { | ||
1098 | container_update_title_bar(view->container); | ||
1099 | } | ||
1293 | 1100 | ||
1294 | ipc_event_window(view->container, "title"); | 1101 | ipc_event_window(view->container, "title"); |
1295 | 1102 | ||
1296 | if (view->foreign_toplevel && title) { | 1103 | if (view->foreign_toplevel && title) { |
1297 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); | 1104 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); |
1298 | } | 1105 | } |
1106 | |||
1107 | if (view->ext_foreign_toplevel) { | ||
1108 | update_ext_foreign_toplevel(view); | ||
1109 | } | ||
1299 | } | 1110 | } |
1300 | 1111 | ||
1301 | bool view_is_visible(struct sway_view *view) { | 1112 | bool view_is_visible(struct sway_view *view) { |
@@ -1356,6 +1167,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1356 | return; | 1167 | return; |
1357 | } | 1168 | } |
1358 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | 1169 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); |
1170 | container_update_itself_and_parents(view->container); | ||
1359 | } else { | 1171 | } else { |
1360 | view->urgent = (struct timespec){ 0 }; | 1172 | view->urgent = (struct timespec){ 0 }; |
1361 | if (view->urgent_timer) { | 1173 | if (view->urgent_timer) { |
@@ -1363,7 +1175,6 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1363 | view->urgent_timer = NULL; | 1175 | view->urgent_timer = NULL; |
1364 | } | 1176 | } |
1365 | } | 1177 | } |
1366 | container_damage_whole(view->container); | ||
1367 | 1178 | ||
1368 | ipc_event_window(view->container, "urgent"); | 1179 | ipc_event_window(view->container, "urgent"); |
1369 | 1180 | ||
@@ -1377,40 +1188,54 @@ bool view_is_urgent(struct sway_view *view) { | |||
1377 | } | 1188 | } |
1378 | 1189 | ||
1379 | void view_remove_saved_buffer(struct sway_view *view) { | 1190 | void view_remove_saved_buffer(struct sway_view *view) { |
1380 | if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { | 1191 | if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) { |
1381 | return; | 1192 | return; |
1382 | } | 1193 | } |
1383 | struct sway_saved_buffer *saved_buf, *tmp; | 1194 | |
1384 | wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { | 1195 | wlr_scene_node_destroy(&view->saved_surface_tree->node); |
1385 | wlr_buffer_unlock(&saved_buf->buffer->base); | 1196 | view->saved_surface_tree = NULL; |
1386 | wl_list_remove(&saved_buf->link); | 1197 | wlr_scene_node_set_enabled(&view->content_tree->node, true); |
1387 | free(saved_buf); | ||
1388 | } | ||
1389 | } | 1198 | } |
1390 | 1199 | ||
1391 | static void view_save_buffer_iterator(struct wlr_surface *surface, | 1200 | static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, |
1392 | int sx, int sy, void *data) { | 1201 | int sx, int sy, void *data) { |
1393 | struct sway_view *view = data; | 1202 | struct wlr_scene_tree *tree = data; |
1394 | 1203 | ||
1395 | if (surface && wlr_surface_has_buffer(surface)) { | 1204 | struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); |
1396 | wlr_buffer_lock(&surface->buffer->base); | 1205 | if (!sbuf) { |
1397 | struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); | 1206 | sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface"); |
1398 | saved_buffer->buffer = surface->buffer; | 1207 | return; |
1399 | saved_buffer->width = surface->current.width; | ||
1400 | saved_buffer->height = surface->current.height; | ||
1401 | saved_buffer->x = view->container->surface_x + sx; | ||
1402 | saved_buffer->y = view->container->surface_y + sy; | ||
1403 | saved_buffer->transform = surface->current.transform; | ||
1404 | wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); | ||
1405 | wl_list_insert(&view->saved_buffers, &saved_buffer->link); | ||
1406 | } | 1208 | } |
1209 | |||
1210 | wlr_scene_buffer_set_dest_size(sbuf, | ||
1211 | buffer->dst_width, buffer->dst_height); | ||
1212 | wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region); | ||
1213 | wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box); | ||
1214 | wlr_scene_node_set_position(&sbuf->node, sx, sy); | ||
1215 | wlr_scene_buffer_set_transform(sbuf, buffer->transform); | ||
1216 | wlr_scene_buffer_set_buffer(sbuf, buffer->buffer); | ||
1407 | } | 1217 | } |
1408 | 1218 | ||
1409 | void view_save_buffer(struct sway_view *view) { | 1219 | void view_save_buffer(struct sway_view *view) { |
1410 | if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { | 1220 | if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) { |
1411 | view_remove_saved_buffer(view); | 1221 | view_remove_saved_buffer(view); |
1412 | } | 1222 | } |
1413 | view_for_each_surface(view, view_save_buffer_iterator, view); | 1223 | |
1224 | view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree); | ||
1225 | if (!view->saved_surface_tree) { | ||
1226 | sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface"); | ||
1227 | return; | ||
1228 | } | ||
1229 | |||
1230 | // Enable and disable the saved surface tree like so to atomitaclly update | ||
1231 | // the tree. This will prevent over damaging or other weirdness. | ||
1232 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false); | ||
1233 | |||
1234 | wlr_scene_node_for_each_buffer(&view->content_tree->node, | ||
1235 | view_save_buffer_iterator, view->saved_surface_tree); | ||
1236 | |||
1237 | wlr_scene_node_set_enabled(&view->content_tree->node, false); | ||
1238 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true); | ||
1414 | } | 1239 | } |
1415 | 1240 | ||
1416 | bool view_is_transient_for(struct sway_view *child, | 1241 | bool view_is_transient_for(struct sway_view *child, |
@@ -1418,3 +1243,19 @@ bool view_is_transient_for(struct sway_view *child, | |||
1418 | return child->impl->is_transient_for && | 1243 | return child->impl->is_transient_for && |
1419 | child->impl->is_transient_for(child, ancestor); | 1244 | child->impl->is_transient_for(child, ancestor); |
1420 | } | 1245 | } |
1246 | |||
1247 | static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, | ||
1248 | int x, int y, void *data) { | ||
1249 | struct timespec *when = data; | ||
1250 | wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); | ||
1251 | } | ||
1252 | |||
1253 | void view_send_frame_done(struct sway_view *view) { | ||
1254 | struct timespec when; | ||
1255 | clock_gettime(CLOCK_MONOTONIC, &when); | ||
1256 | |||
1257 | struct wlr_scene_node *node; | ||
1258 | wl_list_for_each(node, &view->content_tree->children, link) { | ||
1259 | wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when); | ||
1260 | } | ||
1261 | } | ||
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 8dd7789d..a68dc927 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809 | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <limits.h> | 2 | #include <limits.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
@@ -50,12 +49,14 @@ struct sway_output *workspace_get_initial_output(const char *name) { | |||
50 | } else if (focus && focus->type == N_CONTAINER) { | 49 | } else if (focus && focus->type == N_CONTAINER) { |
51 | return focus->sway_container->pending.workspace->output; | 50 | return focus->sway_container->pending.workspace->output; |
52 | } | 51 | } |
53 | // Fallback to the first output or noop output for headless | 52 | // Fallback to the first output or the headless output |
54 | return root->outputs->length ? root->outputs->items[0] : root->noop_output; | 53 | return root->outputs->length ? root->outputs->items[0] : root->fallback_output; |
55 | } | 54 | } |
56 | 55 | ||
57 | struct sway_workspace *workspace_create(struct sway_output *output, | 56 | struct sway_workspace *workspace_create(struct sway_output *output, |
58 | const char *name) { | 57 | const char *name) { |
58 | sway_assert(name, "NULL name given to workspace_create"); | ||
59 | |||
59 | if (output == NULL) { | 60 | if (output == NULL) { |
60 | output = workspace_get_initial_output(name); | 61 | output = workspace_get_initial_output(name); |
61 | } | 62 | } |
@@ -69,7 +70,19 @@ struct sway_workspace *workspace_create(struct sway_output *output, | |||
69 | return NULL; | 70 | return NULL; |
70 | } | 71 | } |
71 | node_init(&ws->node, N_WORKSPACE, ws); | 72 | node_init(&ws->node, N_WORKSPACE, ws); |
72 | ws->name = name ? strdup(name) : NULL; | 73 | |
74 | bool failed = false; | ||
75 | ws->layers.tiling = alloc_scene_tree(root->staging, &failed); | ||
76 | ws->layers.fullscreen = alloc_scene_tree(root->staging, &failed); | ||
77 | |||
78 | if (failed) { | ||
79 | wlr_scene_node_destroy(&ws->layers.tiling->node); | ||
80 | wlr_scene_node_destroy(&ws->layers.fullscreen->node); | ||
81 | free(ws); | ||
82 | return NULL; | ||
83 | } | ||
84 | |||
85 | ws->name = strdup(name); | ||
73 | ws->prev_split_layout = L_NONE; | 86 | ws->prev_split_layout = L_NONE; |
74 | ws->layout = output_get_default_layout(output); | 87 | ws->layout = output_get_default_layout(output); |
75 | ws->floating = create_list(); | 88 | ws->floating = create_list(); |
@@ -114,7 +127,7 @@ struct sway_workspace *workspace_create(struct sway_output *output, | |||
114 | output_sort_workspaces(output); | 127 | output_sort_workspaces(output); |
115 | 128 | ||
116 | ipc_event_workspace(NULL, ws, "init"); | 129 | ipc_event_workspace(NULL, ws, "init"); |
117 | wl_signal_emit(&root->events.new_node, &ws->node); | 130 | wl_signal_emit_mutable(&root->events.new_node, &ws->node); |
118 | 131 | ||
119 | return ws; | 132 | return ws; |
120 | } | 133 | } |
@@ -129,6 +142,11 @@ void workspace_destroy(struct sway_workspace *workspace) { | |||
129 | return; | 142 | return; |
130 | } | 143 | } |
131 | 144 | ||
145 | scene_node_disown_children(workspace->layers.tiling); | ||
146 | scene_node_disown_children(workspace->layers.fullscreen); | ||
147 | wlr_scene_node_destroy(&workspace->layers.tiling->node); | ||
148 | wlr_scene_node_destroy(&workspace->layers.fullscreen->node); | ||
149 | |||
132 | free(workspace->name); | 150 | free(workspace->name); |
133 | free(workspace->representation); | 151 | free(workspace->representation); |
134 | list_free_items_and_destroy(workspace->output_priority); | 152 | list_free_items_and_destroy(workspace->output_priority); |
@@ -142,7 +160,7 @@ void workspace_destroy(struct sway_workspace *workspace) { | |||
142 | void workspace_begin_destroy(struct sway_workspace *workspace) { | 160 | void workspace_begin_destroy(struct sway_workspace *workspace) { |
143 | sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); | 161 | sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); |
144 | ipc_event_workspace(NULL, workspace, "empty"); // intentional | 162 | ipc_event_workspace(NULL, workspace, "empty"); // intentional |
145 | wl_signal_emit(&workspace->node.events.destroy, &workspace->node); | 163 | wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); |
146 | 164 | ||
147 | if (workspace->output) { | 165 | if (workspace->output) { |
148 | workspace_detach(workspace); | 166 | workspace_detach(workspace); |
@@ -174,22 +192,16 @@ void workspace_consider_destroy(struct sway_workspace *ws) { | |||
174 | static bool workspace_valid_on_output(const char *output_name, | 192 | static bool workspace_valid_on_output(const char *output_name, |
175 | const char *ws_name) { | 193 | const char *ws_name) { |
176 | struct workspace_config *wsc = workspace_find_config(ws_name); | 194 | struct workspace_config *wsc = workspace_find_config(ws_name); |
177 | char identifier[128]; | ||
178 | struct sway_output *output = output_by_name_or_id(output_name); | 195 | struct sway_output *output = output_by_name_or_id(output_name); |
179 | if (!output) { | 196 | if (!output) { |
180 | return false; | 197 | return false; |
181 | } | 198 | } |
182 | output_name = output->wlr_output->name; | ||
183 | output_get_identifier(identifier, sizeof(identifier), output); | ||
184 | |||
185 | if (!wsc) { | 199 | if (!wsc) { |
186 | return true; | 200 | return true; |
187 | } | 201 | } |
188 | 202 | ||
189 | for (int i = 0; i < wsc->outputs->length; i++) { | 203 | for (int i = 0; i < wsc->outputs->length; i++) { |
190 | if (strcmp(wsc->outputs->items[i], "*") == 0 || | 204 | if (output_match_name_or_id(output, wsc->outputs->items[i])) { |
191 | strcmp(wsc->outputs->items[i], output_name) == 0 || | ||
192 | strcmp(wsc->outputs->items[i], identifier) == 0) { | ||
193 | return true; | 205 | return true; |
194 | } | 206 | } |
195 | } | 207 | } |
@@ -284,13 +296,10 @@ char *workspace_next_name(const char *output_name) { | |||
284 | // assignments primarily, falling back to bindings and numbers. | 296 | // assignments primarily, falling back to bindings and numbers. |
285 | struct sway_mode *mode = config->current_mode; | 297 | struct sway_mode *mode = config->current_mode; |
286 | 298 | ||
287 | char identifier[128]; | ||
288 | struct sway_output *output = output_by_name_or_id(output_name); | 299 | struct sway_output *output = output_by_name_or_id(output_name); |
289 | if (!output) { | 300 | if (!output) { |
290 | return NULL; | 301 | return NULL; |
291 | } | 302 | } |
292 | output_name = output->wlr_output->name; | ||
293 | output_get_identifier(identifier, sizeof(identifier), output); | ||
294 | 303 | ||
295 | int order = INT_MAX; | 304 | int order = INT_MAX; |
296 | char *target = NULL; | 305 | char *target = NULL; |
@@ -310,9 +319,7 @@ char *workspace_next_name(const char *output_name) { | |||
310 | } | 319 | } |
311 | bool found = false; | 320 | bool found = false; |
312 | for (int j = 0; j < wsc->outputs->length; ++j) { | 321 | for (int j = 0; j < wsc->outputs->length; ++j) { |
313 | if (strcmp(wsc->outputs->items[j], "*") == 0 || | 322 | if (output_match_name_or_id(output, wsc->outputs->items[j])) { |
314 | strcmp(wsc->outputs->items[j], output_name) == 0 || | ||
315 | strcmp(wsc->outputs->items[j], identifier) == 0) { | ||
316 | found = true; | 323 | found = true; |
317 | free(target); | 324 | free(target); |
318 | target = strdup(wsc->workspace); | 325 | target = strdup(wsc->workspace); |
@@ -652,15 +659,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace, | |||
652 | 659 | ||
653 | struct sway_output *workspace_output_get_highest_available( | 660 | struct sway_output *workspace_output_get_highest_available( |
654 | struct sway_workspace *ws, struct sway_output *exclude) { | 661 | struct sway_workspace *ws, struct sway_output *exclude) { |
655 | char exclude_id[128] = {'\0'}; | ||
656 | if (exclude) { | ||
657 | output_get_identifier(exclude_id, sizeof(exclude_id), exclude); | ||
658 | } | ||
659 | |||
660 | for (int i = 0; i < ws->output_priority->length; i++) { | 662 | for (int i = 0; i < ws->output_priority->length; i++) { |
661 | char *name = ws->output_priority->items[i]; | 663 | const char *name = ws->output_priority->items[i]; |
662 | if (exclude && (strcmp(name, exclude->wlr_output->name) == 0 | 664 | if (exclude && output_match_name_or_id(exclude, name)) { |
663 | || strcmp(name, exclude_id) == 0)) { | ||
664 | continue; | 665 | continue; |
665 | } | 666 | } |
666 | 667 | ||
@@ -684,7 +685,6 @@ void workspace_detect_urgent(struct sway_workspace *workspace) { | |||
684 | if (workspace->urgent != new_urgent) { | 685 | if (workspace->urgent != new_urgent) { |
685 | workspace->urgent = new_urgent; | 686 | workspace->urgent = new_urgent; |
686 | ipc_event_workspace(NULL, workspace, "urgent"); | 687 | ipc_event_workspace(NULL, workspace, "urgent"); |
687 | output_damage_whole(workspace->output); | ||
688 | } | 688 | } |
689 | } | 689 | } |
690 | 690 | ||
@@ -844,24 +844,36 @@ struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, | |||
844 | return con; | 844 | return con; |
845 | } | 845 | } |
846 | 846 | ||
847 | bool workspace_has_single_visible_container(struct sway_workspace *ws) { | ||
848 | struct sway_seat *seat = input_manager_get_default_seat(); | ||
849 | struct sway_container *focus = | ||
850 | seat_get_focus_inactive_tiling(seat, ws); | ||
851 | if (focus && !focus->view) { | ||
852 | focus = seat_get_focus_inactive_view(seat, &focus->node); | ||
853 | } | ||
854 | return (focus && focus->view && view_ancestor_is_only_visible(focus->view)); | ||
855 | } | ||
856 | |||
847 | void workspace_add_gaps(struct sway_workspace *ws) { | 857 | void workspace_add_gaps(struct sway_workspace *ws) { |
848 | if (config->smart_gaps) { | 858 | if (config->smart_gaps == SMART_GAPS_ON |
849 | struct sway_seat *seat = input_manager_get_default_seat(); | 859 | && workspace_has_single_visible_container(ws)) { |
850 | struct sway_container *focus = | 860 | ws->current_gaps.top = 0; |
851 | seat_get_focus_inactive_tiling(seat, ws); | 861 | ws->current_gaps.right = 0; |
852 | if (focus && !focus->view) { | 862 | ws->current_gaps.bottom = 0; |
853 | focus = seat_get_focus_inactive_view(seat, &focus->node); | 863 | ws->current_gaps.left = 0; |
854 | } | 864 | return; |
855 | if (focus && focus->view && view_ancestor_is_only_visible(focus->view)) { | 865 | } |
856 | ws->current_gaps.top = 0; | 866 | |
857 | ws->current_gaps.right = 0; | 867 | if (config->smart_gaps == SMART_GAPS_INVERSE_OUTER |
858 | ws->current_gaps.bottom = 0; | 868 | && !workspace_has_single_visible_container(ws)) { |
859 | ws->current_gaps.left = 0; | 869 | ws->current_gaps.top = 0; |
860 | return; | 870 | ws->current_gaps.right = 0; |
861 | } | 871 | ws->current_gaps.bottom = 0; |
872 | ws->current_gaps.left = 0; | ||
873 | } else { | ||
874 | ws->current_gaps = ws->gaps_outer; | ||
862 | } | 875 | } |
863 | 876 | ||
864 | ws->current_gaps = ws->gaps_outer; | ||
865 | // Add inner gaps and make sure we don't turn out negative | 877 | // Add inner gaps and make sure we don't turn out negative |
866 | ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); | 878 | ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); |
867 | ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); | 879 | ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); |
diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c index 6c70c785..b7c80dd4 100644 --- a/sway/xdg_activation_v1.c +++ b/sway/xdg_activation_v1.c | |||
@@ -1,20 +1,65 @@ | |||
1 | #include <wlr/types/wlr_xdg_activation_v1.h> | 1 | #include <wlr/types/wlr_xdg_activation_v1.h> |
2 | #include <wlr/types/wlr_xdg_shell.h> | ||
3 | #include "sway/desktop/launcher.h" | ||
2 | #include "sway/tree/view.h" | 4 | #include "sway/tree/view.h" |
5 | #include "sway/tree/workspace.h" | ||
3 | 6 | ||
4 | void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, | 7 | void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, |
5 | void *data) { | 8 | void *data) { |
6 | const struct wlr_xdg_activation_v1_request_activate_event *event = data; | 9 | const struct wlr_xdg_activation_v1_request_activate_event *event = data; |
7 | 10 | ||
8 | if (!wlr_surface_is_xdg_surface(event->surface)) { | 11 | struct wlr_xdg_surface *xdg_surface = |
12 | wlr_xdg_surface_try_from_wlr_surface(event->surface); | ||
13 | if (xdg_surface == NULL) { | ||
9 | return; | 14 | return; |
10 | } | 15 | } |
11 | |||
12 | struct wlr_xdg_surface *xdg_surface = | ||
13 | wlr_xdg_surface_from_wlr_surface(event->surface); | ||
14 | struct sway_view *view = xdg_surface->data; | 16 | struct sway_view *view = xdg_surface->data; |
15 | if (!xdg_surface->mapped || view == NULL) { | 17 | if (view == NULL) { |
18 | return; | ||
19 | } | ||
20 | |||
21 | struct launcher_ctx *ctx = event->token->data; | ||
22 | if (ctx == NULL) { | ||
23 | return; | ||
24 | } | ||
25 | |||
26 | if (!xdg_surface->surface->mapped) { | ||
27 | // This is a startup notification. If we are tracking it, the data | ||
28 | // field is a launcher_ctx. | ||
29 | if (ctx->activated) { | ||
30 | // This ctx has already been activated and cannot be used again | ||
31 | // for a startup notification. It will be destroyed | ||
32 | return; | ||
33 | } else { | ||
34 | ctx->activated = true; | ||
35 | view_assign_ctx(view, ctx); | ||
36 | } | ||
16 | return; | 37 | return; |
17 | } | 38 | } |
18 | 39 | ||
19 | view_request_activate(view); | 40 | // This is an activation request. If this context is internal we have ctx->seat. |
41 | struct sway_seat *seat = ctx->seat; | ||
42 | if (!seat) { | ||
43 | // Otherwise, use the seat indicated by the launcher client in set_serial | ||
44 | seat = ctx->token->seat ? ctx->token->seat->data : NULL; | ||
45 | } | ||
46 | |||
47 | if (seat && ctx->had_focused_surface) { | ||
48 | view_request_activate(view, seat); | ||
49 | } else { | ||
50 | // The token is valid, but cannot be used to activate a window | ||
51 | view_request_urgent(view); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void *data) { | ||
56 | struct wlr_xdg_activation_token_v1 *token = data; | ||
57 | struct sway_seat *seat = token->seat ? token->seat->data : | ||
58 | input_manager_current_seat(); | ||
59 | |||
60 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | ||
61 | if (ws) { | ||
62 | launcher_ctx_create(token, &ws->node); | ||
63 | return; | ||
64 | } | ||
20 | } | 65 | } |
diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c index e7c3ea73..fa8c6279 100644 --- a/sway/xdg_decoration.c +++ b/sway/xdg_decoration.c | |||
@@ -10,7 +10,7 @@ static void xdg_decoration_handle_destroy(struct wl_listener *listener, | |||
10 | void *data) { | 10 | void *data) { |
11 | struct sway_xdg_decoration *deco = | 11 | struct sway_xdg_decoration *deco = |
12 | wl_container_of(listener, deco, destroy); | 12 | wl_container_of(listener, deco, destroy); |
13 | if(deco->view) { | 13 | if (deco->view) { |
14 | deco->view->xdg_decoration = NULL; | 14 | deco->view->xdg_decoration = NULL; |
15 | } | 15 | } |
16 | wl_list_remove(&deco->destroy.link); | 16 | wl_list_remove(&deco->destroy.link); |
@@ -23,13 +23,12 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener, | |||
23 | void *data) { | 23 | void *data) { |
24 | struct sway_xdg_decoration *deco = | 24 | struct sway_xdg_decoration *deco = |
25 | wl_container_of(listener, deco, request_mode); | 25 | wl_container_of(listener, deco, request_mode); |
26 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | 26 | set_xdg_decoration_mode(deco); |
27 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); | ||
28 | } | 27 | } |
29 | 28 | ||
30 | void handle_xdg_decoration(struct wl_listener *listener, void *data) { | 29 | void handle_xdg_decoration(struct wl_listener *listener, void *data) { |
31 | struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; | 30 | struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; |
32 | struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->surface->data; | 31 | struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->toplevel->base->data; |
33 | 32 | ||
34 | struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); | 33 | struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); |
35 | if (deco == NULL) { | 34 | if (deco == NULL) { |
@@ -48,16 +47,46 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data) { | |||
48 | 47 | ||
49 | wl_list_insert(&server.xdg_decorations, &deco->link); | 48 | wl_list_insert(&server.xdg_decorations, &deco->link); |
50 | 49 | ||
51 | xdg_decoration_handle_request_mode(&deco->request_mode, wlr_deco); | 50 | set_xdg_decoration_mode(deco); |
52 | } | 51 | } |
53 | 52 | ||
54 | struct sway_xdg_decoration *xdg_decoration_from_surface( | 53 | struct sway_xdg_decoration *xdg_decoration_from_surface( |
55 | struct wlr_surface *surface) { | 54 | struct wlr_surface *surface) { |
56 | struct sway_xdg_decoration *deco; | 55 | struct sway_xdg_decoration *deco; |
57 | wl_list_for_each(deco, &server.xdg_decorations, link) { | 56 | wl_list_for_each(deco, &server.xdg_decorations, link) { |
58 | if (deco->wlr_xdg_decoration->surface->surface == surface) { | 57 | if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) { |
59 | return deco; | 58 | return deco; |
60 | } | 59 | } |
61 | } | 60 | } |
62 | return NULL; | 61 | return NULL; |
63 | } | 62 | } |
63 | |||
64 | void set_xdg_decoration_mode(struct sway_xdg_decoration *deco) { | ||
65 | struct sway_view *view = deco->view; | ||
66 | enum wlr_xdg_toplevel_decoration_v1_mode mode = | ||
67 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; | ||
68 | enum wlr_xdg_toplevel_decoration_v1_mode client_mode = | ||
69 | deco->wlr_xdg_decoration->requested_mode; | ||
70 | |||
71 | bool floating; | ||
72 | if (view->container) { | ||
73 | floating = container_is_floating(view->container); | ||
74 | bool csd = false; | ||
75 | csd = client_mode == | ||
76 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; | ||
77 | view_update_csd_from_client(view, csd); | ||
78 | arrange_container(view->container); | ||
79 | transaction_commit_dirty(); | ||
80 | } else { | ||
81 | floating = view->impl->wants_floating && | ||
82 | view->impl->wants_floating(view); | ||
83 | } | ||
84 | |||
85 | if (floating && client_mode) { | ||
86 | mode = client_mode; | ||
87 | } | ||
88 | |||
89 | if (view->wlr_xdg_toplevel->base->initialized) { | ||
90 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, mode); | ||
91 | } | ||
92 | } | ||
diff --git a/swaybar/bar.c b/swaybar/bar.c index 15eab782..5b1213a8 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <errno.h> | 2 | #include <errno.h> |
4 | #include <fcntl.h> | 3 | #include <fcntl.h> |
@@ -51,10 +50,6 @@ static void swaybar_output_free(struct swaybar_output *output) { | |||
51 | if (output->surface != NULL) { | 50 | if (output->surface != NULL) { |
52 | wl_surface_destroy(output->surface); | 51 | wl_surface_destroy(output->surface); |
53 | } | 52 | } |
54 | if (output->input_region != NULL) { | ||
55 | wl_region_destroy(output->input_region); | ||
56 | } | ||
57 | zxdg_output_v1_destroy(output->xdg_output); | ||
58 | wl_output_destroy(output->output); | 53 | wl_output_destroy(output->output); |
59 | destroy_buffer(&output->buffers[0]); | 54 | destroy_buffer(&output->buffers[0]); |
60 | destroy_buffer(&output->buffers[1]); | 55 | destroy_buffer(&output->buffers[1]); |
@@ -114,10 +109,9 @@ static void add_layer_surface(struct swaybar_output *output) { | |||
114 | 109 | ||
115 | if (overlay) { | 110 | if (overlay) { |
116 | // Empty input region | 111 | // Empty input region |
117 | output->input_region = wl_compositor_create_region(bar->compositor); | 112 | struct wl_region *region = wl_compositor_create_region(bar->compositor); |
118 | assert(output->input_region); | 113 | wl_surface_set_input_region(output->surface, region); |
119 | 114 | wl_region_destroy(region); | |
120 | wl_surface_set_input_region(output->surface, output->input_region); | ||
121 | } | 115 | } |
122 | 116 | ||
123 | zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position); | 117 | zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position); |
@@ -172,7 +166,7 @@ bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) { | |||
172 | if (bar->status) { | 166 | if (bar->status) { |
173 | sway_log(SWAY_DEBUG, "Sending %s signal to status command", | 167 | sway_log(SWAY_DEBUG, "Sending %s signal to status command", |
174 | visible ? "cont" : "stop"); | 168 | visible ? "cont" : "stop"); |
175 | kill(bar->status->pid, visible ? | 169 | kill(-bar->status->pid, visible ? |
176 | bar->status->cont_signal : bar->status->stop_signal); | 170 | bar->status->cont_signal : bar->status->stop_signal); |
177 | } | 171 | } |
178 | } | 172 | } |
@@ -367,6 +361,9 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
367 | } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { | 361 | } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { |
368 | bar->xdg_output_manager = wl_registry_bind(registry, name, | 362 | bar->xdg_output_manager = wl_registry_bind(registry, name, |
369 | &zxdg_output_manager_v1_interface, 2); | 363 | &zxdg_output_manager_v1_interface, 2); |
364 | } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { | ||
365 | bar->cursor_shape_manager = wl_registry_bind(registry, name, | ||
366 | &wp_cursor_shape_manager_v1_interface, 1); | ||
370 | } | 367 | } |
371 | } | 368 | } |
372 | 369 | ||
@@ -430,15 +427,17 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) { | |||
430 | // Second roundtrip for xdg-output | 427 | // Second roundtrip for xdg-output |
431 | wl_display_roundtrip(bar->display); | 428 | wl_display_roundtrip(bar->display); |
432 | 429 | ||
433 | struct swaybar_seat *seat; | 430 | if (!bar->cursor_shape_manager) { |
434 | wl_list_for_each(seat, &bar->seats, link) { | 431 | struct swaybar_seat *seat; |
435 | struct swaybar_pointer *pointer = &seat->pointer; | 432 | wl_list_for_each(seat, &bar->seats, link) { |
436 | if (!pointer) { | 433 | struct swaybar_pointer *pointer = &seat->pointer; |
437 | continue; | 434 | if (!pointer) { |
435 | continue; | ||
436 | } | ||
437 | pointer->cursor_surface = | ||
438 | wl_compositor_create_surface(bar->compositor); | ||
439 | assert(pointer->cursor_surface); | ||
438 | } | 440 | } |
439 | pointer->cursor_surface = | ||
440 | wl_compositor_create_surface(bar->compositor); | ||
441 | assert(pointer->cursor_surface); | ||
442 | } | 441 | } |
443 | 442 | ||
444 | if (bar->config->status_command) { | 443 | if (bar->config->status_command) { |
diff --git a/swaybar/config.c b/swaybar/config.c index abedaec0..55bfcb72 100644 --- a/swaybar/config.c +++ b/swaybar/config.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include "swaybar/config.h" | 3 | #include "swaybar/config.h" |
@@ -26,7 +25,7 @@ struct swaybar_config *init_config(void) { | |||
26 | config->status_command = NULL; | 25 | config->status_command = NULL; |
27 | config->pango_markup = false; | 26 | config->pango_markup = false; |
28 | config->position = parse_position("bottom"); | 27 | config->position = parse_position("bottom"); |
29 | config->font = strdup("monospace 10"); | 28 | config->font_description = pango_font_description_from_string("monospace 10"); |
30 | config->mode = strdup("dock"); | 29 | config->mode = strdup("dock"); |
31 | config->hidden_state = strdup("hide"); | 30 | config->hidden_state = strdup("hide"); |
32 | config->sep_symbol = NULL; | 31 | config->sep_symbol = NULL; |
@@ -105,7 +104,7 @@ void free_tray_binding(struct tray_binding *binding) { | |||
105 | 104 | ||
106 | void free_config(struct swaybar_config *config) { | 105 | void free_config(struct swaybar_config *config) { |
107 | free(config->status_command); | 106 | free(config->status_command); |
108 | free(config->font); | 107 | pango_font_description_free(config->font_description); |
109 | free(config->mode); | 108 | free(config->mode); |
110 | free(config->hidden_state); | 109 | free(config->hidden_state); |
111 | free(config->sep_symbol); | 110 | free(config->sep_symbol); |
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 4bcd5843..62c22d43 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <json.h> | 1 | #include <json.h> |
3 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
4 | #include <ctype.h> | 3 | #include <ctype.h> |
@@ -28,6 +27,19 @@ void i3bar_block_unref(struct i3bar_block *block) { | |||
28 | } | 27 | } |
29 | } | 28 | } |
30 | 29 | ||
30 | static bool i3bar_parse_json_color(json_object *json, uint32_t *color) { | ||
31 | if (!json) { | ||
32 | return false; | ||
33 | } | ||
34 | |||
35 | const char *hexstring = json_object_get_string(json); | ||
36 | bool color_set = parse_color(hexstring, color); | ||
37 | if (!color_set) { | ||
38 | sway_log(SWAY_ERROR, "Ignoring invalid block hexadecimal color string: %s", hexstring); | ||
39 | } | ||
40 | return color_set; | ||
41 | } | ||
42 | |||
31 | static void i3bar_parse_json(struct status_line *status, | 43 | static void i3bar_parse_json(struct status_line *status, |
32 | struct json_object *json_array) { | 44 | struct json_object *json_array) { |
33 | struct i3bar_block *block, *tmp; | 45 | struct i3bar_block *block, *tmp; |
@@ -68,13 +80,7 @@ static void i3bar_parse_json(struct status_line *status, | |||
68 | strdup(json_object_get_string(full_text)) : NULL; | 80 | strdup(json_object_get_string(full_text)) : NULL; |
69 | block->short_text = short_text ? | 81 | block->short_text = short_text ? |
70 | strdup(json_object_get_string(short_text)) : NULL; | 82 | strdup(json_object_get_string(short_text)) : NULL; |
71 | if (color) { | 83 | block->color_set = i3bar_parse_json_color(color, &block->color); |
72 | const char *hexstring = json_object_get_string(color); | ||
73 | block->color_set = parse_color(hexstring, &block->color); | ||
74 | if (!block->color_set) { | ||
75 | sway_log(SWAY_ERROR, "Invalid block color: %s", hexstring); | ||
76 | } | ||
77 | } | ||
78 | if (min_width) { | 84 | if (min_width) { |
79 | json_type type = json_object_get_type(min_width); | 85 | json_type type = json_object_get_type(min_width); |
80 | if (type == json_type_int) { | 86 | if (type == json_type_int) { |
@@ -100,14 +106,8 @@ static void i3bar_parse_json(struct status_line *status, | |||
100 | block->separator_block_width = separator_block_width ? | 106 | block->separator_block_width = separator_block_width ? |
101 | json_object_get_int(separator_block_width) : 9; | 107 | json_object_get_int(separator_block_width) : 9; |
102 | // Airblader features | 108 | // Airblader features |
103 | const char *hex = background ? json_object_get_string(background) : NULL; | 109 | i3bar_parse_json_color(background, &block->background); |
104 | if (hex && !parse_color(hex, &block->background)) { | 110 | block->border_set = i3bar_parse_json_color(border, &block->border); |
105 | sway_log(SWAY_ERROR, "Ignoring invalid block background: %s", hex); | ||
106 | } | ||
107 | hex = border ? json_object_get_string(border) : NULL; | ||
108 | if (hex && !parse_color(hex, &block->border)) { | ||
109 | sway_log(SWAY_ERROR, "Ignoring invalid block border: %s", hex); | ||
110 | } | ||
111 | block->border_top = border_top ? json_object_get_int(border_top) : 1; | 111 | block->border_top = border_top ? json_object_get_int(border_top) : 1; |
112 | block->border_bottom = border_bottom ? | 112 | block->border_bottom = border_bottom ? |
113 | json_object_get_int(border_bottom) : 1; | 113 | json_object_get_int(border_bottom) : 1; |
@@ -268,11 +268,16 @@ bool i3bar_handle_readable(struct status_line *status) { | |||
268 | 268 | ||
269 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, | 269 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, |
270 | struct i3bar_block *block, double x, double y, double rx, double ry, | 270 | struct i3bar_block *block, double x, double y, double rx, double ry, |
271 | double w, double h, int scale, uint32_t button) { | 271 | double w, double h, int scale, uint32_t button, bool released) { |
272 | sway_log(SWAY_DEBUG, "block %s clicked", block->name); | 272 | sway_log(SWAY_DEBUG, "block %s clicked", block->name); |
273 | if (!block->name || !status->click_events) { | 273 | if (!block->name || !status->click_events) { |
274 | return HOTSPOT_PROCESS; | 274 | return HOTSPOT_PROCESS; |
275 | } | 275 | } |
276 | if (released) { | ||
277 | // Since we handle the pressed event, also handle the released event | ||
278 | // to block it from falling through to a binding in the bar | ||
279 | return HOTSPOT_IGNORE; | ||
280 | } | ||
276 | 281 | ||
277 | struct json_object *event_json = json_object_new_object(); | 282 | struct json_object *event_json = json_object_new_object(); |
278 | json_object_object_add(event_json, "name", | 283 | json_object_object_add(event_json, "name", |
diff --git a/common/background-image.c b/swaybar/image.c index 994a0805..ed24b9f9 100644 --- a/common/background-image.c +++ b/swaybar/image.c | |||
@@ -1,29 +1,12 @@ | |||
1 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include "background-image.h" | 2 | #include "config.h" |
3 | #include "cairo_util.h" | ||
4 | #include "log.h" | 3 | #include "log.h" |
4 | #include "swaybar/image.h" | ||
5 | |||
5 | #if HAVE_GDK_PIXBUF | 6 | #if HAVE_GDK_PIXBUF |
6 | #include <gdk-pixbuf/gdk-pixbuf.h> | 7 | #include <gdk-pixbuf/gdk-pixbuf.h> |
7 | #endif | 8 | #endif |
8 | 9 | ||
9 | enum background_mode parse_background_mode(const char *mode) { | ||
10 | if (strcmp(mode, "stretch") == 0) { | ||
11 | return BACKGROUND_MODE_STRETCH; | ||
12 | } else if (strcmp(mode, "fill") == 0) { | ||
13 | return BACKGROUND_MODE_FILL; | ||
14 | } else if (strcmp(mode, "fit") == 0) { | ||
15 | return BACKGROUND_MODE_FIT; | ||
16 | } else if (strcmp(mode, "center") == 0) { | ||
17 | return BACKGROUND_MODE_CENTER; | ||
18 | } else if (strcmp(mode, "tile") == 0) { | ||
19 | return BACKGROUND_MODE_TILE; | ||
20 | } else if (strcmp(mode, "solid_color") == 0) { | ||
21 | return BACKGROUND_MODE_SOLID_COLOR; | ||
22 | } | ||
23 | sway_log(SWAY_ERROR, "Unsupported background mode: %s", mode); | ||
24 | return BACKGROUND_MODE_INVALID; | ||
25 | } | ||
26 | |||
27 | #if HAVE_GDK_PIXBUF | 10 | #if HAVE_GDK_PIXBUF |
28 | static cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( | 11 | static cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( |
29 | const GdkPixbuf *gdkbuf) { | 12 | const GdkPixbuf *gdkbuf) { |
@@ -121,7 +104,7 @@ static cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( | |||
121 | } | 104 | } |
122 | #endif // HAVE_GDK_PIXBUF | 105 | #endif // HAVE_GDK_PIXBUF |
123 | 106 | ||
124 | cairo_surface_t *load_background_image(const char *path) { | 107 | cairo_surface_t *load_image(const char *path) { |
125 | cairo_surface_t *image; | 108 | cairo_surface_t *image; |
126 | #if HAVE_GDK_PIXBUF | 109 | #if HAVE_GDK_PIXBUF |
127 | GError *err = NULL; | 110 | GError *err = NULL; |
@@ -151,70 +134,3 @@ cairo_surface_t *load_background_image(const char *path) { | |||
151 | } | 134 | } |
152 | return image; | 135 | return image; |
153 | } | 136 | } |
154 | |||
155 | void render_background_image(cairo_t *cairo, cairo_surface_t *image, | ||
156 | enum background_mode mode, int buffer_width, int buffer_height) { | ||
157 | double width = cairo_image_surface_get_width(image); | ||
158 | double height = cairo_image_surface_get_height(image); | ||
159 | |||
160 | cairo_save(cairo); | ||
161 | switch (mode) { | ||
162 | case BACKGROUND_MODE_STRETCH: | ||
163 | cairo_scale(cairo, | ||
164 | (double)buffer_width / width, | ||
165 | (double)buffer_height / height); | ||
166 | cairo_set_source_surface(cairo, image, 0, 0); | ||
167 | break; | ||
168 | case BACKGROUND_MODE_FILL: { | ||
169 | double window_ratio = (double)buffer_width / buffer_height; | ||
170 | double bg_ratio = width / height; | ||
171 | |||
172 | if (window_ratio > bg_ratio) { | ||
173 | double scale = (double)buffer_width / width; | ||
174 | cairo_scale(cairo, scale, scale); | ||
175 | cairo_set_source_surface(cairo, image, | ||
176 | 0, (double)buffer_height / 2 / scale - height / 2); | ||
177 | } else { | ||
178 | double scale = (double)buffer_height / height; | ||
179 | cairo_scale(cairo, scale, scale); | ||
180 | cairo_set_source_surface(cairo, image, | ||
181 | (double)buffer_width / 2 / scale - width / 2, 0); | ||
182 | } | ||
183 | break; | ||
184 | } | ||
185 | case BACKGROUND_MODE_FIT: { | ||
186 | double window_ratio = (double)buffer_width / buffer_height; | ||
187 | double bg_ratio = width / height; | ||
188 | |||
189 | if (window_ratio > bg_ratio) { | ||
190 | double scale = (double)buffer_height / height; | ||
191 | cairo_scale(cairo, scale, scale); | ||
192 | cairo_set_source_surface(cairo, image, | ||
193 | (double)buffer_width / 2 / scale - width / 2, 0); | ||
194 | } else { | ||
195 | double scale = (double)buffer_width / width; | ||
196 | cairo_scale(cairo, scale, scale); | ||
197 | cairo_set_source_surface(cairo, image, | ||
198 | 0, (double)buffer_height / 2 / scale - height / 2); | ||
199 | } | ||
200 | break; | ||
201 | } | ||
202 | case BACKGROUND_MODE_CENTER: | ||
203 | cairo_set_source_surface(cairo, image, | ||
204 | (double)buffer_width / 2 - width / 2, | ||
205 | (double)buffer_height / 2 - height / 2); | ||
206 | break; | ||
207 | case BACKGROUND_MODE_TILE: { | ||
208 | cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); | ||
209 | cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); | ||
210 | cairo_set_source(cairo, pattern); | ||
211 | break; | ||
212 | } | ||
213 | case BACKGROUND_MODE_SOLID_COLOR: | ||
214 | case BACKGROUND_MODE_INVALID: | ||
215 | assert(0); | ||
216 | break; | ||
217 | } | ||
218 | cairo_paint(cairo); | ||
219 | cairo_restore(cairo); | ||
220 | } | ||
diff --git a/swaybar/input.c b/swaybar/input.c index 6e13f177..ada4bc86 100644 --- a/swaybar/input.c +++ b/swaybar/input.c | |||
@@ -81,8 +81,16 @@ void update_cursor(struct swaybar_seat *seat) { | |||
81 | int scale = pointer->current ? pointer->current->scale : 1; | 81 | int scale = pointer->current ? pointer->current->scale : 1; |
82 | pointer->cursor_theme = wl_cursor_theme_load( | 82 | pointer->cursor_theme = wl_cursor_theme_load( |
83 | cursor_theme, cursor_size * scale, seat->bar->shm); | 83 | cursor_theme, cursor_size * scale, seat->bar->shm); |
84 | if (!pointer->cursor_theme) { | ||
85 | sway_log(SWAY_ERROR, "Failed to load cursor theme"); | ||
86 | return; | ||
87 | } | ||
84 | struct wl_cursor *cursor; | 88 | struct wl_cursor *cursor; |
85 | cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); | 89 | cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default"); |
90 | if (!cursor) { | ||
91 | sway_log(SWAY_ERROR, "Failed to get default cursor from theme"); | ||
92 | return; | ||
93 | } | ||
86 | pointer->cursor_image = cursor->images[0]; | 94 | pointer->cursor_image = cursor->images[0]; |
87 | wl_surface_set_buffer_scale(pointer->cursor_surface, scale); | 95 | wl_surface_set_buffer_scale(pointer->cursor_surface, scale); |
88 | wl_surface_attach(pointer->cursor_surface, | 96 | wl_surface_attach(pointer->cursor_surface, |
@@ -103,7 +111,7 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | |||
103 | struct swaybar_pointer *pointer = &seat->pointer; | 111 | struct swaybar_pointer *pointer = &seat->pointer; |
104 | seat->pointer.x = wl_fixed_to_double(surface_x); | 112 | seat->pointer.x = wl_fixed_to_double(surface_x); |
105 | seat->pointer.y = wl_fixed_to_double(surface_y); | 113 | seat->pointer.y = wl_fixed_to_double(surface_y); |
106 | pointer->serial = serial; | 114 | |
107 | struct swaybar_output *output; | 115 | struct swaybar_output *output; |
108 | wl_list_for_each(output, &seat->bar->outputs, link) { | 116 | wl_list_for_each(output, &seat->bar->outputs, link) { |
109 | if (output->surface == surface) { | 117 | if (output->surface == surface) { |
@@ -111,7 +119,18 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | |||
111 | break; | 119 | break; |
112 | } | 120 | } |
113 | } | 121 | } |
114 | update_cursor(seat); | 122 | |
123 | if (seat->bar->cursor_shape_manager) { | ||
124 | struct wp_cursor_shape_device_v1 *device = | ||
125 | wp_cursor_shape_manager_v1_get_pointer( | ||
126 | seat->bar->cursor_shape_manager, wl_pointer); | ||
127 | wp_cursor_shape_device_v1_set_shape(device, serial, | ||
128 | WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); | ||
129 | wp_cursor_shape_device_v1_destroy(device); | ||
130 | } else { | ||
131 | pointer->serial = serial; | ||
132 | update_cursor(seat); | ||
133 | } | ||
115 | } | 134 | } |
116 | 135 | ||
117 | static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, | 136 | static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, |
@@ -141,16 +160,15 @@ static bool check_bindings(struct swaybar *bar, uint32_t button, | |||
141 | } | 160 | } |
142 | 161 | ||
143 | static bool process_hotspots(struct swaybar_output *output, | 162 | static bool process_hotspots(struct swaybar_output *output, |
144 | double x, double y, uint32_t button) { | 163 | double x, double y, uint32_t button, uint32_t state) { |
145 | double px = x * output->scale; | 164 | bool released = state == WL_POINTER_BUTTON_STATE_RELEASED; |
146 | double py = y * output->scale; | ||
147 | struct swaybar_hotspot *hotspot; | 165 | struct swaybar_hotspot *hotspot; |
148 | wl_list_for_each(hotspot, &output->hotspots, link) { | 166 | wl_list_for_each(hotspot, &output->hotspots, link) { |
149 | if (px >= hotspot->x && py >= hotspot->y | 167 | if (x >= hotspot->x && y >= hotspot->y |
150 | && px < hotspot->x + hotspot->width | 168 | && x < hotspot->x + hotspot->width |
151 | && py < hotspot->y + hotspot->height) { | 169 | && y < hotspot->y + hotspot->height) { |
152 | if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, | 170 | if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, |
153 | button, hotspot->data)) { | 171 | button, released, hotspot->data)) { |
154 | return true; | 172 | return true; |
155 | } | 173 | } |
156 | } | 174 | } |
@@ -168,14 +186,11 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, | |||
168 | return; | 186 | return; |
169 | } | 187 | } |
170 | 188 | ||
171 | if (check_bindings(seat->bar, button, state)) { | 189 | if (process_hotspots(output, pointer->x, pointer->y, button, state)) { |
172 | return; | 190 | return; |
173 | } | 191 | } |
174 | 192 | ||
175 | if (state != WL_POINTER_BUTTON_STATE_PRESSED) { | 193 | check_bindings(seat->bar, button, state); |
176 | return; | ||
177 | } | ||
178 | process_hotspots(output, pointer->x, pointer->y, button); | ||
179 | } | 194 | } |
180 | 195 | ||
181 | static void workspace_next(struct swaybar *bar, struct swaybar_output *output, | 196 | static void workspace_next(struct swaybar *bar, struct swaybar_output *output, |
@@ -211,7 +226,7 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output, | |||
211 | } | 226 | } |
212 | } | 227 | } |
213 | 228 | ||
214 | if (new) { | 229 | if (new && new != active) { |
215 | ipc_send_workspace_command(bar, new->name); | 230 | ipc_send_workspace_command(bar, new->name); |
216 | 231 | ||
217 | // Since we're asking Sway to switch to 'new', it should become visible. | 232 | // Since we're asking Sway to switch to 'new', it should become visible. |
@@ -224,15 +239,15 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output, | |||
224 | static void process_discrete_scroll(struct swaybar_seat *seat, | 239 | static void process_discrete_scroll(struct swaybar_seat *seat, |
225 | struct swaybar_output *output, struct swaybar_pointer *pointer, | 240 | struct swaybar_output *output, struct swaybar_pointer *pointer, |
226 | uint32_t axis, wl_fixed_t value) { | 241 | uint32_t axis, wl_fixed_t value) { |
227 | // If there is a button press binding, execute it, skip default behavior, | ||
228 | // and check button release bindings | ||
229 | uint32_t button = wl_axis_to_button(axis, value); | 242 | uint32_t button = wl_axis_to_button(axis, value); |
230 | if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) { | 243 | if (process_hotspots(output, pointer->x, pointer->y, button, WL_POINTER_BUTTON_STATE_PRESSED)) { |
231 | check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED); | 244 | // (Currently hotspots don't do anything on release events, so no need to emit one) |
232 | return; | 245 | return; |
233 | } | 246 | } |
234 | 247 | ||
235 | if (process_hotspots(output, pointer->x, pointer->y, button)) { | 248 | // If there is a button press binding, execute it, and check button release bindings |
249 | if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) { | ||
250 | check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED); | ||
236 | return; | 251 | return; |
237 | } | 252 | } |
238 | 253 | ||
@@ -405,7 +420,8 @@ static void wl_touch_up(void *data, struct wl_touch *wl_touch, | |||
405 | } | 420 | } |
406 | if (time - slot->time < 500) { | 421 | if (time - slot->time < 500) { |
407 | // Tap, treat it like a pointer click | 422 | // Tap, treat it like a pointer click |
408 | process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT); | 423 | process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); |
424 | // (Currently hotspots don't do anything on release events, so no need to emit one) | ||
409 | } | 425 | } |
410 | slot->output = NULL; | 426 | slot->output = NULL; |
411 | } | 427 | } |
diff --git a/swaybar/ipc.c b/swaybar/ipc.c index a64aa1ab..03500bdf 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809 | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <poll.h> | 2 | #include <poll.h> |
4 | #include <stdio.h> | 3 | #include <stdio.h> |
@@ -147,8 +146,10 @@ static bool ipc_parse_config( | |||
147 | 146 | ||
148 | json_object *font = json_object_object_get(bar_config, "font"); | 147 | json_object *font = json_object_object_get(bar_config, "font"); |
149 | if (font) { | 148 | if (font) { |
150 | free(config->font); | 149 | pango_font_description_free(config->font_description); |
151 | config->font = parse_font(json_object_get_string(font)); | 150 | char *font_value = parse_font(json_object_get_string(font)); |
151 | config->font_description = pango_font_description_from_string(font_value); | ||
152 | free(font_value); | ||
152 | } | 153 | } |
153 | 154 | ||
154 | json_object *gaps = json_object_object_get(bar_config, "gaps"); | 155 | json_object *gaps = json_object_object_get(bar_config, "gaps"); |
@@ -424,12 +425,9 @@ bool ipc_initialize(struct swaybar *bar) { | |||
424 | } | 425 | } |
425 | free(res); | 426 | free(res); |
426 | 427 | ||
427 | struct swaybar_config *config = bar->config; | 428 | char *subscribe = |
428 | char subscribe[128]; // suitably large buffer | 429 | "[ \"barconfig_update\", \"bar_state_update\", \"mode\", \"workspace\" ]"; |
429 | len = snprintf(subscribe, 128, | 430 | len = strlen(subscribe); |
430 | "[ \"barconfig_update\" , \"bar_state_update\" %s %s ]", | ||
431 | config->binding_mode_indicator ? ", \"mode\"" : "", | ||
432 | config->workspace_buttons ? ", \"workspace\"" : ""); | ||
433 | free(ipc_single_command(bar->ipc_event_socketfd, | 431 | free(ipc_single_command(bar->ipc_event_socketfd, |
434 | IPC_SUBSCRIBE, subscribe, &len)); | 432 | IPC_SUBSCRIBE, subscribe, &len)); |
435 | return true; | 433 | return true; |
@@ -485,8 +483,7 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload, | |||
485 | destroy_layer_surface(output); | 483 | destroy_layer_surface(output); |
486 | wl_list_remove(&output->link); | 484 | wl_list_remove(&output->link); |
487 | wl_list_insert(&bar->unused_outputs, &output->link); | 485 | wl_list_insert(&bar->unused_outputs, &output->link); |
488 | } else if (!oldcfg->font || !newcfg->font || | 486 | } else if (!pango_font_description_equal(oldcfg->font_description, newcfg->font_description)) { |
489 | strcmp(oldcfg->font, newcfg->font) != 0) { | ||
490 | output->height = 0; // force update height | 487 | output->height = 0; // force update height |
491 | } | 488 | } |
492 | } | 489 | } |
@@ -550,7 +547,7 @@ bool handle_ipc_readable(struct swaybar *bar) { | |||
550 | // The default depth of 32 is too small to represent some nested layouts, but | 547 | // The default depth of 32 is too small to represent some nested layouts, but |
551 | // we can't pass INT_MAX here because json-c (as of this writing) prefaults | 548 | // we can't pass INT_MAX here because json-c (as of this writing) prefaults |
552 | // all the memory for its stack. | 549 | // all the memory for its stack. |
553 | json_tokener *tok = json_tokener_new_ex(256); | 550 | json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH); |
554 | if (!tok) { | 551 | if (!tok) { |
555 | sway_log_errno(SWAY_ERROR, "failed to create tokener"); | 552 | sway_log_errno(SWAY_ERROR, "failed to create tokener"); |
556 | free_ipc_response(resp); | 553 | free_ipc_response(resp); |
diff --git a/swaybar/main.c b/swaybar/main.c index a44c1e63..3dc67233 100644 --- a/swaybar/main.c +++ b/swaybar/main.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <string.h> | 3 | #include <string.h> |
diff --git a/swaybar/meson.build b/swaybar/meson.build index 9feb3cd2..34bbdeea 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build | |||
@@ -8,7 +8,6 @@ tray_files = have_tray ? [ | |||
8 | 8 | ||
9 | swaybar_deps = [ | 9 | swaybar_deps = [ |
10 | cairo, | 10 | cairo, |
11 | client_protos, | ||
12 | gdk_pixbuf, | 11 | gdk_pixbuf, |
13 | jsonc, | 12 | jsonc, |
14 | math, | 13 | math, |
@@ -27,12 +26,14 @@ executable( | |||
27 | 'bar.c', | 26 | 'bar.c', |
28 | 'config.c', | 27 | 'config.c', |
29 | 'i3bar.c', | 28 | 'i3bar.c', |
29 | 'image.c', | ||
30 | 'input.c', | 30 | 'input.c', |
31 | 'ipc.c', | 31 | 'ipc.c', |
32 | 'main.c', | 32 | 'main.c', |
33 | 'render.c', | 33 | 'render.c', |
34 | 'status_line.c', | 34 | 'status_line.c', |
35 | tray_files | 35 | tray_files, |
36 | wl_protos_src, | ||
36 | ], | 37 | ], |
37 | include_directories: [sway_inc], | 38 | include_directories: [sway_inc], |
38 | dependencies: swaybar_deps, | 39 | dependencies: swaybar_deps, |
diff --git a/swaybar/render.c b/swaybar/render.c index fcc8be1d..879a4e42 100644 --- a/swaybar/render.c +++ b/swaybar/render.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
4 | #include <limits.h> | 3 | #include <limits.h> |
@@ -14,6 +13,7 @@ | |||
14 | #include "swaybar/ipc.h" | 13 | #include "swaybar/ipc.h" |
15 | #include "swaybar/render.h" | 14 | #include "swaybar/render.h" |
16 | #include "swaybar/status_line.h" | 15 | #include "swaybar/status_line.h" |
16 | #include "log.h" | ||
17 | #if HAVE_TRAY | 17 | #if HAVE_TRAY |
18 | #include "swaybar/tray/tray.h" | 18 | #include "swaybar/tray/tray.h" |
19 | #endif | 19 | #endif |
@@ -53,22 +53,21 @@ static uint32_t render_status_line_error(struct render_context *ctx, double *x) | |||
53 | return 0; | 53 | return 0; |
54 | } | 54 | } |
55 | 55 | ||
56 | uint32_t height = output->height * output->scale; | 56 | uint32_t height = output->height; |
57 | 57 | ||
58 | cairo_t *cairo = ctx->cairo; | 58 | cairo_t *cairo = ctx->cairo; |
59 | cairo_set_source_u32(cairo, 0xFF0000FF); | 59 | cairo_set_source_u32(cairo, 0xFF0000FF); |
60 | 60 | ||
61 | int margin = 3 * output->scale; | 61 | int margin = 3; |
62 | double ws_vertical_padding = | 62 | double ws_vertical_padding = output->bar->config->status_padding; |
63 | output->bar->config->status_padding * output->scale; | ||
64 | 63 | ||
65 | char *font = output->bar->config->font; | 64 | PangoFontDescription *font = output->bar->config->font_description; |
66 | int text_width, text_height; | 65 | int text_width, text_height; |
67 | get_text_size(cairo, font, &text_width, &text_height, NULL, | 66 | get_text_size(cairo, font, &text_width, &text_height, NULL, |
68 | output->scale, false, "%s", error); | 67 | 1, false, "%s", error); |
69 | 68 | ||
70 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | 69 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; |
71 | uint32_t ideal_surface_height = ideal_height / output->scale; | 70 | uint32_t ideal_surface_height = ideal_height; |
72 | if (!output->bar->config->height && | 71 | if (!output->bar->config->height && |
73 | output->height < ideal_surface_height) { | 72 | output->height < ideal_surface_height) { |
74 | return ideal_surface_height; | 73 | return ideal_surface_height; |
@@ -78,7 +77,7 @@ static uint32_t render_status_line_error(struct render_context *ctx, double *x) | |||
78 | double text_y = height / 2.0 - text_height / 2.0; | 77 | double text_y = height / 2.0 - text_height / 2.0; |
79 | cairo_move_to(cairo, *x, (int)floor(text_y)); | 78 | cairo_move_to(cairo, *x, (int)floor(text_y)); |
80 | choose_text_aa_mode(ctx, 0xFF0000FF); | 79 | choose_text_aa_mode(ctx, 0xFF0000FF); |
81 | pango_printf(cairo, font, output->scale, false, "%s", error); | 80 | render_text(cairo, font, 1, false, "%s", error); |
82 | *x -= margin; | 81 | *x -= margin; |
83 | return output->height; | 82 | return output->height; |
84 | } | 83 | } |
@@ -97,26 +96,25 @@ static uint32_t render_status_line_text(struct render_context *ctx, double *x) { | |||
97 | cairo_set_source_u32(cairo, fontcolor); | 96 | cairo_set_source_u32(cairo, fontcolor); |
98 | 97 | ||
99 | int text_width, text_height; | 98 | int text_width, text_height; |
100 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 99 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, |
101 | output->scale, config->pango_markup, "%s", text); | 100 | 1, config->pango_markup, "%s", text); |
102 | 101 | ||
103 | double ws_vertical_padding = config->status_padding * output->scale; | 102 | double ws_vertical_padding = config->status_padding; |
104 | int margin = 3 * output->scale; | 103 | int margin = 3; |
105 | 104 | ||
106 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | 105 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; |
107 | uint32_t ideal_surface_height = ideal_height / output->scale; | 106 | uint32_t ideal_surface_height = ideal_height; |
108 | if (!output->bar->config->height && | 107 | if (!output->bar->config->height && |
109 | output->height < ideal_surface_height) { | 108 | output->height < ideal_surface_height) { |
110 | return ideal_surface_height; | 109 | return ideal_surface_height; |
111 | } | 110 | } |
112 | 111 | ||
113 | *x -= text_width + margin; | 112 | *x -= text_width + margin; |
114 | uint32_t height = output->height * output->scale; | 113 | uint32_t height = output->height; |
115 | double text_y = height / 2.0 - text_height / 2.0; | 114 | double text_y = height / 2.0 - text_height / 2.0; |
116 | cairo_move_to(cairo, *x, (int)floor(text_y)); | 115 | cairo_move_to(cairo, *x, (int)floor(text_y)); |
117 | choose_text_aa_mode(ctx, fontcolor); | 116 | choose_text_aa_mode(ctx, fontcolor); |
118 | pango_printf(cairo, config->font, output->scale, | 117 | render_text(cairo, config->font_description, 1, config->pango_markup, "%s", text); |
119 | config->pango_markup, "%s", text); | ||
120 | *x -= margin; | 118 | *x -= margin; |
121 | return output->height; | 119 | return output->height; |
122 | } | 120 | } |
@@ -161,15 +159,15 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, | |||
161 | 159 | ||
162 | static enum hotspot_event_handling block_hotspot_callback( | 160 | static enum hotspot_event_handling block_hotspot_callback( |
163 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, | 161 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, |
164 | double x, double y, uint32_t button, void *data) { | 162 | double x, double y, uint32_t button, bool released, void *data) { |
165 | struct i3bar_block *block = data; | 163 | struct i3bar_block *block = data; |
166 | struct status_line *status = output->bar->status; | 164 | struct status_line *status = output->bar->status; |
167 | return i3bar_block_send_click(status, block, x, y, | 165 | return i3bar_block_send_click(status, block, x, y, |
168 | x - (double)hotspot->x / output->scale, | 166 | x - (double)hotspot->x, |
169 | y - (double)hotspot->y / output->scale, | 167 | y - (double)hotspot->y, |
170 | (double)hotspot->width / output->scale, | 168 | (double)hotspot->width, |
171 | (double)hotspot->height / output->scale, | 169 | (double)hotspot->height, |
172 | output->scale, button); | 170 | output->scale, button, released); |
173 | } | 171 | } |
174 | 172 | ||
175 | static void i3bar_block_unref_callback(void *data) { | 173 | static void i3bar_block_unref_callback(void *data) { |
@@ -191,17 +189,17 @@ static uint32_t render_status_block(struct render_context *ctx, | |||
191 | struct swaybar_output *output = ctx->output; | 189 | struct swaybar_output *output = ctx->output; |
192 | struct swaybar_config *config = output->bar->config; | 190 | struct swaybar_config *config = output->bar->config; |
193 | int text_width, text_height; | 191 | int text_width, text_height; |
194 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 192 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, |
195 | output->scale, block->markup, "%s", text); | 193 | block->markup, "%s", text); |
196 | 194 | ||
197 | int margin = 3 * output->scale; | 195 | int margin = 3; |
198 | double ws_vertical_padding = config->status_padding * output->scale; | 196 | double ws_vertical_padding = config->status_padding; |
199 | 197 | ||
200 | int width = text_width; | 198 | int width = text_width; |
201 | if (block->min_width_str) { | 199 | if (block->min_width_str) { |
202 | int w; | 200 | int w; |
203 | get_text_size(cairo, config->font, &w, NULL, NULL, | 201 | get_text_size(cairo, config->font_description, &w, NULL, NULL, 1, block->markup, |
204 | output->scale, block->markup, "%s", block->min_width_str); | 202 | "%s", block->min_width_str); |
205 | block->min_width = w; | 203 | block->min_width = w; |
206 | } | 204 | } |
207 | if (width < block->min_width) { | 205 | if (width < block->min_width) { |
@@ -210,30 +208,30 @@ static uint32_t render_status_block(struct render_context *ctx, | |||
210 | 208 | ||
211 | double block_width = width; | 209 | double block_width = width; |
212 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | 210 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; |
213 | uint32_t ideal_surface_height = ideal_height / output->scale; | 211 | uint32_t ideal_surface_height = ideal_height; |
214 | if (!output->bar->config->height && | 212 | if (!output->bar->config->height && |
215 | output->height < ideal_surface_height) { | 213 | output->height < ideal_surface_height) { |
216 | return ideal_surface_height; | 214 | return ideal_surface_height; |
217 | } | 215 | } |
218 | 216 | ||
219 | *x -= width; | 217 | *x -= width; |
220 | if ((block->border || block->urgent) && block->border_left > 0) { | 218 | if ((block->border_set || block->urgent) && block->border_left > 0) { |
221 | *x -= (block->border_left * output->scale + margin); | 219 | *x -= (block->border_left + margin); |
222 | block_width += block->border_left * output->scale + margin; | 220 | block_width += block->border_left + margin; |
223 | } | 221 | } |
224 | if ((block->border || block->urgent) && block->border_right > 0) { | 222 | if ((block->border_set || block->urgent) && block->border_right > 0) { |
225 | *x -= (block->border_right * output->scale + margin); | 223 | *x -= (block->border_right + margin); |
226 | block_width += block->border_right * output->scale + margin; | 224 | block_width += block->border_right + margin; |
227 | } | 225 | } |
228 | 226 | ||
229 | int sep_width, sep_height; | 227 | int sep_width, sep_height; |
230 | int sep_block_width = block->separator_block_width; | 228 | int sep_block_width = block->separator_block_width; |
231 | if (!edge) { | 229 | if (!edge) { |
232 | if (config->sep_symbol) { | 230 | if (config->sep_symbol) { |
233 | get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, | 231 | get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL, |
234 | output->scale, false, "%s", config->sep_symbol); | 232 | 1, false, "%s", config->sep_symbol); |
235 | uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; | 233 | uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; |
236 | uint32_t _ideal_surface_height = _ideal_height / output->scale; | 234 | uint32_t _ideal_surface_height = _ideal_height; |
237 | if (!output->bar->config->height && | 235 | if (!output->bar->config->height && |
238 | output->height < _ideal_surface_height) { | 236 | output->height < _ideal_surface_height) { |
239 | return _ideal_surface_height; | 237 | return _ideal_surface_height; |
@@ -244,10 +242,10 @@ static uint32_t render_status_block(struct render_context *ctx, | |||
244 | } | 242 | } |
245 | *x -= sep_block_width; | 243 | *x -= sep_block_width; |
246 | } else if (config->status_edge_padding) { | 244 | } else if (config->status_edge_padding) { |
247 | *x -= config->status_edge_padding * output->scale; | 245 | *x -= config->status_edge_padding; |
248 | } | 246 | } |
249 | 247 | ||
250 | uint32_t height = output->height * output->scale; | 248 | uint32_t height = output->height; |
251 | if (output->bar->status->click_events) { | 249 | if (output->bar->status->click_events) { |
252 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); | 250 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); |
253 | hotspot->x = *x; | 251 | hotspot->x = *x; |
@@ -275,23 +273,25 @@ static uint32_t render_status_block(struct render_context *ctx, | |||
275 | 273 | ||
276 | uint32_t border_color = block->urgent | 274 | uint32_t border_color = block->urgent |
277 | ? config->colors.urgent_workspace.border : block->border; | 275 | ? config->colors.urgent_workspace.border : block->border; |
278 | if (border_color && block->border_top > 0) { | 276 | if (block->border_set || block->urgent) { |
279 | render_sharp_line(cairo, border_color, x_pos, y_pos, | 277 | if (block->border_top > 0) { |
280 | block_width, block->border_top * output->scale); | 278 | render_sharp_line(cairo, border_color, x_pos, y_pos, |
281 | } | 279 | block_width, block->border_top); |
282 | if (border_color && block->border_bottom > 0) { | 280 | } |
283 | render_sharp_line(cairo, border_color, x_pos, | 281 | if (block->border_bottom > 0) { |
284 | y_pos + render_height - block->border_bottom * output->scale, | 282 | render_sharp_line(cairo, border_color, x_pos, |
285 | block_width, block->border_bottom * output->scale); | 283 | y_pos + render_height - block->border_bottom, |
286 | } | 284 | block_width, block->border_bottom); |
287 | if (border_color && block->border_left > 0) { | 285 | } |
288 | render_sharp_line(cairo, border_color, x_pos, y_pos, | 286 | if (block->border_left > 0) { |
289 | block->border_left * output->scale, render_height); | 287 | render_sharp_line(cairo, border_color, x_pos, y_pos, |
290 | x_pos += block->border_left * output->scale + margin; | 288 | block->border_left, render_height); |
289 | } | ||
290 | x_pos += block->border_left + margin; | ||
291 | } | 291 | } |
292 | 292 | ||
293 | double offset = 0; | 293 | double offset = 0; |
294 | if (strncmp(block->align, "left", 5) == 0) { | 294 | if (strncmp(block->align, "left", 4) == 0) { |
295 | offset = x_pos; | 295 | offset = x_pos; |
296 | } else if (strncmp(block->align, "right", 5) == 0) { | 296 | } else if (strncmp(block->align, "right", 5) == 0) { |
297 | offset = x_pos + width - text_width; | 297 | offset = x_pos + width - text_width; |
@@ -306,15 +306,16 @@ static uint32_t render_status_block(struct render_context *ctx, | |||
306 | color = block->urgent ? config->colors.urgent_workspace.text : color; | 306 | color = block->urgent ? config->colors.urgent_workspace.text : color; |
307 | cairo_set_source_u32(cairo, color); | 307 | cairo_set_source_u32(cairo, color); |
308 | choose_text_aa_mode(ctx, color); | 308 | choose_text_aa_mode(ctx, color); |
309 | pango_printf(cairo, config->font, output->scale, | 309 | render_text(cairo, config->font_description, 1, block->markup, "%s", text); |
310 | block->markup, "%s", text); | ||
311 | x_pos += width; | 310 | x_pos += width; |
312 | 311 | ||
313 | if (block->border && block->border_right > 0) { | 312 | if (block->border_set || block->urgent) { |
314 | x_pos += margin; | 313 | x_pos += margin; |
315 | render_sharp_line(cairo, border_color, x_pos, y_pos, | 314 | if (block->border_right > 0) { |
316 | block->border_right * output->scale, render_height); | 315 | render_sharp_line(cairo, border_color, x_pos, y_pos, |
317 | x_pos += block->border_right * output->scale; | 316 | block->border_right, render_height); |
317 | } | ||
318 | x_pos += block->border_right; | ||
318 | } | 319 | } |
319 | 320 | ||
320 | if (!edge && block->separator) { | 321 | if (!edge && block->separator) { |
@@ -329,7 +330,7 @@ static uint32_t render_status_block(struct render_context *ctx, | |||
329 | double sep_y = height / 2.0 - sep_height / 2.0; | 330 | double sep_y = height / 2.0 - sep_height / 2.0; |
330 | cairo_move_to(cairo, offset, (int)floor(sep_y)); | 331 | cairo_move_to(cairo, offset, (int)floor(sep_y)); |
331 | choose_text_aa_mode(ctx, color); | 332 | choose_text_aa_mode(ctx, color); |
332 | pango_printf(cairo, config->font, output->scale, false, | 333 | render_text(cairo, config->font_description, 1, false, |
333 | "%s", config->sep_symbol); | 334 | "%s", config->sep_symbol); |
334 | } else { | 335 | } else { |
335 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | 336 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); |
@@ -352,18 +353,18 @@ static void predict_status_block_pos(cairo_t *cairo, | |||
352 | struct swaybar_config *config = output->bar->config; | 353 | struct swaybar_config *config = output->bar->config; |
353 | 354 | ||
354 | int text_width, text_height; | 355 | int text_width, text_height; |
355 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 356 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, |
356 | output->scale, block->markup, "%s", block->full_text); | 357 | block->markup, "%s", block->full_text); |
357 | 358 | ||
358 | int margin = 3 * output->scale; | 359 | int margin = 3; |
359 | double ws_vertical_padding = config->status_padding * output->scale; | 360 | double ws_vertical_padding = config->status_padding; |
360 | 361 | ||
361 | int width = text_width; | 362 | int width = text_width; |
362 | 363 | ||
363 | if (block->min_width_str) { | 364 | if (block->min_width_str) { |
364 | int w; | 365 | int w; |
365 | get_text_size(cairo, config->font, &w, NULL, NULL, | 366 | get_text_size(cairo, config->font_description, &w, NULL, NULL, |
366 | output->scale, block->markup, "%s", block->min_width_str); | 367 | 1, block->markup, "%s", block->min_width_str); |
367 | block->min_width = w; | 368 | block->min_width = w; |
368 | } | 369 | } |
369 | if (width < block->min_width) { | 370 | if (width < block->min_width) { |
@@ -371,28 +372,28 @@ static void predict_status_block_pos(cairo_t *cairo, | |||
371 | } | 372 | } |
372 | 373 | ||
373 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | 374 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; |
374 | uint32_t ideal_surface_height = ideal_height / output->scale; | 375 | uint32_t ideal_surface_height = ideal_height; |
375 | if (!output->bar->config->height && | 376 | if (!output->bar->config->height && |
376 | output->height < ideal_surface_height) { | 377 | output->height < ideal_surface_height) { |
377 | return; | 378 | return; |
378 | } | 379 | } |
379 | 380 | ||
380 | *x -= width; | 381 | *x -= width; |
381 | if ((block->border || block->urgent) && block->border_left > 0) { | 382 | if ((block->border_set || block->urgent) && block->border_left > 0) { |
382 | *x -= (block->border_left * output->scale + margin); | 383 | *x -= (block->border_left + margin); |
383 | } | 384 | } |
384 | if ((block->border || block->urgent) && block->border_right > 0) { | 385 | if ((block->border_set || block->urgent) && block->border_right > 0) { |
385 | *x -= (block->border_right * output->scale + margin); | 386 | *x -= (block->border_right + margin); |
386 | } | 387 | } |
387 | 388 | ||
388 | int sep_width, sep_height; | 389 | int sep_width, sep_height; |
389 | int sep_block_width = block->separator_block_width; | 390 | int sep_block_width = block->separator_block_width; |
390 | if (!edge) { | 391 | if (!edge) { |
391 | if (config->sep_symbol) { | 392 | if (config->sep_symbol) { |
392 | get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, | 393 | get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL, |
393 | output->scale, false, "%s", config->sep_symbol); | 394 | 1, false, "%s", config->sep_symbol); |
394 | uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; | 395 | uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; |
395 | uint32_t _ideal_surface_height = _ideal_height / output->scale; | 396 | uint32_t _ideal_surface_height = _ideal_height; |
396 | if (!output->bar->config->height && | 397 | if (!output->bar->config->height && |
397 | output->height < _ideal_surface_height) { | 398 | output->height < _ideal_surface_height) { |
398 | return; | 399 | return; |
@@ -403,13 +404,13 @@ static void predict_status_block_pos(cairo_t *cairo, | |||
403 | } | 404 | } |
404 | *x -= sep_block_width; | 405 | *x -= sep_block_width; |
405 | } else if (config->status_edge_padding) { | 406 | } else if (config->status_edge_padding) { |
406 | *x -= config->status_edge_padding * output->scale; | 407 | *x -= config->status_edge_padding; |
407 | } | 408 | } |
408 | } | 409 | } |
409 | 410 | ||
410 | static double predict_status_line_pos(cairo_t *cairo, | 411 | static double predict_status_line_pos(cairo_t *cairo, |
411 | struct swaybar_output *output, double x) { | 412 | struct swaybar_output *output, double x) { |
412 | bool edge = x == output->width * output->scale; | 413 | bool edge = x == output->width; |
413 | struct i3bar_block *block; | 414 | struct i3bar_block *block; |
414 | wl_list_for_each(block, &output->bar->status->blocks, link) { | 415 | wl_list_for_each(block, &output->bar->status->blocks, link) { |
415 | predict_status_block_pos(cairo, output, block, &x, edge); | 416 | predict_status_block_pos(cairo, output, block, &x, edge); |
@@ -424,24 +425,24 @@ static uint32_t predict_workspace_button_length(cairo_t *cairo, | |||
424 | struct swaybar_config *config = output->bar->config; | 425 | struct swaybar_config *config = output->bar->config; |
425 | 426 | ||
426 | int text_width, text_height; | 427 | int text_width, text_height; |
427 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 428 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, |
428 | output->scale, config->pango_markup, "%s", ws->label); | 429 | config->pango_markup, "%s", ws->label); |
429 | 430 | ||
430 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | 431 | int ws_vertical_padding = WS_VERTICAL_PADDING; |
431 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; | 432 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING; |
432 | int border_width = BORDER_WIDTH * output->scale; | 433 | int border_width = BORDER_WIDTH; |
433 | 434 | ||
434 | uint32_t ideal_height = ws_vertical_padding * 2 + text_height | 435 | uint32_t ideal_height = ws_vertical_padding * 2 + text_height |
435 | + border_width * 2; | 436 | + border_width * 2; |
436 | uint32_t ideal_surface_height = ideal_height / output->scale; | 437 | uint32_t ideal_surface_height = ideal_height; |
437 | if (!output->bar->config->height && | 438 | if (!output->bar->config->height && |
438 | output->height < ideal_surface_height) { | 439 | output->height < ideal_surface_height) { |
439 | return 0; | 440 | return 0; |
440 | } | 441 | } |
441 | 442 | ||
442 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; | 443 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; |
443 | if (width < config->workspace_min_width * output->scale) { | 444 | if (width < config->workspace_min_width) { |
444 | width = config->workspace_min_width * output->scale; | 445 | width = config->workspace_min_width; |
445 | } | 446 | } |
446 | return width; | 447 | return width; |
447 | } | 448 | } |
@@ -472,24 +473,24 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo, | |||
472 | } | 473 | } |
473 | 474 | ||
474 | int text_width, text_height; | 475 | int text_width, text_height; |
475 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 476 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, |
476 | output->scale, output->bar->mode_pango_markup, | 477 | 1, output->bar->mode_pango_markup, |
477 | "%s", mode); | 478 | "%s", mode); |
478 | 479 | ||
479 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | 480 | int ws_vertical_padding = WS_VERTICAL_PADDING; |
480 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; | 481 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING; |
481 | int border_width = BORDER_WIDTH * output->scale; | 482 | int border_width = BORDER_WIDTH; |
482 | 483 | ||
483 | uint32_t ideal_height = text_height + ws_vertical_padding * 2 | 484 | uint32_t ideal_height = text_height + ws_vertical_padding * 2 |
484 | + border_width * 2; | 485 | + border_width * 2; |
485 | uint32_t ideal_surface_height = ideal_height / output->scale; | 486 | uint32_t ideal_surface_height = ideal_height; |
486 | if (!output->bar->config->height && | 487 | if (!output->bar->config->height && |
487 | output->height < ideal_surface_height) { | 488 | output->height < ideal_surface_height) { |
488 | return 0; | 489 | return 0; |
489 | } | 490 | } |
490 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; | 491 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; |
491 | if (width < config->workspace_min_width * output->scale) { | 492 | if (width < config->workspace_min_width) { |
492 | width = config->workspace_min_width * output->scale; | 493 | width = config->workspace_min_width; |
493 | } | 494 | } |
494 | return width; | 495 | return width; |
495 | } | 496 | } |
@@ -497,7 +498,7 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo, | |||
497 | static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) { | 498 | static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) { |
498 | struct swaybar_output *output = ctx->output; | 499 | struct swaybar_output *output = ctx->output; |
499 | uint32_t max_height = 0; | 500 | uint32_t max_height = 0; |
500 | bool edge = *x == output->width * output->scale; | 501 | bool edge = *x == output->width; |
501 | struct i3bar_block *block; | 502 | struct i3bar_block *block; |
502 | bool use_short_text = false; | 503 | bool use_short_text = false; |
503 | 504 | ||
@@ -505,7 +506,7 @@ static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) | |||
505 | double reserved_width = | 506 | double reserved_width = |
506 | predict_workspace_buttons_length(cairo, output) + | 507 | predict_workspace_buttons_length(cairo, output) + |
507 | predict_binding_mode_indicator_length(cairo, output) + | 508 | predict_binding_mode_indicator_length(cairo, output) + |
508 | 3 * output->scale; // require a bit of space for margin | 509 | 3; // require a bit of space for margin |
509 | 510 | ||
510 | double predicted_full_pos = | 511 | double predicted_full_pos = |
511 | predict_status_line_pos(cairo, output, *x); | 512 | predict_status_line_pos(cairo, output, *x); |
@@ -549,27 +550,27 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx, | |||
549 | cairo_t *cairo = ctx->cairo; | 550 | cairo_t *cairo = ctx->cairo; |
550 | struct swaybar_config *config = output->bar->config; | 551 | struct swaybar_config *config = output->bar->config; |
551 | int text_width, text_height; | 552 | int text_width, text_height; |
552 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 553 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, |
553 | output->scale, output->bar->mode_pango_markup, | 554 | 1, output->bar->mode_pango_markup, |
554 | "%s", mode); | 555 | "%s", mode); |
555 | 556 | ||
556 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | 557 | int ws_vertical_padding = WS_VERTICAL_PADDING; |
557 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; | 558 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING; |
558 | int border_width = BORDER_WIDTH * output->scale; | 559 | int border_width = BORDER_WIDTH; |
559 | 560 | ||
560 | uint32_t ideal_height = text_height + ws_vertical_padding * 2 | 561 | uint32_t ideal_height = text_height + ws_vertical_padding * 2 |
561 | + border_width * 2; | 562 | + border_width * 2; |
562 | uint32_t ideal_surface_height = ideal_height / output->scale; | 563 | uint32_t ideal_surface_height = ideal_height; |
563 | if (!output->bar->config->height && | 564 | if (!output->bar->config->height && |
564 | output->height < ideal_surface_height) { | 565 | output->height < ideal_surface_height) { |
565 | return ideal_surface_height; | 566 | return ideal_surface_height; |
566 | } | 567 | } |
567 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; | 568 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; |
568 | if (width < config->workspace_min_width * output->scale) { | 569 | if (width < config->workspace_min_width) { |
569 | width = config->workspace_min_width * output->scale; | 570 | width = config->workspace_min_width; |
570 | } | 571 | } |
571 | 572 | ||
572 | uint32_t height = output->height * output->scale; | 573 | uint32_t height = output->height; |
573 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | 574 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); |
574 | cairo_set_source_u32(cairo, config->colors.binding_mode.background); | 575 | cairo_set_source_u32(cairo, config->colors.binding_mode.background); |
575 | ctx->background_color = config->colors.binding_mode.background; | 576 | ctx->background_color = config->colors.binding_mode.background; |
@@ -590,17 +591,22 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx, | |||
590 | cairo_set_source_u32(cairo, config->colors.binding_mode.text); | 591 | cairo_set_source_u32(cairo, config->colors.binding_mode.text); |
591 | cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); | 592 | cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); |
592 | choose_text_aa_mode(ctx, config->colors.binding_mode.text); | 593 | choose_text_aa_mode(ctx, config->colors.binding_mode.text); |
593 | pango_printf(cairo, config->font, output->scale, | 594 | render_text(cairo, config->font_description, 1, output->bar->mode_pango_markup, |
594 | output->bar->mode_pango_markup, "%s", mode); | 595 | "%s", mode); |
595 | return output->height; | 596 | return output->height; |
596 | } | 597 | } |
597 | 598 | ||
598 | static enum hotspot_event_handling workspace_hotspot_callback( | 599 | static enum hotspot_event_handling workspace_hotspot_callback( |
599 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, | 600 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, |
600 | double x, double y, uint32_t button, void *data) { | 601 | double x, double y, uint32_t button, bool released, void *data) { |
601 | if (button != BTN_LEFT) { | 602 | if (button != BTN_LEFT) { |
602 | return HOTSPOT_PROCESS; | 603 | return HOTSPOT_PROCESS; |
603 | } | 604 | } |
605 | if (released) { | ||
606 | // Since we handle the pressed event, also handle the released event | ||
607 | // to block it from falling through to a binding in the bar | ||
608 | return HOTSPOT_IGNORE; | ||
609 | } | ||
604 | ipc_send_workspace_command(output->bar, (const char *)data); | 610 | ipc_send_workspace_command(output->bar, (const char *)data); |
605 | return HOTSPOT_IGNORE; | 611 | return HOTSPOT_IGNORE; |
606 | } | 612 | } |
@@ -620,28 +626,28 @@ static uint32_t render_workspace_button(struct render_context *ctx, | |||
620 | box_colors = config->colors.inactive_workspace; | 626 | box_colors = config->colors.inactive_workspace; |
621 | } | 627 | } |
622 | 628 | ||
623 | uint32_t height = output->height * output->scale; | 629 | uint32_t height = output->height; |
624 | 630 | ||
625 | cairo_t *cairo = ctx->cairo; | 631 | cairo_t *cairo = ctx->cairo; |
626 | int text_width, text_height; | 632 | int text_width, text_height; |
627 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 633 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, |
628 | output->scale, config->pango_markup, "%s", ws->label); | 634 | 1, config->pango_markup, "%s", ws->label); |
629 | 635 | ||
630 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | 636 | int ws_vertical_padding = WS_VERTICAL_PADDING; |
631 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; | 637 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING; |
632 | int border_width = BORDER_WIDTH * output->scale; | 638 | int border_width = BORDER_WIDTH; |
633 | 639 | ||
634 | uint32_t ideal_height = ws_vertical_padding * 2 + text_height | 640 | uint32_t ideal_height = ws_vertical_padding * 2 + text_height |
635 | + border_width * 2; | 641 | + border_width * 2; |
636 | uint32_t ideal_surface_height = ideal_height / output->scale; | 642 | uint32_t ideal_surface_height = ideal_height; |
637 | if (!output->bar->config->height && | 643 | if (!output->bar->config->height && |
638 | output->height < ideal_surface_height) { | 644 | output->height < ideal_surface_height) { |
639 | return ideal_surface_height; | 645 | return ideal_surface_height; |
640 | } | 646 | } |
641 | 647 | ||
642 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; | 648 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; |
643 | if (width < config->workspace_min_width * output->scale) { | 649 | if (width < config->workspace_min_width) { |
644 | width = config->workspace_min_width * output->scale; | 650 | width = config->workspace_min_width; |
645 | } | 651 | } |
646 | 652 | ||
647 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | 653 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); |
@@ -664,7 +670,7 @@ static uint32_t render_workspace_button(struct render_context *ctx, | |||
664 | cairo_set_source_u32(cairo, box_colors.text); | 670 | cairo_set_source_u32(cairo, box_colors.text); |
665 | cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); | 671 | cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); |
666 | choose_text_aa_mode(ctx, box_colors.text); | 672 | choose_text_aa_mode(ctx, box_colors.text); |
667 | pango_printf(cairo, config->font, output->scale, config->pango_markup, | 673 | render_text(cairo, config->font_description, 1, config->pango_markup, |
668 | "%s", ws->label); | 674 | "%s", ws->label); |
669 | 675 | ||
670 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); | 676 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); |
@@ -686,19 +692,10 @@ static uint32_t render_to_cairo(struct render_context *ctx) { | |||
686 | struct swaybar_output *output = ctx->output; | 692 | struct swaybar_output *output = ctx->output; |
687 | struct swaybar *bar = output->bar; | 693 | struct swaybar *bar = output->bar; |
688 | struct swaybar_config *config = bar->config; | 694 | struct swaybar_config *config = bar->config; |
689 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | ||
690 | if (output->focused) { | ||
691 | ctx->background_color = config->colors.focused_background; | ||
692 | } else { | ||
693 | ctx->background_color = config->colors.background; | ||
694 | } | ||
695 | |||
696 | cairo_set_source_u32(cairo, ctx->background_color); | ||
697 | cairo_paint(cairo); | ||
698 | 695 | ||
699 | int th; | 696 | int th; |
700 | get_text_size(cairo, config->font, NULL, &th, NULL, output->scale, false, ""); | 697 | get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, ""); |
701 | uint32_t max_height = (th + WS_VERTICAL_PADDING * 4) / output->scale; | 698 | uint32_t max_height = (th + WS_VERTICAL_PADDING * 4); |
702 | /* | 699 | /* |
703 | * Each render_* function takes the actual height of the bar, and returns | 700 | * Each render_* function takes the actual height of the bar, and returns |
704 | * the ideal height. If the actual height is too short, the render function | 701 | * the ideal height. If the actual height is too short, the render function |
@@ -706,7 +703,7 @@ static uint32_t render_to_cairo(struct render_context *ctx) { | |||
706 | * height is too tall, the render function should adapt its drawing to | 703 | * height is too tall, the render function should adapt its drawing to |
707 | * utilize the available space. | 704 | * utilize the available space. |
708 | */ | 705 | */ |
709 | double x = output->width * output->scale; | 706 | double x = output->width; |
710 | #if HAVE_TRAY | 707 | #if HAVE_TRAY |
711 | if (bar->tray) { | 708 | if (bar->tray) { |
712 | uint32_t h = render_tray(cairo, output, &x); | 709 | uint32_t h = render_tray(cairo, output, &x); |
@@ -756,34 +753,43 @@ void render_frame(struct swaybar_output *output) { | |||
756 | 753 | ||
757 | free_hotspots(&output->hotspots); | 754 | free_hotspots(&output->hotspots); |
758 | 755 | ||
756 | uint32_t background_color; | ||
757 | if (output->focused) { | ||
758 | background_color = output->bar->config->colors.focused_background; | ||
759 | } else { | ||
760 | background_color = output->bar->config->colors.background; | ||
761 | } | ||
762 | |||
759 | struct render_context ctx = { 0 }; | 763 | struct render_context ctx = { 0 }; |
760 | ctx.output = output; | 764 | ctx.output = output; |
765 | // initial background color used for deciding the best way to antialias text | ||
766 | ctx.background_color = background_color; | ||
761 | 767 | ||
762 | cairo_surface_t *recorder = cairo_recording_surface_create( | 768 | cairo_surface_t *recorder = cairo_recording_surface_create( |
763 | CAIRO_CONTENT_COLOR_ALPHA, NULL); | 769 | CAIRO_CONTENT_COLOR_ALPHA, NULL); |
764 | cairo_t *cairo = cairo_create(recorder); | 770 | cairo_t *cairo = cairo_create(recorder); |
771 | cairo_scale(cairo, output->scale, output->scale); | ||
765 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | 772 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); |
766 | ctx.cairo = cairo; | 773 | ctx.cairo = cairo; |
767 | 774 | ||
768 | cairo_font_options_t *fo = cairo_font_options_create(); | 775 | cairo_font_options_t *fo = cairo_font_options_create(); |
769 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
770 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); | 776 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); |
771 | ctx.textaa_safe = fo; | 777 | ctx.textaa_safe = fo; |
772 | if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { | 778 | if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { |
773 | ctx.textaa_sharp = ctx.textaa_safe; | 779 | ctx.textaa_sharp = ctx.textaa_safe; |
774 | } else { | 780 | } else { |
775 | fo = cairo_font_options_create(); | 781 | fo = cairo_font_options_create(); |
776 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
777 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | 782 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); |
778 | cairo_font_options_set_subpixel_order(fo, | 783 | cairo_font_options_set_subpixel_order(fo, |
779 | to_cairo_subpixel_order(output->subpixel)); | 784 | to_cairo_subpixel_order(output->subpixel)); |
780 | ctx.textaa_sharp = fo; | 785 | ctx.textaa_sharp = fo; |
781 | } | 786 | } |
782 | 787 | ||
783 | cairo_save(cairo); | 788 | |
784 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | 789 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); |
790 | cairo_set_source_u32(cairo, background_color); | ||
785 | cairo_paint(cairo); | 791 | cairo_paint(cairo); |
786 | cairo_restore(cairo); | 792 | |
787 | uint32_t height = render_to_cairo(&ctx); | 793 | uint32_t height = render_to_cairo(&ctx); |
788 | int config_height = output->bar->config->height; | 794 | int config_height = output->bar->config->height; |
789 | if (config_height > 0) { | 795 | if (config_height > 0) { |
@@ -810,9 +816,7 @@ void render_frame(struct swaybar_output *output) { | |||
810 | output->width * output->scale, | 816 | output->width * output->scale, |
811 | output->height * output->scale); | 817 | output->height * output->scale); |
812 | if (!output->current_buffer) { | 818 | if (!output->current_buffer) { |
813 | cairo_surface_destroy(recorder); | 819 | goto cleanup; |
814 | cairo_destroy(cairo); | ||
815 | return; | ||
816 | } | 820 | } |
817 | cairo_t *shm = output->current_buffer->cairo; | 821 | cairo_t *shm = output->current_buffer->cairo; |
818 | 822 | ||
@@ -830,6 +834,17 @@ void render_frame(struct swaybar_output *output) { | |||
830 | wl_surface_damage(output->surface, 0, 0, | 834 | wl_surface_damage(output->surface, 0, 0, |
831 | output->width, output->height); | 835 | output->width, output->height); |
832 | 836 | ||
837 | uint32_t bg_alpha = background_color & 0xFF; | ||
838 | if (bg_alpha == 0xFF) { | ||
839 | struct wl_region *region = | ||
840 | wl_compositor_create_region(output->bar->compositor); | ||
841 | wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); | ||
842 | wl_surface_set_opaque_region(output->surface, region); | ||
843 | wl_region_destroy(region); | ||
844 | } else { | ||
845 | wl_surface_set_opaque_region(output->surface, NULL); | ||
846 | } | ||
847 | |||
833 | struct wl_callback *frame_callback = wl_surface_frame(output->surface); | 848 | struct wl_callback *frame_callback = wl_surface_frame(output->surface); |
834 | wl_callback_add_listener(frame_callback, &output_frame_listener, output); | 849 | wl_callback_add_listener(frame_callback, &output_frame_listener, output); |
835 | output->frame_scheduled = true; | 850 | output->frame_scheduled = true; |
@@ -837,6 +852,7 @@ void render_frame(struct swaybar_output *output) { | |||
837 | wl_surface_commit(output->surface); | 852 | wl_surface_commit(output->surface); |
838 | } | 853 | } |
839 | 854 | ||
855 | cleanup: | ||
840 | if (ctx.textaa_sharp != ctx.textaa_safe) { | 856 | if (ctx.textaa_sharp != ctx.textaa_safe) { |
841 | cairo_font_options_destroy(ctx.textaa_sharp); | 857 | cairo_font_options_destroy(ctx.textaa_sharp); |
842 | } | 858 | } |
diff --git a/swaybar/status_line.c b/swaybar/status_line.c index ecd91032..e542e606 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <fcntl.h> | 2 | #include <fcntl.h> |
4 | #include <sys/ioctl.h> | 3 | #include <sys/ioctl.h> |
@@ -117,11 +116,11 @@ bool status_handle_readable(struct status_line *status) { | |||
117 | status->text = status->buffer; | 116 | status->text = status->buffer; |
118 | // intentional fall-through | 117 | // intentional fall-through |
119 | case PROTOCOL_TEXT: | 118 | case PROTOCOL_TEXT: |
120 | errno = 0; | ||
121 | while (true) { | 119 | while (true) { |
122 | if (status->buffer[read_bytes - 1] == '\n') { | 120 | if (status->buffer[read_bytes - 1] == '\n') { |
123 | status->buffer[read_bytes - 1] = '\0'; | 121 | status->buffer[read_bytes - 1] = '\0'; |
124 | } | 122 | } |
123 | errno = 0; | ||
125 | read_bytes = getline(&status->buffer, | 124 | read_bytes = getline(&status->buffer, |
126 | &status->buffer_size, status->read); | 125 | &status->buffer_size, status->read); |
127 | if (errno == EAGAIN) { | 126 | if (errno == EAGAIN) { |
@@ -157,7 +156,12 @@ struct status_line *status_line_init(char *cmd) { | |||
157 | assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before " | 156 | assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before " |
158 | " starting `status-command`; WAYLAND_SOCKET should not be set"); | 157 | " starting `status-command`; WAYLAND_SOCKET should not be set"); |
159 | status->pid = fork(); | 158 | status->pid = fork(); |
160 | if (status->pid == 0) { | 159 | if (status->pid < 0) { |
160 | sway_log_errno(SWAY_ERROR, "fork failed"); | ||
161 | exit(1); | ||
162 | } else if (status->pid == 0) { | ||
163 | setpgid(0, 0); | ||
164 | |||
161 | dup2(pipe_read_fd[1], STDOUT_FILENO); | 165 | dup2(pipe_read_fd[1], STDOUT_FILENO); |
162 | close(pipe_read_fd[0]); | 166 | close(pipe_read_fd[0]); |
163 | close(pipe_read_fd[1]); | 167 | close(pipe_read_fd[1]); |
@@ -185,8 +189,8 @@ struct status_line *status_line_init(char *cmd) { | |||
185 | 189 | ||
186 | void status_line_free(struct status_line *status) { | 190 | void status_line_free(struct status_line *status) { |
187 | status_line_close_fds(status); | 191 | status_line_close_fds(status); |
188 | kill(status->pid, status->cont_signal); | 192 | kill(-status->pid, status->cont_signal); |
189 | kill(status->pid, SIGTERM); | 193 | kill(-status->pid, SIGTERM); |
190 | waitpid(status->pid, NULL, 0); | 194 | waitpid(status->pid, NULL, 0); |
191 | if (status->protocol == PROTOCOL_I3BAR) { | 195 | if (status->protocol == PROTOCOL_I3BAR) { |
192 | struct i3bar_block *block, *tmp; | 196 | struct i3bar_block *block, *tmp; |
diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index ddf2416d..79b54606 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -10,6 +9,7 @@ | |||
10 | #include "swaybar/tray/tray.h" | 9 | #include "swaybar/tray/tray.h" |
11 | #include "list.h" | 10 | #include "list.h" |
12 | #include "log.h" | 11 | #include "log.h" |
12 | #include "stringop.h" | ||
13 | 13 | ||
14 | static const char *watcher_path = "/StatusNotifierWatcher"; | 14 | static const char *watcher_path = "/StatusNotifierWatcher"; |
15 | 15 | ||
@@ -138,12 +138,10 @@ static int handle_new_watcher(sd_bus_message *msg, | |||
138 | 138 | ||
139 | bool init_host(struct swaybar_host *host, char *protocol, | 139 | bool init_host(struct swaybar_host *host, char *protocol, |
140 | struct swaybar_tray *tray) { | 140 | struct swaybar_tray *tray) { |
141 | size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; | 141 | host->watcher_interface = format_str("org.%s.StatusNotifierWatcher", protocol); |
142 | host->watcher_interface = malloc(len); | ||
143 | if (!host->watcher_interface) { | 142 | if (!host->watcher_interface) { |
144 | return false; | 143 | return false; |
145 | } | 144 | } |
146 | snprintf(host->watcher_interface, len, "org.%s.StatusNotifierWatcher", protocol); | ||
147 | 145 | ||
148 | sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL; | 146 | sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL; |
149 | int ret = sd_bus_match_signal(tray->bus, ®_slot, host->watcher_interface, | 147 | int ret = sd_bus_match_signal(tray->bus, ®_slot, host->watcher_interface, |
@@ -173,13 +171,10 @@ bool init_host(struct swaybar_host *host, char *protocol, | |||
173 | } | 171 | } |
174 | 172 | ||
175 | pid_t pid = getpid(); | 173 | pid_t pid = getpid(); |
176 | size_t service_len = snprintf(NULL, 0, "org.%s.StatusNotifierHost-%d", | 174 | host->service = format_str("org.%s.StatusNotifierHost-%d", protocol, pid); |
177 | protocol, pid) + 1; | ||
178 | host->service = malloc(service_len); | ||
179 | if (!host->service) { | 175 | if (!host->service) { |
180 | goto error; | 176 | goto error; |
181 | } | 177 | } |
182 | snprintf(host->service, service_len, "org.%s.StatusNotifierHost-%d", protocol, pid); | ||
183 | ret = sd_bus_request_name(tray->bus, host->service, 0); | 178 | ret = sd_bus_request_name(tray->bus, host->service, 0); |
184 | if (ret < 0) { | 179 | if (ret < 0) { |
185 | sway_log(SWAY_DEBUG, "Failed to acquire service name: %s", strerror(-ret)); | 180 | sway_log(SWAY_DEBUG, "Failed to acquire service name: %s", strerror(-ret)); |
diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index c426c3d4..659edd86 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <dirent.h> | 2 | #include <dirent.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
@@ -40,9 +39,7 @@ static list_t *get_basedirs(void) { | |||
40 | data_dirs = strdup(data_dirs); | 39 | data_dirs = strdup(data_dirs); |
41 | char *dir = strtok(data_dirs, ":"); | 40 | char *dir = strtok(data_dirs, ":"); |
42 | do { | 41 | do { |
43 | size_t path_len = snprintf(NULL, 0, "%s/icons", dir) + 1; | 42 | char *path = format_str("%s/icons", dir); |
44 | char *path = malloc(path_len); | ||
45 | snprintf(path, path_len, "%s/icons", dir); | ||
46 | list_add(basedirs, path); | 43 | list_add(basedirs, path); |
47 | } while ((dir = strtok(NULL, ":"))); | 44 | } while ((dir = strtok(NULL, ":"))); |
48 | free(data_dirs); | 45 | free(data_dirs); |
@@ -206,13 +203,7 @@ static const char *entry_handler(char *group, char *key, char *value, | |||
206 | */ | 203 | */ |
207 | static struct icon_theme *read_theme_file(char *basedir, char *theme_name) { | 204 | static struct icon_theme *read_theme_file(char *basedir, char *theme_name) { |
208 | // look for index.theme file | 205 | // look for index.theme file |
209 | size_t path_len = snprintf(NULL, 0, "%s/%s/index.theme", basedir, | 206 | char *path = format_str("%s/%s/index.theme", basedir, theme_name); |
210 | theme_name) + 1; | ||
211 | char *path = malloc(path_len); | ||
212 | if (!path) { | ||
213 | return NULL; | ||
214 | } | ||
215 | snprintf(path, path_len, "%s/%s/index.theme", basedir, theme_name); | ||
216 | FILE *theme_file = fopen(path, "r"); | 207 | FILE *theme_file = fopen(path, "r"); |
217 | free(path); | 208 | free(path); |
218 | if (!theme_file) { | 209 | if (!theme_file) { |
@@ -416,26 +407,20 @@ static char *find_icon_in_subdir(char *name, char *basedir, char *theme, | |||
416 | #endif | 407 | #endif |
417 | }; | 408 | }; |
418 | 409 | ||
419 | size_t path_len = snprintf(NULL, 0, "%s/%s/%s/%s.EXT", basedir, theme, | ||
420 | subdir, name) + 1; | ||
421 | char *path = malloc(path_len); | ||
422 | |||
423 | for (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) { | 410 | for (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) { |
424 | snprintf(path, path_len, "%s/%s/%s/%s.%s", basedir, theme, subdir, | 411 | char *path = format_str("%s/%s/%s/%s.%s", |
425 | name, extensions[i]); | 412 | basedir, theme, subdir, name, extensions[i]); |
426 | if (access(path, R_OK) == 0) { | 413 | if (access(path, R_OK) == 0) { |
427 | return path; | 414 | return path; |
428 | } | 415 | } |
416 | free(path); | ||
429 | } | 417 | } |
430 | 418 | ||
431 | free(path); | ||
432 | return NULL; | 419 | return NULL; |
433 | } | 420 | } |
434 | 421 | ||
435 | static bool theme_exists_in_basedir(char *theme, char *basedir) { | 422 | static bool theme_exists_in_basedir(char *theme, char *basedir) { |
436 | size_t path_len = snprintf(NULL, 0, "%s/%s", basedir, theme) + 1; | 423 | char *path = format_str("%s/%s", basedir, theme); |
437 | char *path = malloc(path_len); | ||
438 | snprintf(path, path_len, "%s/%s", basedir, theme); | ||
439 | bool ret = dir_exists(path); | 424 | bool ret = dir_exists(path); |
440 | free(path); | 425 | free(path); |
441 | return ret; | 426 | return ret; |
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 19f4beac..ca6c03ad 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <arpa/inet.h> | 1 | #include <arpa/inet.h> |
3 | #include <cairo.h> | 2 | #include <cairo.h> |
4 | #include <limits.h> | 3 | #include <limits.h> |
@@ -7,12 +6,12 @@ | |||
7 | #include <string.h> | 6 | #include <string.h> |
8 | #include "swaybar/bar.h" | 7 | #include "swaybar/bar.h" |
9 | #include "swaybar/config.h" | 8 | #include "swaybar/config.h" |
9 | #include "swaybar/image.h" | ||
10 | #include "swaybar/input.h" | 10 | #include "swaybar/input.h" |
11 | #include "swaybar/tray/host.h" | 11 | #include "swaybar/tray/host.h" |
12 | #include "swaybar/tray/icon.h" | 12 | #include "swaybar/tray/icon.h" |
13 | #include "swaybar/tray/item.h" | 13 | #include "swaybar/tray/item.h" |
14 | #include "swaybar/tray/tray.h" | 14 | #include "swaybar/tray/tray.h" |
15 | #include "background-image.h" | ||
16 | #include "cairo_util.h" | 15 | #include "cairo_util.h" |
17 | #include "list.h" | 16 | #include "list.h" |
18 | #include "log.h" | 17 | #include "log.h" |
@@ -385,13 +384,18 @@ static int cmp_sni_id(const void *item, const void *cmp_to) { | |||
385 | 384 | ||
386 | static enum hotspot_event_handling icon_hotspot_callback( | 385 | static enum hotspot_event_handling icon_hotspot_callback( |
387 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, | 386 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, |
388 | double x, double y, uint32_t button, void *data) { | 387 | double x, double y, uint32_t button, bool released, void *data) { |
389 | sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data); | 388 | sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data); |
390 | 389 | ||
391 | struct swaybar_tray *tray = output->bar->tray; | 390 | struct swaybar_tray *tray = output->bar->tray; |
392 | int idx = list_seq_find(tray->items, cmp_sni_id, data); | 391 | int idx = list_seq_find(tray->items, cmp_sni_id, data); |
393 | 392 | ||
394 | if (idx != -1) { | 393 | if (idx != -1) { |
394 | if (released) { | ||
395 | // Since we handle the pressed event, also handle the released event | ||
396 | // to block it from falling through to a binding in the bar | ||
397 | return HOTSPOT_IGNORE; | ||
398 | } | ||
395 | struct swaybar_sni *sni = tray->items->items[idx]; | 399 | struct swaybar_sni *sni = tray->items->items[idx]; |
396 | // guess global position since wayland doesn't expose it | 400 | // guess global position since wayland doesn't expose it |
397 | struct swaybar_config *config = tray->bar->config; | 401 | struct swaybar_config *config = tray->bar->config; |
@@ -426,7 +430,7 @@ static void reload_sni(struct swaybar_sni *sni, char *icon_theme, | |||
426 | list_free(icon_search_paths); | 430 | list_free(icon_search_paths); |
427 | if (icon_path) { | 431 | if (icon_path) { |
428 | cairo_surface_destroy(sni->icon); | 432 | cairo_surface_destroy(sni->icon); |
429 | sni->icon = load_background_image(icon_path); | 433 | sni->icon = load_image(icon_path); |
430 | free(icon_path); | 434 | free(icon_path); |
431 | return; | 435 | return; |
432 | } | 436 | } |
@@ -466,6 +470,11 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, | |||
466 | sni->target_size = target_size; | 470 | sni->target_size = target_size; |
467 | } | 471 | } |
468 | 472 | ||
473 | // Passive | ||
474 | if (sni->status && sni->status[0] == 'P') { | ||
475 | return 0; | ||
476 | } | ||
477 | |||
469 | int icon_size; | 478 | int icon_size; |
470 | cairo_surface_t *icon; | 479 | cairo_surface_t *icon; |
471 | if (sni->icon) { | 480 | if (sni->icon) { |
@@ -493,24 +502,36 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, | |||
493 | cairo_destroy(cairo_icon); | 502 | cairo_destroy(cairo_icon); |
494 | } | 503 | } |
495 | 504 | ||
496 | int padded_size = icon_size + 2*padding; | 505 | double descaled_padding = (double)padding / output->scale; |
497 | *x -= padded_size; | 506 | double descaled_icon_size = (double)icon_size / output->scale; |
498 | int y = floor((height - padded_size) / 2.0); | 507 | |
508 | int size = descaled_icon_size + 2 * descaled_padding; | ||
509 | *x -= size; | ||
510 | int icon_y = floor((output->height - size) / 2.0); | ||
499 | 511 | ||
500 | cairo_operator_t op = cairo_get_operator(cairo); | 512 | cairo_operator_t op = cairo_get_operator(cairo); |
501 | cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); | 513 | cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); |
502 | cairo_set_source_surface(cairo, icon, *x + padding, y + padding); | 514 | |
503 | cairo_rectangle(cairo, *x, y, padded_size, padded_size); | 515 | cairo_matrix_t scale_matrix; |
516 | cairo_pattern_t *icon_pattern = cairo_pattern_create_for_surface(icon); | ||
517 | // TODO: check cairo_pattern_status for "ENOMEM" | ||
518 | cairo_matrix_init_scale(&scale_matrix, output->scale, output->scale); | ||
519 | cairo_matrix_translate(&scale_matrix, -(*x + descaled_padding), -(icon_y + descaled_padding)); | ||
520 | cairo_pattern_set_matrix(icon_pattern, &scale_matrix); | ||
521 | cairo_set_source(cairo, icon_pattern); | ||
522 | cairo_rectangle(cairo, *x, icon_y, size, size); | ||
504 | cairo_fill(cairo); | 523 | cairo_fill(cairo); |
524 | |||
505 | cairo_set_operator(cairo, op); | 525 | cairo_set_operator(cairo, op); |
506 | 526 | ||
527 | cairo_pattern_destroy(icon_pattern); | ||
507 | cairo_surface_destroy(icon); | 528 | cairo_surface_destroy(icon); |
508 | 529 | ||
509 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); | 530 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); |
510 | hotspot->x = *x; | 531 | hotspot->x = *x; |
511 | hotspot->y = 0; | 532 | hotspot->y = 0; |
512 | hotspot->width = height; | 533 | hotspot->width = size; |
513 | hotspot->height = height; | 534 | hotspot->height = output->height; |
514 | hotspot->callback = icon_hotspot_callback; | 535 | hotspot->callback = icon_hotspot_callback; |
515 | hotspot->destroy = free; | 536 | hotspot->destroy = free; |
516 | hotspot->data = strdup(sni->watcher_id); | 537 | hotspot->data = strdup(sni->watcher_id); |
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index 5fe6f9c3..b0545f4a 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c | |||
@@ -116,8 +116,8 @@ uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) { | |||
116 | } | 116 | } |
117 | } // else display on all | 117 | } // else display on all |
118 | 118 | ||
119 | if ((int) output->height*output->scale <= 2*config->tray_padding) { | 119 | if ((int)(output->height * output->scale) <= 2 * config->tray_padding) { |
120 | return 2*config->tray_padding + 1; | 120 | return (2 * config->tray_padding + 1) / output->scale; |
121 | } | 121 | } |
122 | 122 | ||
123 | uint32_t max_height = 0; | 123 | uint32_t max_height = 0; |
diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 16afc27c..3cfea8d8 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <stddef.h> | 2 | #include <stddef.h> |
4 | #include <stdio.h> | 3 | #include <stdio.h> |
@@ -6,6 +5,7 @@ | |||
6 | #include <string.h> | 5 | #include <string.h> |
7 | #include "list.h" | 6 | #include "list.h" |
8 | #include "log.h" | 7 | #include "log.h" |
8 | #include "stringop.h" | ||
9 | #include "swaybar/tray/watcher.h" | 9 | #include "swaybar/tray/watcher.h" |
10 | 10 | ||
11 | static const char *obj_path = "/StatusNotifierWatcher"; | 11 | static const char *obj_path = "/StatusNotifierWatcher"; |
@@ -76,9 +76,7 @@ static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) { | |||
76 | service = service_or_path; | 76 | service = service_or_path; |
77 | path = "/StatusNotifierItem"; | 77 | path = "/StatusNotifierItem"; |
78 | } | 78 | } |
79 | size_t id_len = snprintf(NULL, 0, "%s%s", service, path) + 1; | 79 | id = format_str("%s%s", service, path); |
80 | id = malloc(id_len); | ||
81 | snprintf(id, id_len, "%s%s", service, path); | ||
82 | } | 80 | } |
83 | 81 | ||
84 | if (list_seq_find(watcher->items, cmp_id, id) == -1) { | 82 | if (list_seq_find(watcher->items, cmp_id, id) == -1) { |
@@ -107,7 +105,7 @@ static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) { | |||
107 | sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); | 105 | sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); |
108 | list_add(watcher->hosts, strdup(service)); | 106 | list_add(watcher->hosts, strdup(service)); |
109 | sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, | 107 | sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, |
110 | "StatusNotifierHostRegistered", "s", service); | 108 | "StatusNotifierHostRegistered", ""); |
111 | } else { | 109 | } else { |
112 | sway_log(SWAY_DEBUG, "Status Notifier Host '%s' already registered", service); | 110 | sway_log(SWAY_DEBUG, "Status Notifier Host '%s' already registered", service); |
113 | } | 111 | } |
@@ -159,9 +157,7 @@ struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) { | |||
159 | return NULL; | 157 | return NULL; |
160 | } | 158 | } |
161 | 159 | ||
162 | size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; | 160 | watcher->interface = format_str("org.%s.StatusNotifierWatcher", protocol); |
163 | watcher->interface = malloc(len); | ||
164 | snprintf(watcher->interface, len, "org.%s.StatusNotifierWatcher", protocol); | ||
165 | 161 | ||
166 | sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL; | 162 | sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL; |
167 | int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path, | 163 | int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path, |
diff --git a/swaymsg/main.c b/swaymsg/main.c index 574d3b75..573a7b16 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | |
2 | #include <limits.h> | ||
2 | #include <stdio.h> | 3 | #include <stdio.h> |
3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
4 | #include <string.h> | 5 | #include <string.h> |
@@ -58,7 +59,7 @@ static void pretty_print_cmd(json_object *r) { | |||
58 | if (!success_object(r)) { | 59 | if (!success_object(r)) { |
59 | json_object *error; | 60 | json_object *error; |
60 | if (!json_object_object_get_ex(r, "error", &error)) { | 61 | if (!json_object_object_get_ex(r, "error", &error)) { |
61 | printf("An unknkown error occurred"); | 62 | printf("An unknown error occurred"); |
62 | } else { | 63 | } else { |
63 | printf("Error: %s\n", json_object_get_string(error)); | 64 | printf("Error: %s\n", json_object_get_string(error)); |
64 | } | 65 | } |
@@ -183,12 +184,14 @@ static void pretty_print_seat(json_object *i) { | |||
183 | } | 184 | } |
184 | 185 | ||
185 | static void pretty_print_output(json_object *o) { | 186 | static void pretty_print_output(json_object *o) { |
186 | json_object *name, *rect, *focused, *active, *ws, *current_mode; | 187 | json_object *name, *rect, *focused, *active, *power, *ws, *current_mode, *non_desktop; |
187 | json_object_object_get_ex(o, "name", &name); | 188 | json_object_object_get_ex(o, "name", &name); |
188 | json_object_object_get_ex(o, "rect", &rect); | 189 | json_object_object_get_ex(o, "rect", &rect); |
189 | json_object_object_get_ex(o, "focused", &focused); | 190 | json_object_object_get_ex(o, "focused", &focused); |
190 | json_object_object_get_ex(o, "active", &active); | 191 | json_object_object_get_ex(o, "active", &active); |
192 | json_object_object_get_ex(o, "power", &power); | ||
191 | json_object_object_get_ex(o, "current_workspace", &ws); | 193 | json_object_object_get_ex(o, "current_workspace", &ws); |
194 | json_object_object_get_ex(o, "non_desktop", &non_desktop); | ||
192 | json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, | 195 | json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, |
193 | *transform, *max_render_time, *adaptive_sync_status; | 196 | *transform, *max_render_time, *adaptive_sync_status; |
194 | json_object_object_get_ex(o, "make", &make); | 197 | json_object_object_get_ex(o, "make", &make); |
@@ -211,10 +214,19 @@ static void pretty_print_output(json_object *o) { | |||
211 | json_object_object_get_ex(current_mode, "height", &height); | 214 | json_object_object_get_ex(current_mode, "height", &height); |
212 | json_object_object_get_ex(current_mode, "refresh", &refresh); | 215 | json_object_object_get_ex(current_mode, "refresh", &refresh); |
213 | 216 | ||
214 | if (json_object_get_boolean(active)) { | 217 | if (json_object_get_boolean(non_desktop)) { |
218 | printf( | ||
219 | "Output %s '%s %s %s' (non-desktop)\n", | ||
220 | json_object_get_string(name), | ||
221 | json_object_get_string(make), | ||
222 | json_object_get_string(model), | ||
223 | json_object_get_string(serial) | ||
224 | ); | ||
225 | } else if (json_object_get_boolean(active)) { | ||
215 | printf( | 226 | printf( |
216 | "Output %s '%s %s %s'%s\n" | 227 | "Output %s '%s %s %s'%s\n" |
217 | " Current mode: %dx%d @ %.3f Hz\n" | 228 | " Current mode: %dx%d @ %.3f Hz\n" |
229 | " Power: %s\n" | ||
218 | " Position: %d,%d\n" | 230 | " Position: %d,%d\n" |
219 | " Scale factor: %f\n" | 231 | " Scale factor: %f\n" |
220 | " Scale filter: %s\n" | 232 | " Scale filter: %s\n" |
@@ -229,6 +241,7 @@ static void pretty_print_output(json_object *o) { | |||
229 | json_object_get_int(width), | 241 | json_object_get_int(width), |
230 | json_object_get_int(height), | 242 | json_object_get_int(height), |
231 | (double)json_object_get_int(refresh) / 1000, | 243 | (double)json_object_get_int(refresh) / 1000, |
244 | json_object_get_boolean(power) ? "on" : "off", | ||
232 | json_object_get_int(x), json_object_get_int(y), | 245 | json_object_get_int(x), json_object_get_int(y), |
233 | json_object_get_double(scale), | 246 | json_object_get_double(scale), |
234 | json_object_get_string(scale_filter), | 247 | json_object_get_string(scale_filter), |
@@ -245,7 +258,7 @@ static void pretty_print_output(json_object *o) { | |||
245 | json_object_get_string(adaptive_sync_status)); | 258 | json_object_get_string(adaptive_sync_status)); |
246 | } else { | 259 | } else { |
247 | printf( | 260 | printf( |
248 | "Output %s '%s %s %s' (inactive)\n", | 261 | "Output %s '%s %s %s' (disabled)\n", |
249 | json_object_get_string(name), | 262 | json_object_get_string(name), |
250 | json_object_get_string(make), | 263 | json_object_get_string(make), |
251 | json_object_get_string(model), | 264 | json_object_get_string(model), |
@@ -260,14 +273,22 @@ static void pretty_print_output(json_object *o) { | |||
260 | for (size_t i = 0; i < modes_len; ++i) { | 273 | for (size_t i = 0; i < modes_len; ++i) { |
261 | json_object *mode = json_object_array_get_idx(modes, i); | 274 | json_object *mode = json_object_array_get_idx(modes, i); |
262 | 275 | ||
263 | json_object *mode_width, *mode_height, *mode_refresh; | 276 | json_object *mode_width, *mode_height, *mode_refresh, |
277 | *mode_picture_aspect_ratio; | ||
264 | json_object_object_get_ex(mode, "width", &mode_width); | 278 | json_object_object_get_ex(mode, "width", &mode_width); |
265 | json_object_object_get_ex(mode, "height", &mode_height); | 279 | json_object_object_get_ex(mode, "height", &mode_height); |
266 | json_object_object_get_ex(mode, "refresh", &mode_refresh); | 280 | json_object_object_get_ex(mode, "refresh", &mode_refresh); |
281 | json_object_object_get_ex(mode, "picture_aspect_ratio", | ||
282 | &mode_picture_aspect_ratio); | ||
267 | 283 | ||
268 | printf(" %dx%d @ %.3f Hz\n", json_object_get_int(mode_width), | 284 | printf(" %dx%d @ %.3f Hz", json_object_get_int(mode_width), |
269 | json_object_get_int(mode_height), | 285 | json_object_get_int(mode_height), |
270 | (double)json_object_get_int(mode_refresh) / 1000); | 286 | (double)json_object_get_int(mode_refresh) / 1000); |
287 | if (mode_picture_aspect_ratio && | ||
288 | strcmp("none", json_object_get_string(mode_picture_aspect_ratio)) != 0) { | ||
289 | printf(" (%s)", json_object_get_string(mode_picture_aspect_ratio)); | ||
290 | } | ||
291 | printf("\n"); | ||
271 | } | 292 | } |
272 | } | 293 | } |
273 | 294 | ||
@@ -286,28 +307,83 @@ static void pretty_print_config(json_object *c) { | |||
286 | printf("%s\n", json_object_get_string(config)); | 307 | printf("%s\n", json_object_get_string(config)); |
287 | } | 308 | } |
288 | 309 | ||
289 | static void pretty_print(int type, json_object *resp) { | 310 | static void pretty_print_tree(json_object *obj, int indent) { |
290 | if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && | 311 | for (int i = 0; i < indent; i++) { |
291 | type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && | 312 | printf(" "); |
292 | type != IPC_GET_VERSION && type != IPC_GET_SEATS && | ||
293 | type != IPC_GET_CONFIG && type != IPC_SEND_TICK) { | ||
294 | printf("%s\n", json_object_to_json_string_ext(resp, | ||
295 | JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); | ||
296 | return; | ||
297 | } | 313 | } |
298 | 314 | ||
299 | if (type == IPC_SEND_TICK) { | 315 | int id = json_object_get_int(json_object_object_get(obj, "id")); |
300 | return; | 316 | const char *name = json_object_get_string(json_object_object_get(obj, "name")); |
317 | const char *type = json_object_get_string(json_object_object_get(obj, "type")); | ||
318 | const char *shell = json_object_get_string(json_object_object_get(obj, "shell")); | ||
319 | |||
320 | printf("#%d: %s \"%s\"", id, type, name); | ||
321 | |||
322 | if (shell != NULL) { | ||
323 | int pid = json_object_get_int(json_object_object_get(obj, "pid")); | ||
324 | const char *app_id = json_object_get_string(json_object_object_get(obj, "app_id")); | ||
325 | json_object *window_props_obj = json_object_object_get(obj, "window_properties"); | ||
326 | const char *instance = json_object_get_string(json_object_object_get(window_props_obj, "instance")); | ||
327 | const char *class = json_object_get_string(json_object_object_get(window_props_obj, "class")); | ||
328 | int x11_id = json_object_get_int(json_object_object_get(obj, "window")); | ||
329 | |||
330 | printf(" (%s, pid: %d", shell, pid); | ||
331 | if (app_id != NULL) { | ||
332 | printf(", app_id: \"%s\"", app_id); | ||
333 | } | ||
334 | if (instance != NULL) { | ||
335 | printf(", instance: \"%s\"", instance); | ||
336 | } | ||
337 | if (class != NULL) { | ||
338 | printf(", class: \"%s\"", class); | ||
339 | } | ||
340 | if (x11_id != 0) { | ||
341 | printf(", X11 window: 0x%X", x11_id); | ||
342 | } | ||
343 | printf(")"); | ||
301 | } | 344 | } |
302 | 345 | ||
303 | if (type == IPC_GET_VERSION) { | 346 | printf("\n"); |
304 | pretty_print_version(resp); | 347 | |
305 | return; | 348 | json_object *nodes_obj = json_object_object_get(obj, "nodes"); |
349 | size_t len = json_object_array_length(nodes_obj); | ||
350 | for (size_t i = 0; i < len; i++) { | ||
351 | pretty_print_tree(json_object_array_get_idx(nodes_obj, i), indent + 1); | ||
306 | } | 352 | } |
307 | 353 | ||
308 | if (type == IPC_GET_CONFIG) { | 354 | json_object *floating_nodes_obj; |
355 | json_bool floating_nodes = json_object_object_get_ex(obj, "floating_nodes", &floating_nodes_obj); | ||
356 | if (floating_nodes) { | ||
357 | size_t len = json_object_array_length(floating_nodes_obj); | ||
358 | for (size_t i = 0; i < len; i++) { | ||
359 | pretty_print_tree(json_object_array_get_idx(floating_nodes_obj, i), indent + 1); | ||
360 | } | ||
361 | } | ||
362 | } | ||
363 | |||
364 | static void pretty_print(int type, json_object *resp) { | ||
365 | switch (type) { | ||
366 | case IPC_SEND_TICK: | ||
367 | return; | ||
368 | case IPC_GET_VERSION: | ||
369 | pretty_print_version(resp); | ||
370 | return; | ||
371 | case IPC_GET_CONFIG: | ||
309 | pretty_print_config(resp); | 372 | pretty_print_config(resp); |
310 | return; | 373 | return; |
374 | case IPC_GET_TREE: | ||
375 | pretty_print_tree(resp, 0); | ||
376 | return; | ||
377 | case IPC_COMMAND: | ||
378 | case IPC_GET_WORKSPACES: | ||
379 | case IPC_GET_INPUTS: | ||
380 | case IPC_GET_OUTPUTS: | ||
381 | case IPC_GET_SEATS: | ||
382 | break; | ||
383 | default: | ||
384 | printf("%s\n", json_object_to_json_string_ext(resp, | ||
385 | JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); | ||
386 | return; | ||
311 | } | 387 | } |
312 | 388 | ||
313 | json_object *obj; | 389 | json_object *obj; |
@@ -480,12 +556,20 @@ int main(int argc, char **argv) { | |||
480 | char *resp = ipc_single_command(socketfd, type, command, &len); | 556 | char *resp = ipc_single_command(socketfd, type, command, &len); |
481 | 557 | ||
482 | // pretty print the json | 558 | // pretty print the json |
483 | json_object *obj = json_tokener_parse(resp); | 559 | json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH); |
484 | if (obj == NULL) { | 560 | if (tok == NULL) { |
561 | if (quiet) { | ||
562 | exit(EXIT_FAILURE); | ||
563 | } | ||
564 | sway_abort("failed allocating json_tokener"); | ||
565 | } | ||
566 | json_object *obj = json_tokener_parse_ex(tok, resp, -1); | ||
567 | enum json_tokener_error err = json_tokener_get_error(tok); | ||
568 | json_tokener_free(tok); | ||
569 | if (obj == NULL || err != json_tokener_success) { | ||
485 | if (!quiet) { | 570 | if (!quiet) { |
486 | fprintf(stderr, "ERROR: Could not parse json response from ipc. " | 571 | sway_log(SWAY_ERROR, "failed to parse payload as json: %s", |
487 | "This is a bug in sway."); | 572 | json_tokener_error_desc(err)); |
488 | printf("%s\n", resp); | ||
489 | } | 573 | } |
490 | ret = 1; | 574 | ret = 1; |
491 | } else { | 575 | } else { |
@@ -517,13 +601,22 @@ int main(int argc, char **argv) { | |||
517 | break; | 601 | break; |
518 | } | 602 | } |
519 | 603 | ||
520 | json_object *obj = json_tokener_parse(reply->payload); | 604 | json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH); |
521 | if (obj == NULL) { | 605 | if (tok == NULL) { |
606 | if (quiet) { | ||
607 | exit(EXIT_FAILURE); | ||
608 | } | ||
609 | sway_abort("failed allocating json_tokener"); | ||
610 | } | ||
611 | json_object *obj = json_tokener_parse_ex(tok, reply->payload, -1); | ||
612 | enum json_tokener_error err = json_tokener_get_error(tok); | ||
613 | json_tokener_free(tok); | ||
614 | if (obj == NULL || err != json_tokener_success) { | ||
522 | if (!quiet) { | 615 | if (!quiet) { |
523 | fprintf(stderr, "ERROR: Could not parse json response from" | 616 | sway_log(SWAY_ERROR, "failed to parse payload as json: %s", |
524 | " ipc. This is a bug in sway."); | 617 | json_tokener_error_desc(err)); |
525 | ret = 1; | ||
526 | } | 618 | } |
619 | ret = 1; | ||
527 | break; | 620 | break; |
528 | } else if (quiet) { | 621 | } else if (quiet) { |
529 | json_object_put(obj); | 622 | json_object_put(obj); |
diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index b69013b5..abee1bb9 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd | |||
@@ -21,12 +21,13 @@ _swaymsg_ [options...] [message] | |||
21 | 21 | ||
22 | *-p, --pretty* | 22 | *-p, --pretty* |
23 | Use pretty output even when not using a tty. | 23 | Use pretty output even when not using a tty. |
24 | Not available for all message types. | ||
24 | 25 | ||
25 | *-q, --quiet* | 26 | *-q, --quiet* |
26 | Sends the IPC message but does not print the response from sway. | 27 | Sends the IPC message but does not print the response from sway. |
27 | 28 | ||
28 | *-r, --raw* | 29 | *-r, --raw* |
29 | Use raw output even if using a tty. | 30 | Use raw JSON output even if using a tty. |
30 | 31 | ||
31 | *-s, --socket* <path> | 32 | *-s, --socket* <path> |
32 | Use the specified socket path. Otherwise, swaymsg will ask sway where the | 33 | Use the specified socket path. Otherwise, swaymsg will ask sway where the |
@@ -46,6 +47,11 @@ _swaymsg_ [options...] [message] | |||
46 | 47 | ||
47 | See *sway*(5) for a list of commands. | 48 | See *sway*(5) for a list of commands. |
48 | 49 | ||
50 | _swaymsg_ can return pretty printed (standalone-default) or JSON-formatted | ||
51 | (*--raw*) output. For detailed documentation on the returned JSON-data of | ||
52 | each message type listed below, refer to *sway-ipc*(7). The JSON-format can | ||
53 | contain more information than the pretty print. | ||
54 | |||
49 | Tips: | 55 | Tips: |
50 | - Command expansion is performed twice: once by swaymsg, and again by sway. | 56 | - Command expansion is performed twice: once by swaymsg, and again by sway. |
51 | If you have quoted multi-word strings in your command, enclose the entire | 57 | If you have quoted multi-word strings in your command, enclose the entire |
@@ -60,20 +66,20 @@ _swaymsg_ [options...] [message] | |||
60 | _swaymsg -- mark --add test_ instead of _swaymsg mark --add test_. | 66 | _swaymsg -- mark --add test_ instead of _swaymsg mark --add test_. |
61 | 67 | ||
62 | *get\_workspaces* | 68 | *get\_workspaces* |
63 | Gets a JSON-encoded list of workspaces and their status. | 69 | Gets a list of workspaces and their status. |
64 | 70 | ||
65 | *get\_inputs* | 71 | *get\_inputs* |
66 | Gets a JSON-encoded list of current inputs. | 72 | Gets a list of current inputs. |
67 | 73 | ||
68 | *get\_outputs* | 74 | *get\_outputs* |
69 | Gets a JSON-encoded list of current outputs. | 75 | Gets a list of current outputs. |
70 | 76 | ||
71 | *get\_tree* | 77 | *get\_tree* |
72 | Gets a JSON-encoded layout tree of all open windows, containers, outputs, | 78 | Gets a JSON-encoded layout tree of all open windows, containers, outputs, |
73 | workspaces, and so on. | 79 | workspaces, and so on. |
74 | 80 | ||
75 | *get\_seats* | 81 | *get\_seats* |
76 | Gets a JSON-encoded list of all seats, | 82 | Gets a list of all seats, |
77 | its properties and all assigned devices. | 83 | its properties and all assigned devices. |
78 | 84 | ||
79 | *get\_marks* | 85 | *get\_marks* |
@@ -83,7 +89,7 @@ _swaymsg_ [options...] [message] | |||
83 | Get a JSON-encoded configuration for swaybar. | 89 | Get a JSON-encoded configuration for swaybar. |
84 | 90 | ||
85 | *get\_version* | 91 | *get\_version* |
86 | Get JSON-encoded version information for the running instance of sway. | 92 | Get version information for the running instance of sway. |
87 | 93 | ||
88 | *get\_binding\_modes* | 94 | *get\_binding\_modes* |
89 | Gets a JSON-encoded list of currently configured binding modes. | 95 | Gets a JSON-encoded list of currently configured binding modes. |
@@ -92,7 +98,7 @@ _swaymsg_ [options...] [message] | |||
92 | Gets JSON-encoded info about the current binding state. | 98 | Gets JSON-encoded info about the current binding state. |
93 | 99 | ||
94 | *get\_config* | 100 | *get\_config* |
95 | Gets a JSON-encoded copy of the current configuration. | 101 | Gets a copy of the current configuration. Doesn't expand includes. |
96 | 102 | ||
97 | *send\_tick* | 103 | *send\_tick* |
98 | Sends a tick event to all subscribed clients. | 104 | Sends a tick event to all subscribed clients. |
@@ -101,6 +107,8 @@ _swaymsg_ [options...] [message] | |||
101 | Subscribe to a list of event types. The argument for this type should be | 107 | Subscribe to a list of event types. The argument for this type should be |
102 | provided in the form of a valid JSON array. If any of the types are invalid | 108 | provided in the form of a valid JSON array. If any of the types are invalid |
103 | or if a valid JSON array is not provided, this will result in a failure. | 109 | or if a valid JSON array is not provided, this will result in a failure. |
110 | For a list of valid event types and the data returned with them refer to | ||
111 | *sway-ipc*(7). | ||
104 | 112 | ||
105 | # RETURN CODES | 113 | # RETURN CODES |
106 | 114 | ||
diff --git a/swaynag/config.c b/swaynag/config.c index 6db7cce5..efd71ce7 100644 --- a/swaynag/config.c +++ b/swaynag/config.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <getopt.h> | 1 | #include <getopt.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -11,24 +10,40 @@ | |||
11 | #include "util.h" | 10 | #include "util.h" |
12 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 11 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
13 | 12 | ||
14 | static char *read_from_stdin(void) { | 13 | static char *read_and_trim_stdin(void) { |
15 | char *buffer = NULL; | 14 | char *buffer = NULL, *line = NULL; |
16 | size_t buffer_len = 0; | 15 | size_t buffer_len = 0, line_size = 0; |
17 | char *line = NULL; | 16 | while (1) { |
18 | size_t line_size = 0; | 17 | ssize_t nread = getline(&line, &line_size, stdin); |
19 | ssize_t nread; | 18 | if (nread == -1) { |
20 | while ((nread = getline(&line, &line_size, stdin)) != -1) { | 19 | if (feof(stdin)) { |
20 | break; | ||
21 | } else { | ||
22 | perror("getline"); | ||
23 | goto freeline; | ||
24 | } | ||
25 | } | ||
21 | buffer = realloc(buffer, buffer_len + nread + 1); | 26 | buffer = realloc(buffer, buffer_len + nread + 1); |
22 | snprintf(&buffer[buffer_len], nread + 1, "%s", line); | 27 | if (!buffer) { |
28 | perror("realloc"); | ||
29 | goto freebuf; | ||
30 | } | ||
31 | memcpy(&buffer[buffer_len], line, nread + 1); | ||
23 | buffer_len += nread; | 32 | buffer_len += nread; |
24 | } | 33 | } |
25 | free(line); | 34 | free(line); |
26 | 35 | ||
27 | while (buffer && buffer[buffer_len - 1] == '\n') { | 36 | while (buffer_len && buffer[buffer_len - 1] == '\n') { |
28 | buffer[--buffer_len] = '\0'; | 37 | buffer[--buffer_len] = '\0'; |
29 | } | 38 | } |
30 | 39 | ||
31 | return buffer; | 40 | return buffer; |
41 | |||
42 | freeline: | ||
43 | free(line); | ||
44 | freebuf: | ||
45 | free(buffer); | ||
46 | return NULL; | ||
32 | } | 47 | } |
33 | 48 | ||
34 | int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | 49 | int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, |
@@ -150,8 +165,11 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
150 | fprintf(stderr, "Missing action for button %s\n", optarg); | 165 | fprintf(stderr, "Missing action for button %s\n", optarg); |
151 | return EXIT_FAILURE; | 166 | return EXIT_FAILURE; |
152 | } | 167 | } |
153 | struct swaynag_button *button; | 168 | struct swaynag_button *button = calloc(1, sizeof(struct swaynag_button)); |
154 | button = calloc(sizeof(struct swaynag_button), 1); | 169 | if (!button) { |
170 | perror("calloc"); | ||
171 | return EXIT_FAILURE; | ||
172 | } | ||
155 | button->text = strdup(optarg); | 173 | button->text = strdup(optarg); |
156 | button->type = SWAYNAG_ACTION_COMMAND; | 174 | button->type = SWAYNAG_ACTION_COMMAND; |
157 | button->action = strdup(argv[optind]); | 175 | button->action = strdup(argv[optind]); |
@@ -207,22 +225,25 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
207 | break; | 225 | break; |
208 | case 'f': // Font | 226 | case 'f': // Font |
209 | if (type) { | 227 | if (type) { |
210 | free(type->font); | 228 | pango_font_description_free(type->font_description); |
211 | type->font = strdup(optarg); | 229 | type->font_description = pango_font_description_from_string(optarg); |
212 | } | 230 | } |
213 | break; | 231 | break; |
214 | case 'l': // Detailed Message | 232 | case 'l': // Detailed Message |
215 | if (swaynag) { | 233 | if (swaynag) { |
216 | free(swaynag->details.message); | 234 | free(swaynag->details.message); |
217 | swaynag->details.message = read_from_stdin(); | 235 | swaynag->details.message = read_and_trim_stdin(); |
236 | if (!swaynag->details.message) { | ||
237 | return EXIT_FAILURE; | ||
238 | } | ||
218 | swaynag->details.button_up.text = strdup("â–²"); | 239 | swaynag->details.button_up.text = strdup("â–²"); |
219 | swaynag->details.button_down.text = strdup("â–¼"); | 240 | swaynag->details.button_down.text = strdup("â–¼"); |
220 | } | 241 | } |
221 | break; | 242 | break; |
222 | case 'L': // Detailed Button Text | 243 | case 'L': // Detailed Button Text |
223 | if (swaynag) { | 244 | if (swaynag) { |
224 | free(swaynag->details.button_details->text); | 245 | free(swaynag->details.details_text); |
225 | swaynag->details.button_details->text = strdup(optarg); | 246 | swaynag->details.details_text = strdup(optarg); |
226 | } | 247 | } |
227 | break; | 248 | break; |
228 | case 'm': // Message | 249 | case 'm': // Message |
@@ -239,8 +260,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
239 | break; | 260 | break; |
240 | case 's': // Dismiss Button Text | 261 | case 's': // Dismiss Button Text |
241 | if (swaynag) { | 262 | if (swaynag) { |
242 | struct swaynag_button *button_close; | 263 | struct swaynag_button *button_close = swaynag->buttons->items[0]; |
243 | button_close = swaynag->buttons->items[0]; | ||
244 | free(button_close->text); | 264 | free(button_close->text); |
245 | button_close->text = strdup(optarg); | 265 | button_close->text = strdup(optarg); |
246 | } | 266 | } |
@@ -399,23 +419,24 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) { | |||
399 | 419 | ||
400 | if (line[0] == '[') { | 420 | if (line[0] == '[') { |
401 | char *close = strchr(line, ']'); | 421 | char *close = strchr(line, ']'); |
402 | if (!close) { | 422 | if (!close || close != &line[nread - 2] || nread <= 3) { |
403 | fprintf(stderr, "Closing bracket not found on line %d\n", | 423 | fprintf(stderr, "Line %d is malformed\n", line_number); |
404 | line_number); | ||
405 | result = 1; | 424 | result = 1; |
406 | break; | 425 | break; |
407 | } | 426 | } |
408 | char *name = calloc(1, close - line); | 427 | *close = '\0'; |
409 | strncat(name, line + 1, close - line - 1); | 428 | type = swaynag_type_get(types, &line[1]); |
410 | type = swaynag_type_get(types, name); | ||
411 | if (!type) { | 429 | if (!type) { |
412 | type = swaynag_type_new(name); | 430 | type = swaynag_type_new(&line[1]); |
413 | list_add(types, type); | 431 | list_add(types, type); |
414 | } | 432 | } |
415 | free(name); | ||
416 | } else { | 433 | } else { |
417 | char *flag = malloc(sizeof(char) * (nread + 3)); | 434 | char *flag = malloc(nread + 3); |
418 | sprintf(flag, "--%s", line); | 435 | if (!flag) { |
436 | perror("calloc"); | ||
437 | return EXIT_FAILURE; | ||
438 | } | ||
439 | snprintf(flag, nread + 3, "--%s", line); | ||
419 | char *argv[] = {"swaynag", flag}; | 440 | char *argv[] = {"swaynag", flag}; |
420 | result = swaynag_parse_options(2, argv, swaynag, types, type, | 441 | result = swaynag_parse_options(2, argv, swaynag, types, type, |
421 | NULL, NULL); | 442 | NULL, NULL); |
diff --git a/swaynag/main.c b/swaynag/main.c index 88007818..634bddbf 100644 --- a/swaynag/main.c +++ b/swaynag/main.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <signal.h> | 2 | #include <signal.h> |
4 | #include "log.h" | 3 | #include "log.h" |
@@ -20,33 +19,27 @@ void sway_terminate(int code) { | |||
20 | } | 19 | } |
21 | 20 | ||
22 | int main(int argc, char **argv) { | 21 | int main(int argc, char **argv) { |
23 | int exit_code = EXIT_SUCCESS; | 22 | int status = EXIT_SUCCESS; |
24 | 23 | ||
25 | list_t *types = create_list(); | 24 | list_t *types = create_list(); |
26 | swaynag_types_add_default(types); | 25 | swaynag_types_add_default(types); |
27 | 26 | ||
28 | memset(&swaynag, 0, sizeof(swaynag)); | ||
29 | swaynag.buttons = create_list(); | 27 | swaynag.buttons = create_list(); |
30 | wl_list_init(&swaynag.outputs); | 28 | wl_list_init(&swaynag.outputs); |
31 | wl_list_init(&swaynag.seats); | 29 | wl_list_init(&swaynag.seats); |
32 | 30 | ||
33 | struct swaynag_button *button_close = | 31 | struct swaynag_button *button_close = calloc(1, sizeof(struct swaynag_button)); |
34 | calloc(sizeof(struct swaynag_button), 1); | ||
35 | button_close->text = strdup("X"); | 32 | button_close->text = strdup("X"); |
36 | button_close->type = SWAYNAG_ACTION_DISMISS; | 33 | button_close->type = SWAYNAG_ACTION_DISMISS; |
37 | list_add(swaynag.buttons, button_close); | 34 | list_add(swaynag.buttons, button_close); |
38 | 35 | ||
39 | swaynag.details.button_details = | 36 | swaynag.details.details_text = strdup("Toggle details"); |
40 | calloc(sizeof(struct swaynag_button), 1); | ||
41 | swaynag.details.button_details->text = strdup("Toggle details"); | ||
42 | swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND; | ||
43 | 37 | ||
44 | char *config_path = NULL; | 38 | char *config_path = NULL; |
45 | bool debug = false; | 39 | bool debug = false; |
46 | int launch_status = swaynag_parse_options(argc, argv, NULL, NULL, NULL, | 40 | status = swaynag_parse_options(argc, argv, NULL, NULL, NULL, |
47 | &config_path, &debug); | 41 | &config_path, &debug); |
48 | if (launch_status != 0) { | 42 | if (status != 0) { |
49 | exit_code = launch_status; | ||
50 | goto cleanup; | 43 | goto cleanup; |
51 | } | 44 | } |
52 | sway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL); | 45 | sway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL); |
@@ -56,29 +49,27 @@ int main(int argc, char **argv) { | |||
56 | } | 49 | } |
57 | if (config_path) { | 50 | if (config_path) { |
58 | sway_log(SWAY_DEBUG, "Loading config file: %s", config_path); | 51 | sway_log(SWAY_DEBUG, "Loading config file: %s", config_path); |
59 | int config_status = swaynag_load_config(config_path, &swaynag, types); | 52 | status = swaynag_load_config(config_path, &swaynag, types); |
60 | free(config_path); | 53 | if (status != 0) { |
61 | if (config_status != 0) { | ||
62 | exit_code = config_status; | ||
63 | goto cleanup; | 54 | goto cleanup; |
64 | } | 55 | } |
65 | } | 56 | } |
66 | 57 | ||
58 | |||
67 | if (argc > 1) { | 59 | if (argc > 1) { |
68 | struct swaynag_type *type_args = swaynag_type_new("<args>"); | 60 | struct swaynag_type *type_args = swaynag_type_new("<args>"); |
69 | list_add(types, type_args); | 61 | list_add(types, type_args); |
70 | 62 | ||
71 | int result = swaynag_parse_options(argc, argv, &swaynag, types, | 63 | status = swaynag_parse_options(argc, argv, &swaynag, types, |
72 | type_args, NULL, NULL); | 64 | type_args, NULL, NULL); |
73 | if (result != 0) { | 65 | if (status != 0) { |
74 | exit_code = result; | ||
75 | goto cleanup; | 66 | goto cleanup; |
76 | } | 67 | } |
77 | } | 68 | } |
78 | 69 | ||
79 | if (!swaynag.message) { | 70 | if (!swaynag.message) { |
80 | sway_log(SWAY_ERROR, "No message passed. Please provide --message/-m"); | 71 | sway_log(SWAY_ERROR, "No message passed. Please provide --message/-m"); |
81 | exit_code = EXIT_FAILURE; | 72 | status = EXIT_FAILURE; |
82 | goto cleanup; | 73 | goto cleanup; |
83 | } | 74 | } |
84 | 75 | ||
@@ -96,20 +87,20 @@ int main(int argc, char **argv) { | |||
96 | swaynag_type_merge(type, swaynag_type_get(types, "<args>")); | 87 | swaynag_type_merge(type, swaynag_type_get(types, "<args>")); |
97 | swaynag.type = type; | 88 | swaynag.type = type; |
98 | 89 | ||
99 | swaynag_types_free(types); | ||
100 | |||
101 | if (swaynag.details.message) { | 90 | if (swaynag.details.message) { |
91 | swaynag.details.button_details = calloc(1, sizeof(struct swaynag_button)); | ||
92 | swaynag.details.button_details->text = strdup(swaynag.details.details_text); | ||
93 | swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND; | ||
102 | list_add(swaynag.buttons, swaynag.details.button_details); | 94 | list_add(swaynag.buttons, swaynag.details.button_details); |
103 | } else { | ||
104 | free(swaynag.details.button_details->text); | ||
105 | free(swaynag.details.button_details); | ||
106 | } | 95 | } |
107 | 96 | ||
108 | sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output); | 97 | sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output); |
109 | sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors); | 98 | sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors); |
110 | sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name); | 99 | sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name); |
111 | sway_log(SWAY_DEBUG, "Message: %s", swaynag.message); | 100 | sway_log(SWAY_DEBUG, "Message: %s", swaynag.message); |
112 | sway_log(SWAY_DEBUG, "Font: %s", swaynag.type->font); | 101 | char *font = pango_font_description_to_string(swaynag.type->font_description); |
102 | sway_log(SWAY_DEBUG, "Font: %s", font); | ||
103 | free(font); | ||
113 | sway_log(SWAY_DEBUG, "Buttons"); | 104 | sway_log(SWAY_DEBUG, "Buttons"); |
114 | for (int i = 0; i < swaynag.buttons->length; i++) { | 105 | for (int i = 0; i < swaynag.buttons->length; i++) { |
115 | struct swaynag_button *button = swaynag.buttons->items[i]; | 106 | struct swaynag_button *button = swaynag.buttons->items[i]; |
@@ -120,12 +111,9 @@ int main(int argc, char **argv) { | |||
120 | 111 | ||
121 | swaynag_setup(&swaynag); | 112 | swaynag_setup(&swaynag); |
122 | swaynag_run(&swaynag); | 113 | swaynag_run(&swaynag); |
123 | return exit_code; | ||
124 | 114 | ||
125 | cleanup: | 115 | cleanup: |
126 | swaynag_types_free(types); | 116 | swaynag_types_free(types); |
127 | free(swaynag.details.button_details->text); | ||
128 | free(swaynag.details.button_details); | ||
129 | swaynag_destroy(&swaynag); | 117 | swaynag_destroy(&swaynag); |
130 | return exit_code; | 118 | return status; |
131 | } | 119 | } |
diff --git a/swaynag/meson.build b/swaynag/meson.build index 71f2fc2d..aef21483 100644 --- a/swaynag/meson.build +++ b/swaynag/meson.build | |||
@@ -5,13 +5,14 @@ executable( | |||
5 | 'render.c', | 5 | 'render.c', |
6 | 'swaynag.c', | 6 | 'swaynag.c', |
7 | 'types.c', | 7 | 'types.c', |
8 | wl_protos_src, | ||
8 | ], | 9 | ], |
9 | include_directories: [sway_inc], | 10 | include_directories: [sway_inc], |
10 | dependencies: [ | 11 | dependencies: [ |
11 | cairo, | 12 | cairo, |
12 | client_protos, | ||
13 | pango, | 13 | pango, |
14 | pangocairo, | 14 | pangocairo, |
15 | rt, | ||
15 | wayland_client, | 16 | wayland_client, |
16 | wayland_cursor, | 17 | wayland_cursor, |
17 | ], | 18 | ], |
diff --git a/swaynag/render.c b/swaynag/render.c index 2a7f869a..21b03289 100644 --- a/swaynag/render.c +++ b/swaynag/render.c | |||
@@ -9,20 +9,20 @@ | |||
9 | 9 | ||
10 | static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { | 10 | static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { |
11 | int text_width, text_height; | 11 | int text_width, text_height; |
12 | get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, | 12 | get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, |
13 | swaynag->scale, true, "%s", swaynag->message); | 13 | 1, true, "%s", swaynag->message); |
14 | 14 | ||
15 | int padding = swaynag->type->message_padding * swaynag->scale; | 15 | int padding = swaynag->type->message_padding; |
16 | 16 | ||
17 | uint32_t ideal_height = text_height + padding * 2; | 17 | uint32_t ideal_height = text_height + padding * 2; |
18 | uint32_t ideal_surface_height = ideal_height / swaynag->scale; | 18 | uint32_t ideal_surface_height = ideal_height; |
19 | if (swaynag->height < ideal_surface_height) { | 19 | if (swaynag->height < ideal_surface_height) { |
20 | return ideal_surface_height; | 20 | return ideal_surface_height; |
21 | } | 21 | } |
22 | 22 | ||
23 | cairo_set_source_u32(cairo, swaynag->type->text); | 23 | cairo_set_source_u32(cairo, swaynag->type->text); |
24 | cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); | 24 | cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); |
25 | pango_printf(cairo, swaynag->type->font, swaynag->scale, false, | 25 | render_text(cairo, swaynag->type->font_description, 1, false, |
26 | "%s", swaynag->message); | 26 | "%s", swaynag->message); |
27 | 27 | ||
28 | return ideal_surface_height; | 28 | return ideal_surface_height; |
@@ -31,11 +31,11 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { | |||
31 | static void render_details_scroll_button(cairo_t *cairo, | 31 | static void render_details_scroll_button(cairo_t *cairo, |
32 | struct swaynag *swaynag, struct swaynag_button *button) { | 32 | struct swaynag *swaynag, struct swaynag_button *button) { |
33 | int text_width, text_height; | 33 | int text_width, text_height; |
34 | get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, | 34 | get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, |
35 | swaynag->scale, true, "%s", button->text); | 35 | 1, true, "%s", button->text); |
36 | 36 | ||
37 | int border = swaynag->type->button_border_thickness * swaynag->scale; | 37 | int border = swaynag->type->button_border_thickness; |
38 | int padding = swaynag->type->button_padding * swaynag->scale; | 38 | int padding = swaynag->type->button_padding; |
39 | 39 | ||
40 | cairo_set_source_u32(cairo, swaynag->type->details_background); | 40 | cairo_set_source_u32(cairo, swaynag->type->details_background); |
41 | cairo_rectangle(cairo, button->x, button->y, | 41 | cairo_rectangle(cairo, button->x, button->y, |
@@ -50,41 +50,41 @@ static void render_details_scroll_button(cairo_t *cairo, | |||
50 | cairo_set_source_u32(cairo, swaynag->type->button_text); | 50 | cairo_set_source_u32(cairo, swaynag->type->button_text); |
51 | cairo_move_to(cairo, button->x + border + padding, | 51 | cairo_move_to(cairo, button->x + border + padding, |
52 | button->y + border + (button->height - text_height) / 2); | 52 | button->y + border + (button->height - text_height) / 2); |
53 | pango_printf(cairo, swaynag->type->font, swaynag->scale, true, | 53 | render_text(cairo, swaynag->type->font_description, 1, true, |
54 | "%s", button->text); | 54 | "%s", button->text); |
55 | } | 55 | } |
56 | 56 | ||
57 | static int get_detailed_scroll_button_width(cairo_t *cairo, | 57 | static int get_detailed_scroll_button_width(cairo_t *cairo, |
58 | struct swaynag *swaynag) { | 58 | struct swaynag *swaynag) { |
59 | int up_width, down_width, temp_height; | 59 | int up_width, down_width, temp_height; |
60 | get_text_size(cairo, swaynag->type->font, &up_width, &temp_height, NULL, | 60 | get_text_size(cairo, swaynag->type->font_description, &up_width, &temp_height, NULL, |
61 | swaynag->scale, true, | 61 | 1, true, |
62 | "%s", swaynag->details.button_up.text); | 62 | "%s", swaynag->details.button_up.text); |
63 | get_text_size(cairo, swaynag->type->font, &down_width, &temp_height, NULL, | 63 | get_text_size(cairo, swaynag->type->font_description, &down_width, &temp_height, NULL, |
64 | swaynag->scale, true, | 64 | 1, true, |
65 | "%s", swaynag->details.button_down.text); | 65 | "%s", swaynag->details.button_down.text); |
66 | 66 | ||
67 | int text_width = up_width > down_width ? up_width : down_width; | 67 | int text_width = up_width > down_width ? up_width : down_width; |
68 | int border = swaynag->type->button_border_thickness * swaynag->scale; | 68 | int border = swaynag->type->button_border_thickness; |
69 | int padding = swaynag->type->button_padding * swaynag->scale; | 69 | int padding = swaynag->type->button_padding; |
70 | 70 | ||
71 | return text_width + border * 2 + padding * 2; | 71 | return text_width + border * 2 + padding * 2; |
72 | } | 72 | } |
73 | 73 | ||
74 | static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, | 74 | static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, |
75 | uint32_t y) { | 75 | uint32_t y) { |
76 | uint32_t width = swaynag->width * swaynag->scale; | 76 | uint32_t width = swaynag->width; |
77 | 77 | ||
78 | int border = swaynag->type->details_border_thickness * swaynag->scale; | 78 | int border = swaynag->type->details_border_thickness; |
79 | int padding = swaynag->type->message_padding * swaynag->scale; | 79 | int padding = swaynag->type->message_padding; |
80 | int decor = padding + border; | 80 | int decor = padding + border; |
81 | 81 | ||
82 | swaynag->details.x = decor; | 82 | swaynag->details.x = decor; |
83 | swaynag->details.y = y * swaynag->scale + decor; | 83 | swaynag->details.y = y + decor; |
84 | swaynag->details.width = width - decor * 2; | 84 | swaynag->details.width = width - decor * 2; |
85 | 85 | ||
86 | PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font, | 86 | PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font_description, |
87 | swaynag->details.message, swaynag->scale, false); | 87 | swaynag->details.message, 1, false); |
88 | pango_layout_set_width(layout, | 88 | pango_layout_set_width(layout, |
89 | (swaynag->details.width - padding * 2) * PANGO_SCALE); | 89 | (swaynag->details.width - padding * 2) * PANGO_SCALE); |
90 | pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); | 90 | pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); |
@@ -164,7 +164,7 @@ static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, | |||
164 | pango_cairo_show_layout(cairo, layout); | 164 | pango_cairo_show_layout(cairo, layout); |
165 | g_object_unref(layout); | 165 | g_object_unref(layout); |
166 | 166 | ||
167 | return ideal_height / swaynag->scale; | 167 | return ideal_height; |
168 | } | 168 | } |
169 | 169 | ||
170 | static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, | 170 | static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, |
@@ -172,14 +172,14 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, | |||
172 | struct swaynag_button *button = swaynag->buttons->items[button_index]; | 172 | struct swaynag_button *button = swaynag->buttons->items[button_index]; |
173 | 173 | ||
174 | int text_width, text_height; | 174 | int text_width, text_height; |
175 | get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, | 175 | get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, |
176 | swaynag->scale, true, "%s", button->text); | 176 | 1, true, "%s", button->text); |
177 | 177 | ||
178 | int border = swaynag->type->button_border_thickness * swaynag->scale; | 178 | int border = swaynag->type->button_border_thickness; |
179 | int padding = swaynag->type->button_padding * swaynag->scale; | 179 | int padding = swaynag->type->button_padding; |
180 | 180 | ||
181 | uint32_t ideal_height = text_height + padding * 2 + border * 2; | 181 | uint32_t ideal_height = text_height + padding * 2 + border * 2; |
182 | uint32_t ideal_surface_height = ideal_height / swaynag->scale; | 182 | uint32_t ideal_surface_height = ideal_height; |
183 | if (swaynag->height < ideal_surface_height) { | 183 | if (swaynag->height < ideal_surface_height) { |
184 | return ideal_surface_height; | 184 | return ideal_surface_height; |
185 | } | 185 | } |
@@ -201,7 +201,7 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, | |||
201 | 201 | ||
202 | cairo_set_source_u32(cairo, swaynag->type->button_text); | 202 | cairo_set_source_u32(cairo, swaynag->type->button_text); |
203 | cairo_move_to(cairo, button->x + padding, button->y + padding); | 203 | cairo_move_to(cairo, button->x + padding, button->y + padding); |
204 | pango_printf(cairo, swaynag->type->font, swaynag->scale, true, | 204 | render_text(cairo, swaynag->type->font_description, 1, true, |
205 | "%s", button->text); | 205 | "%s", button->text); |
206 | 206 | ||
207 | *x = button->x - border; | 207 | *x = button->x - border; |
@@ -220,13 +220,12 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) { | |||
220 | max_height = h > max_height ? h : max_height; | 220 | max_height = h > max_height ? h : max_height; |
221 | 221 | ||
222 | int x = swaynag->width - swaynag->type->button_margin_right; | 222 | int x = swaynag->width - swaynag->type->button_margin_right; |
223 | x *= swaynag->scale; | ||
224 | for (int i = 0; i < swaynag->buttons->length; i++) { | 223 | for (int i = 0; i < swaynag->buttons->length; i++) { |
225 | h = render_button(cairo, swaynag, i, &x); | 224 | h = render_button(cairo, swaynag, i, &x); |
226 | max_height = h > max_height ? h : max_height; | 225 | max_height = h > max_height ? h : max_height; |
227 | x -= swaynag->type->button_gap * swaynag->scale; | 226 | x -= swaynag->type->button_gap; |
228 | if (i == 0) { | 227 | if (i == 0) { |
229 | x -= swaynag->type->button_gap_close * swaynag->scale; | 228 | x -= swaynag->type->button_gap_close; |
230 | } | 229 | } |
231 | } | 230 | } |
232 | 231 | ||
@@ -235,14 +234,14 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) { | |||
235 | max_height = h > max_height ? h : max_height; | 234 | max_height = h > max_height ? h : max_height; |
236 | } | 235 | } |
237 | 236 | ||
238 | int border = swaynag->type->bar_border_thickness * swaynag->scale; | 237 | int border = swaynag->type->bar_border_thickness; |
239 | if (max_height > swaynag->height) { | 238 | if (max_height > swaynag->height) { |
240 | max_height += border; | 239 | max_height += border; |
241 | } | 240 | } |
242 | cairo_set_source_u32(cairo, swaynag->type->border_bottom); | 241 | cairo_set_source_u32(cairo, swaynag->type->border_bottom); |
243 | cairo_rectangle(cairo, 0, | 242 | cairo_rectangle(cairo, 0, |
244 | swaynag->height * swaynag->scale - border, | 243 | swaynag->height - border, |
245 | swaynag->width * swaynag->scale, | 244 | swaynag->width, |
246 | border); | 245 | border); |
247 | cairo_fill(cairo); | 246 | cairo_fill(cairo); |
248 | 247 | ||
@@ -257,6 +256,7 @@ void render_frame(struct swaynag *swaynag) { | |||
257 | cairo_surface_t *recorder = cairo_recording_surface_create( | 256 | cairo_surface_t *recorder = cairo_recording_surface_create( |
258 | CAIRO_CONTENT_COLOR_ALPHA, NULL); | 257 | CAIRO_CONTENT_COLOR_ALPHA, NULL); |
259 | cairo_t *cairo = cairo_create(recorder); | 258 | cairo_t *cairo = cairo_create(recorder); |
259 | cairo_scale(cairo, swaynag->scale, swaynag->scale); | ||
260 | cairo_save(cairo); | 260 | cairo_save(cairo); |
261 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | 261 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); |
262 | cairo_paint(cairo); | 262 | cairo_paint(cairo); |
diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index dd17c0b0..50eea148 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <assert.h> | 2 | #include <assert.h> |
4 | #include <sys/stat.h> | 3 | #include <sys/stat.h> |
@@ -28,8 +27,13 @@ static bool terminal_execute(char *terminal, char *command) { | |||
28 | fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command); | 27 | fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command); |
29 | fclose(tmp); | 28 | fclose(tmp); |
30 | chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR); | 29 | chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR); |
31 | char *cmd = malloc(sizeof(char) * (strlen(terminal) + strlen(" -e ") + strlen(fname) + 1)); | 30 | size_t cmd_size = strlen(terminal) + strlen(" -e ") + strlen(fname) + 1; |
32 | sprintf(cmd, "%s -e %s", terminal, fname); | 31 | char *cmd = malloc(cmd_size); |
32 | if (!cmd) { | ||
33 | perror("malloc"); | ||
34 | return false; | ||
35 | } | ||
36 | snprintf(cmd, cmd_size, "%s -e %s", terminal, fname); | ||
33 | execlp("sh", "sh", "-c", cmd, NULL); | 37 | execlp("sh", "sh", "-c", cmd, NULL); |
34 | sway_log_errno(SWAY_ERROR, "Failed to run command, execlp() returned."); | 38 | sway_log_errno(SWAY_ERROR, "Failed to run command, execlp() returned."); |
35 | free(cmd); | 39 | free(cmd); |
@@ -58,7 +62,7 @@ static void swaynag_button_execute(struct swaynag *swaynag, | |||
58 | } else if (pid == 0) { | 62 | } else if (pid == 0) { |
59 | // Child of the child. Will be reparented to the init process | 63 | // Child of the child. Will be reparented to the init process |
60 | char *terminal = getenv("TERMINAL"); | 64 | char *terminal = getenv("TERMINAL"); |
61 | if (button->terminal && terminal && strlen(terminal)) { | 65 | if (button->terminal && terminal && *terminal) { |
62 | sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal); | 66 | sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal); |
63 | if (!terminal_execute(terminal, button->action)) { | 67 | if (!terminal_execute(terminal, button->action)) { |
64 | swaynag_destroy(swaynag); | 68 | swaynag_destroy(swaynag); |
@@ -138,7 +142,7 @@ static void update_cursor(struct swaynag_seat *seat) { | |||
138 | const char *cursor_theme = getenv("XCURSOR_THEME"); | 142 | const char *cursor_theme = getenv("XCURSOR_THEME"); |
139 | unsigned cursor_size = 24; | 143 | unsigned cursor_size = 24; |
140 | const char *env_cursor_size = getenv("XCURSOR_SIZE"); | 144 | const char *env_cursor_size = getenv("XCURSOR_SIZE"); |
141 | if (env_cursor_size && strlen(env_cursor_size) > 0) { | 145 | if (env_cursor_size && *env_cursor_size) { |
142 | errno = 0; | 146 | errno = 0; |
143 | char *end; | 147 | char *end; |
144 | unsigned size = strtoul(env_cursor_size, &end, 10); | 148 | unsigned size = strtoul(env_cursor_size, &end, 10); |
@@ -148,8 +152,15 @@ static void update_cursor(struct swaynag_seat *seat) { | |||
148 | } | 152 | } |
149 | pointer->cursor_theme = wl_cursor_theme_load( | 153 | pointer->cursor_theme = wl_cursor_theme_load( |
150 | cursor_theme, cursor_size * swaynag->scale, swaynag->shm); | 154 | cursor_theme, cursor_size * swaynag->scale, swaynag->shm); |
151 | struct wl_cursor *cursor = | 155 | if (!pointer->cursor_theme) { |
152 | wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); | 156 | sway_log(SWAY_ERROR, "Failed to load cursor theme"); |
157 | return; | ||
158 | } | ||
159 | struct wl_cursor *cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default"); | ||
160 | if (!cursor) { | ||
161 | sway_log(SWAY_ERROR, "Failed to get default cursor from theme"); | ||
162 | return; | ||
163 | } | ||
153 | pointer->cursor_image = cursor->images[0]; | 164 | pointer->cursor_image = cursor->images[0]; |
154 | wl_surface_set_buffer_scale(pointer->cursor_surface, | 165 | wl_surface_set_buffer_scale(pointer->cursor_surface, |
155 | swaynag->scale); | 166 | swaynag->scale); |
@@ -177,11 +188,22 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | |||
177 | uint32_t serial, struct wl_surface *surface, | 188 | uint32_t serial, struct wl_surface *surface, |
178 | wl_fixed_t surface_x, wl_fixed_t surface_y) { | 189 | wl_fixed_t surface_x, wl_fixed_t surface_y) { |
179 | struct swaynag_seat *seat = data; | 190 | struct swaynag_seat *seat = data; |
191 | |||
180 | struct swaynag_pointer *pointer = &seat->pointer; | 192 | struct swaynag_pointer *pointer = &seat->pointer; |
181 | pointer->x = wl_fixed_to_int(surface_x); | 193 | pointer->x = wl_fixed_to_int(surface_x); |
182 | pointer->y = wl_fixed_to_int(surface_y); | 194 | pointer->y = wl_fixed_to_int(surface_y); |
183 | pointer->serial = serial; | 195 | |
184 | update_cursor(seat); | 196 | if (seat->swaynag->cursor_shape_manager) { |
197 | struct wp_cursor_shape_device_v1 *device = | ||
198 | wp_cursor_shape_manager_v1_get_pointer( | ||
199 | seat->swaynag->cursor_shape_manager, wl_pointer); | ||
200 | wp_cursor_shape_device_v1_set_shape(device, serial, | ||
201 | WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); | ||
202 | wp_cursor_shape_device_v1_destroy(device); | ||
203 | } else { | ||
204 | pointer->serial = serial; | ||
205 | update_cursor(seat); | ||
206 | } | ||
185 | } | 207 | } |
186 | 208 | ||
187 | static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, | 209 | static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, |
@@ -200,8 +222,8 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, | |||
200 | return; | 222 | return; |
201 | } | 223 | } |
202 | 224 | ||
203 | double x = seat->pointer.x * swaynag->scale; | 225 | double x = seat->pointer.x; |
204 | double y = seat->pointer.y * swaynag->scale; | 226 | double y = seat->pointer.y; |
205 | for (int i = 0; i < swaynag->buttons->length; i++) { | 227 | for (int i = 0; i < swaynag->buttons->length; i++) { |
206 | struct swaynag_button *nagbutton = swaynag->buttons->items[i]; | 228 | struct swaynag_button *nagbutton = swaynag->buttons->items[i]; |
207 | if (x >= nagbutton->x | 229 | if (x >= nagbutton->x |
@@ -307,33 +329,25 @@ static void output_scale(void *data, struct wl_output *output, | |||
307 | } | 329 | } |
308 | } | 330 | } |
309 | 331 | ||
310 | static const struct wl_output_listener output_listener = { | 332 | static void output_name(void *data, struct wl_output *output, |
311 | .geometry = nop, | 333 | const char *name) { |
312 | .mode = nop, | ||
313 | .done = nop, | ||
314 | .scale = output_scale, | ||
315 | }; | ||
316 | |||
317 | static void xdg_output_handle_name(void *data, | ||
318 | struct zxdg_output_v1 *xdg_output, const char *name) { | ||
319 | struct swaynag_output *swaynag_output = data; | 334 | struct swaynag_output *swaynag_output = data; |
320 | char *outname = swaynag_output->swaynag->type->output; | 335 | swaynag_output->name = strdup(name); |
321 | sway_log(SWAY_DEBUG, "Checking against output %s for %s", name, outname); | 336 | |
322 | if (!swaynag_output->swaynag->output && outname && name | 337 | const char *outname = swaynag_output->swaynag->type->output; |
323 | && strcmp(outname, name) == 0) { | 338 | if (!swaynag_output->swaynag->output && outname && |
339 | strcmp(outname, name) == 0) { | ||
324 | sway_log(SWAY_DEBUG, "Using output %s", name); | 340 | sway_log(SWAY_DEBUG, "Using output %s", name); |
325 | swaynag_output->swaynag->output = swaynag_output; | 341 | swaynag_output->swaynag->output = swaynag_output; |
326 | } | 342 | } |
327 | swaynag_output->name = strdup(name); | ||
328 | zxdg_output_v1_destroy(xdg_output); | ||
329 | swaynag_output->swaynag->querying_outputs--; | ||
330 | } | 343 | } |
331 | 344 | ||
332 | static const struct zxdg_output_v1_listener xdg_output_listener = { | 345 | static const struct wl_output_listener output_listener = { |
333 | .logical_position = nop, | 346 | .geometry = nop, |
334 | .logical_size = nop, | 347 | .mode = nop, |
335 | .done = nop, | 348 | .done = nop, |
336 | .name = xdg_output_handle_name, | 349 | .scale = output_scale, |
350 | .name = output_name, | ||
337 | .description = nop, | 351 | .description = nop, |
338 | }; | 352 | }; |
339 | 353 | ||
@@ -347,6 +361,7 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
347 | struct swaynag_seat *seat = | 361 | struct swaynag_seat *seat = |
348 | calloc(1, sizeof(struct swaynag_seat)); | 362 | calloc(1, sizeof(struct swaynag_seat)); |
349 | if (!seat) { | 363 | if (!seat) { |
364 | perror("calloc"); | ||
350 | return; | 365 | return; |
351 | } | 366 | } |
352 | 367 | ||
@@ -361,33 +376,28 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
361 | } else if (strcmp(interface, wl_shm_interface.name) == 0) { | 376 | } else if (strcmp(interface, wl_shm_interface.name) == 0) { |
362 | swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); | 377 | swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); |
363 | } else if (strcmp(interface, wl_output_interface.name) == 0) { | 378 | } else if (strcmp(interface, wl_output_interface.name) == 0) { |
364 | if (!swaynag->output && swaynag->xdg_output_manager) { | 379 | if (!swaynag->output) { |
365 | swaynag->querying_outputs++; | ||
366 | struct swaynag_output *output = | 380 | struct swaynag_output *output = |
367 | calloc(1, sizeof(struct swaynag_output)); | 381 | calloc(1, sizeof(struct swaynag_output)); |
382 | if (!output) { | ||
383 | perror("calloc"); | ||
384 | return; | ||
385 | } | ||
368 | output->wl_output = wl_registry_bind(registry, name, | 386 | output->wl_output = wl_registry_bind(registry, name, |
369 | &wl_output_interface, 3); | 387 | &wl_output_interface, 4); |
370 | output->wl_name = name; | 388 | output->wl_name = name; |
371 | output->scale = 1; | 389 | output->scale = 1; |
372 | output->swaynag = swaynag; | 390 | output->swaynag = swaynag; |
373 | wl_list_insert(&swaynag->outputs, &output->link); | 391 | wl_list_insert(&swaynag->outputs, &output->link); |
374 | wl_output_add_listener(output->wl_output, | 392 | wl_output_add_listener(output->wl_output, |
375 | &output_listener, output); | 393 | &output_listener, output); |
376 | |||
377 | struct zxdg_output_v1 *xdg_output; | ||
378 | xdg_output = zxdg_output_manager_v1_get_xdg_output( | ||
379 | swaynag->xdg_output_manager, output->wl_output); | ||
380 | zxdg_output_v1_add_listener(xdg_output, | ||
381 | &xdg_output_listener, output); | ||
382 | } | 394 | } |
383 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { | 395 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { |
384 | swaynag->layer_shell = wl_registry_bind( | 396 | swaynag->layer_shell = wl_registry_bind( |
385 | registry, name, &zwlr_layer_shell_v1_interface, 1); | 397 | registry, name, &zwlr_layer_shell_v1_interface, 1); |
386 | } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 | 398 | } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { |
387 | && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { | 399 | swaynag->cursor_shape_manager = wl_registry_bind( |
388 | swaynag->xdg_output_manager = wl_registry_bind(registry, name, | 400 | registry, name, &wp_cursor_shape_manager_v1_interface, 1); |
389 | &zxdg_output_manager_v1_interface, | ||
390 | ZXDG_OUTPUT_V1_NAME_SINCE_VERSION); | ||
391 | } | 401 | } |
392 | } | 402 | } |
393 | 403 | ||
@@ -453,12 +463,11 @@ void swaynag_setup(struct swaynag *swaynag) { | |||
453 | 463 | ||
454 | assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm); | 464 | assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm); |
455 | 465 | ||
456 | while (swaynag->querying_outputs > 0) { | 466 | // Second roundtrip to get wl_output properties |
457 | if (wl_display_roundtrip(swaynag->display) < 0) { | 467 | if (wl_display_roundtrip(swaynag->display) < 0) { |
458 | sway_log(SWAY_ERROR, "Error during outputs init."); | 468 | sway_log(SWAY_ERROR, "Error during outputs init."); |
459 | swaynag_destroy(swaynag); | 469 | swaynag_destroy(swaynag); |
460 | exit(EXIT_FAILURE); | 470 | exit(EXIT_FAILURE); |
461 | } | ||
462 | } | 471 | } |
463 | 472 | ||
464 | if (!swaynag->output && swaynag->type->output) { | 473 | if (!swaynag->output && swaynag->type->output) { |
@@ -467,7 +476,9 @@ void swaynag_setup(struct swaynag *swaynag) { | |||
467 | exit(EXIT_FAILURE); | 476 | exit(EXIT_FAILURE); |
468 | } | 477 | } |
469 | 478 | ||
470 | swaynag_setup_cursors(swaynag); | 479 | if (!swaynag->cursor_shape_manager) { |
480 | swaynag_setup_cursors(swaynag); | ||
481 | } | ||
471 | 482 | ||
472 | swaynag->surface = wl_compositor_create_surface(swaynag->compositor); | 483 | swaynag->surface = wl_compositor_create_surface(swaynag->compositor); |
473 | assert(swaynag->surface); | 484 | assert(swaynag->surface); |
@@ -494,10 +505,6 @@ void swaynag_run(struct swaynag *swaynag) { | |||
494 | && wl_display_dispatch(swaynag->display) != -1) { | 505 | && wl_display_dispatch(swaynag->display) != -1) { |
495 | // This is intentionally left blank | 506 | // This is intentionally left blank |
496 | } | 507 | } |
497 | |||
498 | if (swaynag->display) { | ||
499 | wl_display_disconnect(swaynag->display); | ||
500 | } | ||
501 | } | 508 | } |
502 | 509 | ||
503 | void swaynag_destroy(struct swaynag *swaynag) { | 510 | void swaynag_destroy(struct swaynag *swaynag) { |
@@ -512,6 +519,7 @@ void swaynag_destroy(struct swaynag *swaynag) { | |||
512 | } | 519 | } |
513 | list_free(swaynag->buttons); | 520 | list_free(swaynag->buttons); |
514 | free(swaynag->details.message); | 521 | free(swaynag->details.message); |
522 | free(swaynag->details.details_text); | ||
515 | free(swaynag->details.button_up.text); | 523 | free(swaynag->details.button_up.text); |
516 | free(swaynag->details.button_down.text); | 524 | free(swaynag->details.button_down.text); |
517 | 525 | ||
@@ -532,13 +540,8 @@ void swaynag_destroy(struct swaynag *swaynag) { | |||
532 | swaynag_seat_destroy(seat); | 540 | swaynag_seat_destroy(seat); |
533 | } | 541 | } |
534 | 542 | ||
535 | if (&swaynag->buffers[0]) { | 543 | destroy_buffer(&swaynag->buffers[0]); |
536 | destroy_buffer(&swaynag->buffers[0]); | 544 | destroy_buffer(&swaynag->buffers[1]); |
537 | } | ||
538 | |||
539 | if (&swaynag->buffers[1]) { | ||
540 | destroy_buffer(&swaynag->buffers[1]); | ||
541 | } | ||
542 | 545 | ||
543 | if (swaynag->outputs.prev || swaynag->outputs.next) { | 546 | if (swaynag->outputs.prev || swaynag->outputs.next) { |
544 | struct swaynag_output *output, *temp; | 547 | struct swaynag_output *output, *temp; |
@@ -557,4 +560,8 @@ void swaynag_destroy(struct swaynag *swaynag) { | |||
557 | if (swaynag->shm) { | 560 | if (swaynag->shm) { |
558 | wl_shm_destroy(swaynag->shm); | 561 | wl_shm_destroy(swaynag->shm); |
559 | } | 562 | } |
563 | |||
564 | if (swaynag->display) { | ||
565 | wl_display_disconnect(swaynag->display); | ||
566 | } | ||
560 | } | 567 | } |
diff --git a/swaynag/types.c b/swaynag/types.c index 7bef0f87..821e5b21 100644 --- a/swaynag/types.c +++ b/swaynag/types.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <getopt.h> | 1 | #include <getopt.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -32,7 +31,8 @@ struct swaynag_type *swaynag_type_new(const char *name) { | |||
32 | 31 | ||
33 | void swaynag_types_add_default(list_t *types) { | 32 | void swaynag_types_add_default(list_t *types) { |
34 | struct swaynag_type *type_defaults = swaynag_type_new("<defaults>"); | 33 | struct swaynag_type *type_defaults = swaynag_type_new("<defaults>"); |
35 | type_defaults->font = strdup("pango:Monospace 10"); | 34 | type_defaults->font_description = |
35 | pango_font_description_from_string("pango:Monospace 10"); | ||
36 | type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | 36 | type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
37 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | 37 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
38 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; | 38 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; |
@@ -90,8 +90,8 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { | |||
90 | return; | 90 | return; |
91 | } | 91 | } |
92 | 92 | ||
93 | if (src->font) { | 93 | if (src->font_description) { |
94 | dest->font = strdup(src->font); | 94 | dest->font_description = pango_font_description_copy(src->font_description); |
95 | } | 95 | } |
96 | 96 | ||
97 | if (src->output) { | 97 | if (src->output) { |
@@ -172,7 +172,7 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { | |||
172 | 172 | ||
173 | void swaynag_type_free(struct swaynag_type *type) { | 173 | void swaynag_type_free(struct swaynag_type *type) { |
174 | free(type->name); | 174 | free(type->name); |
175 | free(type->font); | 175 | pango_font_description_free(type->font_description); |
176 | free(type->output); | 176 | free(type->output); |
177 | free(type); | 177 | free(type); |
178 | } | 178 | } |