diff options
236 files changed, 7106 insertions, 7328 deletions
diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 324cf6ce..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 |
@@ -19,7 +20,8 @@ packages: | |||
19 | - wayland-protocols | 20 | - wayland-protocols |
20 | - xcb-util-image-dev | 21 | - xcb-util-image-dev |
21 | - xcb-util-wm-dev | 22 | - xcb-util-wm-dev |
22 | - xwayland | 23 | - xwayland-dev |
24 | - hwdata-dev | ||
23 | sources: | 25 | sources: |
24 | - https://github.com/swaywm/sway | 26 | - https://github.com/swaywm/sway |
25 | - https://gitlab.freedesktop.org/wlroots/wlroots.git | 27 | - https://gitlab.freedesktop.org/wlroots/wlroots.git |
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index ac5536e5..9972c01a 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml | |||
@@ -3,6 +3,7 @@ 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 |
@@ -17,6 +18,7 @@ packages: | |||
17 | - xcb-util-wm | 18 | - xcb-util-wm |
18 | - xorg-xwayland | 19 | - xorg-xwayland |
19 | - seatd | 20 | - seatd |
21 | - hwdata | ||
20 | sources: | 22 | sources: |
21 | - https://github.com/swaywm/sway | 23 | - https://github.com/swaywm/sway |
22 | - https://gitlab.freedesktop.org/wlroots/wlroots.git | 24 | - https://gitlab.freedesktop.org/wlroots/wlroots.git |
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index aa3007d8..29c6312a 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml | |||
@@ -20,11 +20,14 @@ packages: | |||
20 | - devel/libudev-devd | 20 | - devel/libudev-devd |
21 | - graphics/libdrm | 21 | - graphics/libdrm |
22 | - graphics/mesa-libs | 22 | - graphics/mesa-libs |
23 | - sysutils/libdisplay-info | ||
23 | - sysutils/seatd | 24 | - sysutils/seatd |
24 | - x11/libinput | 25 | - x11/libinput |
25 | - x11/libX11 | 26 | - x11/libX11 |
26 | - x11/pixman | 27 | - x11/pixman |
27 | - x11/xcb-util-wm | 28 | - x11/xcb-util-wm |
29 | - x11-servers/xwayland-devel | ||
30 | - misc/hwdata | ||
28 | sources: | 31 | sources: |
29 | - https://github.com/swaywm/sway | 32 | - https://github.com/swaywm/sway |
30 | - https://gitlab.freedesktop.org/wlroots/wlroots.git | 33 | - https://gitlab.freedesktop.org/wlroots/wlroots.git |
@@ -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 059fda89..68b411d9 100644 --- a/README.de.md +++ b/README.de.md | |||
@@ -2,13 +2,13 @@ | |||
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). | 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 | ||
@@ -23,8 +23,8 @@ sway benötigt die folgenden Pakete: | |||
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 3a641295..5ce94cde 100644 --- a/README.dk.md +++ b/README.dk.md | |||
@@ -45,12 +45,6 @@ Kør følgende kommandoer: | |||
45 | ninja -C build | 45 | ninja -C build |
46 | sudo ninja -C build install | 46 | sudo ninja -C build install |
47 | 47 | ||
48 | På systemer uden logind eller seatd skal du sætte SUID bit på sway filen: | ||
49 | |||
50 | sudo chmod a+s /usr/local/bin/sway | ||
51 | |||
52 | Sway dropper 'root' tilladelser kort efter opstart. | ||
53 | |||
54 | ## Konfiguration | 48 | ## Konfiguration |
55 | 49 | ||
56 | Hvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til | 50 | Hvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til |
diff --git a/README.es.md b/README.es.md index 5e598e1a..1f1657df 100644 --- a/README.es.md +++ b/README.es.md | |||
@@ -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 e99160e8..7752fc70 100644 --- a/README.fr.md +++ b/README.fr.md | |||
@@ -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 |
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 index f325cb88..d697f78e 100644 --- a/README.gr.md +++ b/README.gr.md | |||
@@ -44,12 +44,6 @@ _\*Compile-time dep_ | |||
44 | ninja -C build/ | 44 | ninja -C build/ |
45 | sudo ninja -C build/ install | 45 | sudo ninja -C build/ install |
46 | 46 | ||
47 | Σε συστήματα χωρίς logind ή seatd, πρέπει να κάνετε suid το sway binary: | ||
48 | |||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | |||
51 | Το Sway θα κάνει drop root δικαιώματα λίγο μετά την εκκίνηση. | ||
52 | |||
53 | ## Configuration | 47 | ## Configuration |
54 | 48 | ||
55 | Εάν ήδη χρησιμοποιήτε το i3, αντιγράψτε το i3 config σας στο `~/.config/sway/config` και | 49 | Εάν ήδη χρησιμοποιήτε το i3, αντιγράψτε το i3 config σας στο `~/.config/sway/config` και |
diff --git a/README.hi.md b/README.hi.md index af64eada..eae5e90a 100644 --- a/README.hi.md +++ b/README.hi.md | |||
@@ -48,13 +48,6 @@ _\* Compilation के समय आवश्यक_ | |||
48 | ninja -C build/ | 48 | ninja -C build/ |
49 | sudo ninja -C build/ install | 49 | sudo ninja -C build/ install |
50 | 50 | ||
51 | उन systems पर जिनमें ना तो logind है, ना ही seatd है, आपको sway की binary | ||
52 | को suid करना पडेगा: | ||
53 | |||
54 | sudo chmod a+s /usr/local/bin/sway | ||
55 | |||
56 | Sway अपनी root अनुमतियां प्रारंभ होने के कुछ ही देर बाद छोड़ देगी। | ||
57 | |||
58 | ## Configuration | 51 | ## Configuration |
59 | 52 | ||
60 | अगर आप पहले से ही i3 का उपयोग करते हैं तो अपने i3 config को | 53 | अगर आप पहले से ही i3 का उपयोग करते हैं तो अपने i3 config को |
diff --git a/README.hu.md b/README.hu.md index 9e3bc9e1..82ca6785 100644 --- a/README.hu.md +++ b/README.hu.md | |||
@@ -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 |
diff --git a/README.ir.md b/README.ir.md index 64956a9e..a485a405 100644 --- a/README.ir.md +++ b/README.ir.md | |||
@@ -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 پس از startup مجوزهای دسترسی root را رها میکند. | ||
53 | |||
54 | ### شخصی سازی و تنظیمات | 48 | ### شخصی سازی و تنظیمات |
55 | 49 | ||
56 | اگر در حال حاضر از i3 استفاده میکنید، تنظیمات i3 خودتان را در فایل `~/.config/sway/config` کپی کنید و بدون نیاز به تغییر کار خواهد کرد. در غیر اینصورت، فایل نمونه تنظیمات را استفاده کنید. این فایل عموما در `/etc/sway/config` قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید. | 50 | اگر در حال حاضر از i3 استفاده میکنید، تنظیمات i3 خودتان را در فایل `~/.config/sway/config` کپی کنید و بدون نیاز به تغییر کار خواهد کرد. در غیر اینصورت، فایل نمونه تنظیمات را استفاده کنید. این فایل عموما در `/etc/sway/config` قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید. |
diff --git a/README.it.md b/README.it.md index b14b522d..82bb5783 100644 --- a/README.it.md +++ b/README.it.md | |||
@@ -42,13 +42,6 @@ Esegui questi comandi: | |||
42 | ninja -C build/ | 42 | ninja -C build/ |
43 | sudo ninja -C build/ install | 43 | sudo ninja -C build/ install |
44 | 44 | ||
45 | Nei sistemi in cui non sono disponibili né logind né seatd, è necessario | ||
46 | impostare il permesso suid al binario di sway: | ||
47 | |||
48 | sudo chmod a+s /usr/local/bin/sway | ||
49 | |||
50 | Sway rinuncerà ai permessi di root poco dopo l'avvio. | ||
51 | |||
52 | ## Configurazione | 45 | ## Configurazione |
53 | 46 | ||
54 | Se hai già usato i3, copia il tuo file di configurazione in | 47 | Se hai già usato i3, copia il tuo file di configurazione in |
diff --git a/README.ja.md b/README.ja.md index ece9ce48..4e9a9971 100644 --- a/README.ja.md +++ b/README.ja.md | |||
@@ -34,11 +34,11 @@ Swayは沢山のディストリビューションで提供されています。" | |||
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 7a1f4ce1..e086c174 100644 --- a/README.ko.md +++ b/README.ko.md | |||
@@ -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]** - [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] | 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). |
@@ -31,7 +31,8 @@ Install dependencies: | |||
31 | * json-c | 31 | * json-c |
32 | * pango | 32 | * pango |
33 | * cairo | 33 | * cairo |
34 | * gdk-pixbuf2 (optional: system tray) | 34 | * gdk-pixbuf2 (optional: additional image formats for system tray) |
35 | * [swaybg] (optional: wallpaper) | ||
35 | * [scdoc] (optional: man pages) \* | 36 | * [scdoc] (optional: man pages) \* |
36 | * git (optional: version info) \* | 37 | * git (optional: version info) \* |
37 | 38 | ||
@@ -43,12 +44,6 @@ Run these commands: | |||
43 | ninja -C build/ | 44 | ninja -C build/ |
44 | sudo ninja -C build/ install | 45 | sudo ninja -C build/ install |
45 | 46 | ||
46 | On systems without logind nor seatd, you need to suid the sway binary: | ||
47 | |||
48 | sudo chmod a+s /usr/local/bin/sway | ||
49 | |||
50 | Sway will drop root permissions shortly after startup. | ||
51 | |||
52 | ## Configuration | 47 | ## Configuration |
53 | 48 | ||
54 | 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 |
@@ -62,11 +57,13 @@ Run `sway` from a TTY. Some display managers may work but are not supported by | |||
62 | sway (gdm is known to work fairly well). | 57 | sway (gdm is known to work fairly well). |
63 | 58 | ||
64 | [en]: https://github.com/swaywm/sway#readme | 59 | [en]: https://github.com/swaywm/sway#readme |
60 | [ar]: README.ar.md | ||
61 | [cs]: README.cs.md | ||
65 | [de]: README.de.md | 62 | [de]: README.de.md |
66 | [dk]: README.dk.md | 63 | [dk]: README.dk.md |
67 | [es]: README.es.md | 64 | [es]: README.es.md |
68 | [fr]: README.fr.md | 65 | [fr]: README.fr.md |
69 | [sv]: README.sv.md | 66 | [ge]: README.ge.md |
70 | [gr]: README.gr.md | 67 | [gr]: README.gr.md |
71 | [hi]: README.hi.md | 68 | [hi]: README.hi.md |
72 | [hu]: README.hu.md | 69 | [hu]: README.hu.md |
@@ -75,10 +72,12 @@ sway (gdm is known to work fairly well). | |||
75 | [ja]: README.ja.md | 72 | [ja]: README.ja.md |
76 | [ko]: README.ko.md | 73 | [ko]: README.ko.md |
77 | [nl]: README.nl.md | 74 | [nl]: README.nl.md |
75 | [no]: README.no.md | ||
78 | [pl]: README.pl.md | 76 | [pl]: README.pl.md |
79 | [pt]: README.pt.md | 77 | [pt]: README.pt.md |
80 | [ro]: README.ro.md | 78 | [ro]: README.ro.md |
81 | [ru]: README.ru.md | 79 | [ru]: README.ru.md |
80 | [sv]: README.sv.md | ||
82 | [tr]: README.tr.md | 81 | [tr]: README.tr.md |
83 | [uk]: README.uk.md | 82 | [uk]: README.uk.md |
84 | [zh-CN]: README.zh-CN.md | 83 | [zh-CN]: README.zh-CN.md |
@@ -91,4 +90,5 @@ sway (gdm is known to work fairly well). | |||
91 | [GitHub releases]: https://github.com/swaywm/sway/releases | 90 | [GitHub releases]: https://github.com/swaywm/sway/releases |
92 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | 91 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup |
93 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | 92 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots |
93 | [swaybg]: https://github.com/swaywm/swaybg/ | ||
94 | [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 8a344f03..bf1ea975 100644 --- a/README.nl.md +++ b/README.nl.md | |||
@@ -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 ab99abb1..65b3c3a1 100644 --- a/README.pl.md +++ b/README.pl.md | |||
@@ -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 b22948aa..c1611a31 100644 --- a/README.pt.md +++ b/README.pt.md | |||
@@ -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 4ce05093..a3559a8b 100644 --- a/README.ro.md +++ b/README.ro.md | |||
@@ -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 fb6f3344..edc0eda7 100644 --- a/README.ru.md +++ b/README.ru.md | |||
@@ -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`, и |
diff --git a/README.sv.md b/README.sv.md index 28724270..c50ca068 100644 --- a/README.sv.md +++ b/README.sv.md | |||
@@ -41,12 +41,6 @@ Kör dessa kommandon: | |||
41 | ninja -C build/ | 41 | ninja -C build/ |
42 | sudo ninja -C build/ install | 42 | sudo ninja -C build/ install |
43 | 43 | ||
44 | På system utan logind eller seatd måste du ge sways exekverbara fil root-privilegier: | ||
45 | |||
46 | sudo chmod a+s /usr/local/bin/sway | ||
47 | |||
48 | Sway kommer att överge sina root-privilegier kort efter uppstart. | ||
49 | |||
50 | ## Konfiguration | 44 | ## Konfiguration |
51 | 45 | ||
52 | 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. | 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. |
diff --git a/README.tr.md b/README.tr.md index e09d2eaf..40de1474 100644 --- a/README.tr.md +++ b/README.tr.md | |||
@@ -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. |
diff --git a/README.uk.md b/README.uk.md index d9adcd13..33359cff 100644 --- a/README.uk.md +++ b/README.uk.md | |||
@@ -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 b057bfb8..a6f4518a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md | |||
@@ -39,12 +39,6 @@ _\*编译时依赖_ | |||
39 | ninja -C build/ | 39 | ninja -C build/ |
40 | sudo ninja -C build/ install | 40 | sudo ninja -C build/ install |
41 | 41 | ||
42 | 在没有logind或seatd的系统上, 你需要给sway二进制文件设置suid: | ||
43 | |||
44 | sudo chmod a+s /usr/local/bin/sway | ||
45 | |||
46 | 启动后,Sway会尽快放弃root权限。 | ||
47 | |||
48 | ## 配置 | 42 | ## 配置 |
49 | 43 | ||
50 | 如果你已经在使用i3,直接复制i3配置文件到 `~/.config/sway/config`,这是开箱即用的。或者,你可以复制配置样例到`~/.config/sway/config`。它通常位于 `/etc/sway/config`。 | 44 | 如果你已经在使用i3,直接复制i3配置文件到 `~/.config/sway/config`,这是开箱即用的。或者,你可以复制配置样例到`~/.config/sway/config`。它通常位于 `/etc/sway/config`。 |
diff --git a/README.zh-TW.md b/README.zh-TW.md index a7168244..2de2f63f 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md | |||
@@ -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 index 8c2efe99..272aa837 100644 --- a/common/gesture.c +++ b/common/gesture.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "gesture.h" | 1 | #include "gesture.h" |
3 | 2 | ||
4 | #include <math.h> | 3 | #include <math.h> |
@@ -12,23 +11,6 @@ | |||
12 | 11 | ||
13 | const uint8_t GESTURE_FINGERS_ANY = 0; | 12 | const uint8_t GESTURE_FINGERS_ANY = 0; |
14 | 13 | ||
15 | // Helper to easily allocate and format string | ||
16 | static char *strformat(const char *format, ...) { | ||
17 | va_list args; | ||
18 | va_start(args, format); | ||
19 | int length = vsnprintf(NULL, 0, format, args) + 1; | ||
20 | va_end(args); | ||
21 | |||
22 | char *result = malloc(length); | ||
23 | if (result) { | ||
24 | va_start(args, format); | ||
25 | vsnprintf(result, length, format, args); | ||
26 | va_end(args); | ||
27 | } | ||
28 | |||
29 | return result; | ||
30 | } | ||
31 | |||
32 | char *gesture_parse(const char *input, struct gesture *output) { | 14 | char *gesture_parse(const char *input, struct gesture *output) { |
33 | // Clear output in case of failure | 15 | // Clear output in case of failure |
34 | output->type = GESTURE_TYPE_NONE; | 16 | output->type = GESTURE_TYPE_NONE; |
@@ -38,7 +20,7 @@ char *gesture_parse(const char *input, struct gesture *output) { | |||
38 | // Split input type, fingers and directions | 20 | // Split input type, fingers and directions |
39 | list_t *split = split_string(input, ":"); | 21 | list_t *split = split_string(input, ":"); |
40 | if (split->length < 1 || split->length > 3) { | 22 | if (split->length < 1 || split->length > 3) { |
41 | return strformat( | 23 | return format_str( |
42 | "expected <gesture>[:<fingers>][:direction], got %s", | 24 | "expected <gesture>[:<fingers>][:direction], got %s", |
43 | input); | 25 | input); |
44 | } | 26 | } |
@@ -51,8 +33,8 @@ char *gesture_parse(const char *input, struct gesture *output) { | |||
51 | } else if (strcmp(split->items[0], "swipe") == 0) { | 33 | } else if (strcmp(split->items[0], "swipe") == 0) { |
52 | output->type = GESTURE_TYPE_SWIPE; | 34 | output->type = GESTURE_TYPE_SWIPE; |
53 | } else { | 35 | } else { |
54 | return strformat("expected hold|pinch|swipe, got %s", | 36 | return format_str("expected hold|pinch|swipe, got %s", |
55 | split->items[0]); | 37 | (const char *)split->items[0]); |
56 | } | 38 | } |
57 | 39 | ||
58 | // Parse optional arguments | 40 | // Parse optional arguments |
@@ -67,7 +49,7 @@ char *gesture_parse(const char *input, struct gesture *output) { | |||
67 | next = split->length == 3 ? split->items[2] : NULL; | 49 | next = split->length == 3 ? split->items[2] : NULL; |
68 | } else if (split->length == 3) { | 50 | } else if (split->length == 3) { |
69 | // Fail here if argument can only be finger count | 51 | // Fail here if argument can only be finger count |
70 | return strformat("expected 1-9, got %s", next); | 52 | return format_str("expected 1-9, got %s", next); |
71 | } | 53 | } |
72 | 54 | ||
73 | // If there is an argument left, try to parse as direction | 55 | // If there is an argument left, try to parse as direction |
@@ -95,7 +77,7 @@ char *gesture_parse(const char *input, struct gesture *output) { | |||
95 | } else if (strcmp(item, "counterclockwise") == 0) { | 77 | } else if (strcmp(item, "counterclockwise") == 0) { |
96 | output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; | 78 | output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; |
97 | } else { | 79 | } else { |
98 | return strformat("expected direction, got %s", item); | 80 | return format_str("expected direction, got %s", item); |
99 | } | 81 | } |
100 | } | 82 | } |
101 | list_free_items_and_destroy(directions); | 83 | list_free_items_and_destroy(directions); |
@@ -163,7 +145,7 @@ static char *gesture_directions_to_string(uint32_t directions) { | |||
163 | if (!result) { | 145 | if (!result) { |
164 | result = strdup(name); | 146 | result = strdup(name); |
165 | } else { | 147 | } else { |
166 | char *new = strformat("%s+%s", result, name); | 148 | char *new = format_str("%s+%s", result, name); |
167 | free(result); | 149 | free(result); |
168 | result = new; | 150 | result = new; |
169 | } | 151 | } |
@@ -179,7 +161,7 @@ static char *gesture_directions_to_string(uint32_t directions) { | |||
179 | 161 | ||
180 | char *gesture_to_string(struct gesture *gesture) { | 162 | char *gesture_to_string(struct gesture *gesture) { |
181 | char *directions = gesture_directions_to_string(gesture->directions); | 163 | char *directions = gesture_directions_to_string(gesture->directions); |
182 | char *result = strformat("%s:%u:%s", | 164 | char *result = format_str("%s:%u:%s", |
183 | gesture_type_string(gesture->type), | 165 | gesture_type_string(gesture->type), |
184 | gesture->fingers, directions); | 166 | gesture->fingers, directions); |
185 | free(directions); | 167 | free(directions); |
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 3756075a..c0ce1f68 100644 --- a/common/meson.build +++ b/common/meson.build | |||
@@ -1,7 +1,6 @@ | |||
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', |
6 | 'gesture.c', | 5 | 'gesture.c', |
7 | 'ipc-client.c', | 6 | 'ipc-client.c', |
@@ -14,7 +13,6 @@ lib_sway_common = static_library( | |||
14 | ), | 13 | ), |
15 | dependencies: [ | 14 | dependencies: [ |
16 | cairo, | 15 | cairo, |
17 | gdk_pixbuf, | ||
18 | pango, | 16 | pango, |
19 | pangocairo, | 17 | pangocairo, |
20 | 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 e04bf80f..288569b3 100644 --- a/common/pango.c +++ b/common/pango.c | |||
@@ -84,18 +84,11 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, | |||
84 | int *baseline, double scale, bool markup, const char *fmt, ...) { | 84 | int *baseline, double scale, bool markup, const char *fmt, ...) { |
85 | va_list args; | 85 | va_list args; |
86 | va_start(args, fmt); | 86 | va_start(args, fmt); |
87 | // Add one since vsnprintf excludes null terminator. | 87 | char *buf = vformat_str(fmt, args); |
88 | int length = vsnprintf(NULL, 0, fmt, args) + 1; | ||
89 | va_end(args); | 88 | va_end(args); |
90 | |||
91 | char *buf = malloc(length); | ||
92 | if (buf == NULL) { | 89 | if (buf == NULL) { |
93 | sway_log(SWAY_ERROR, "Failed to allocate memory"); | ||
94 | return; | 90 | return; |
95 | } | 91 | } |
96 | va_start(args, fmt); | ||
97 | vsnprintf(buf, length, fmt, args); | ||
98 | va_end(args); | ||
99 | 92 | ||
100 | PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); | 93 | PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); |
101 | pango_cairo_update_layout(cairo, layout); | 94 | pango_cairo_update_layout(cairo, layout); |
@@ -104,6 +97,7 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, | |||
104 | *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; | 97 | *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; |
105 | } | 98 | } |
106 | g_object_unref(layout); | 99 | g_object_unref(layout); |
100 | |||
107 | free(buf); | 101 | free(buf); |
108 | } | 102 | } |
109 | 103 | ||
@@ -125,18 +119,11 @@ void render_text(cairo_t *cairo, const PangoFontDescription *desc, | |||
125 | double scale, bool markup, const char *fmt, ...) { | 119 | double scale, bool markup, const char *fmt, ...) { |
126 | va_list args; | 120 | va_list args; |
127 | va_start(args, fmt); | 121 | va_start(args, fmt); |
128 | // Add one since vsnprintf excludes null terminator. | 122 | char *buf = vformat_str(fmt, args); |
129 | int length = vsnprintf(NULL, 0, fmt, args) + 1; | ||
130 | va_end(args); | 123 | va_end(args); |
131 | |||
132 | char *buf = malloc(length); | ||
133 | if (buf == NULL) { | 124 | if (buf == NULL) { |
134 | sway_log(SWAY_ERROR, "Failed to allocate memory"); | ||
135 | return; | 125 | return; |
136 | } | 126 | } |
137 | va_start(args, fmt); | ||
138 | vsnprintf(buf, length, fmt, args); | ||
139 | va_end(args); | ||
140 | 127 | ||
141 | PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); | 128 | PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); |
142 | cairo_font_options_t *fo = cairo_font_options_create(); | 129 | cairo_font_options_t *fo = cairo_font_options_create(); |
@@ -146,5 +133,6 @@ void render_text(cairo_t *cairo, const PangoFontDescription *desc, | |||
146 | pango_cairo_update_layout(cairo, layout); | 133 | pango_cairo_update_layout(cairo, layout); |
147 | pango_cairo_show_layout(cairo, layout); | 134 | pango_cairo_show_layout(cairo, layout); |
148 | g_object_unref(layout); | 135 | g_object_unref(layout); |
136 | |||
149 | free(buf); | 137 | free(buf); |
150 | } | 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 5d4c0673..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> |
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 | # |
@@ -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 %I:%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 1ec19def..00000000 --- a/contrib/grimshot +++ /dev/null | |||
@@ -1,168 +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:-$HOME/.config}/user-dirs.dirs" && \ | ||
17 | . "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" | ||
18 | |||
19 | echo "${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}}" | ||
20 | } | ||
21 | |||
22 | NOTIFY=no | ||
23 | CURSOR= | ||
24 | |||
25 | while [ $# -gt 0 ]; do | ||
26 | key="$1" | ||
27 | |||
28 | case $key in | ||
29 | -n|--notify) | ||
30 | NOTIFY=yes | ||
31 | shift # past argument | ||
32 | ;; | ||
33 | -c|--cursor) | ||
34 | CURSOR=yes | ||
35 | shift # past argument | ||
36 | ;; | ||
37 | *) # unknown option | ||
38 | break # done with parsing --flags | ||
39 | ;; | ||
40 | esac | ||
41 | done | ||
42 | |||
43 | ACTION=${1:-usage} | ||
44 | SUBJECT=${2:-screen} | ||
45 | FILE=${3:-$(getTargetDirectory)/$(date -Ins).png} | ||
46 | |||
47 | if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then | ||
48 | echo "Usage:" | ||
49 | echo " grimshot [--notify] [--cursor] (copy|save) [active|screen|output|area|window] [FILE|-]" | ||
50 | echo " grimshot check" | ||
51 | echo " grimshot usage" | ||
52 | echo "" | ||
53 | echo "Commands:" | ||
54 | echo " copy: Copy the screenshot data into the clipboard." | ||
55 | echo " save: Save the screenshot to a regular file or '-' to pipe to STDOUT." | ||
56 | echo " check: Verify if required tools are installed and exit." | ||
57 | echo " usage: Show this message and exit." | ||
58 | echo "" | ||
59 | echo "Targets:" | ||
60 | echo " active: Currently active window." | ||
61 | echo " screen: All visible outputs." | ||
62 | echo " output: Currently active output." | ||
63 | echo " area: Manually select a region." | ||
64 | echo " window: Manually select a window." | ||
65 | exit | ||
66 | fi | ||
67 | |||
68 | notify() { | ||
69 | notify-send -t 3000 -a grimshot "$@" | ||
70 | } | ||
71 | notifyOk() { | ||
72 | [ "$NOTIFY" = "no" ] && return | ||
73 | |||
74 | TITLE=${2:-"Screenshot"} | ||
75 | MESSAGE=${1:-"OK"} | ||
76 | notify "$TITLE" "$MESSAGE" | ||
77 | } | ||
78 | notifyError() { | ||
79 | if [ $NOTIFY = "yes" ]; then | ||
80 | TITLE=${2:-"Screenshot"} | ||
81 | MESSAGE=${1:-"Error taking screenshot with grim"} | ||
82 | notify -u critical "$TITLE" "$MESSAGE" | ||
83 | else | ||
84 | echo "$1" | ||
85 | fi | ||
86 | } | ||
87 | |||
88 | die() { | ||
89 | MSG=${1:-Bye} | ||
90 | notifyError "Error: $MSG" | ||
91 | exit 2 | ||
92 | } | ||
93 | |||
94 | check() { | ||
95 | COMMAND=$1 | ||
96 | if command -v "$COMMAND" > /dev/null 2>&1; then | ||
97 | RESULT="OK" | ||
98 | else | ||
99 | RESULT="NOT FOUND" | ||
100 | fi | ||
101 | echo " $COMMAND: $RESULT" | ||
102 | } | ||
103 | |||
104 | takeScreenshot() { | ||
105 | FILE=$1 | ||
106 | GEOM=$2 | ||
107 | OUTPUT=$3 | ||
108 | if [ -n "$OUTPUT" ]; then | ||
109 | grim ${CURSOR:+-c} -o "$OUTPUT" "$FILE" || die "Unable to invoke grim" | ||
110 | elif [ -z "$GEOM" ]; then | ||
111 | grim ${CURSOR:+-c} "$FILE" || die "Unable to invoke grim" | ||
112 | else | ||
113 | grim ${CURSOR:+-c} -g "$GEOM" "$FILE" || die "Unable to invoke grim" | ||
114 | fi | ||
115 | } | ||
116 | |||
117 | if [ "$ACTION" = "check" ] ; then | ||
118 | echo "Checking if required tools are installed. If something is missing, install it to your system and make it available in PATH..." | ||
119 | check grim | ||
120 | check slurp | ||
121 | check swaymsg | ||
122 | check wl-copy | ||
123 | check jq | ||
124 | check notify-send | ||
125 | exit | ||
126 | elif [ "$SUBJECT" = "area" ] ; then | ||
127 | GEOM=$(slurp -d) | ||
128 | # Check if user exited slurp without selecting the area | ||
129 | if [ -z "$GEOM" ]; then | ||
130 | exit 1 | ||
131 | fi | ||
132 | WHAT="Area" | ||
133 | elif [ "$SUBJECT" = "active" ] ; then | ||
134 | FOCUSED=$(swaymsg -t get_tree | jq -r 'recurse(.nodes[]?, .floating_nodes[]?) | select(.focused)') | ||
135 | GEOM=$(echo "$FOCUSED" | jq -r '.rect | "\(.x),\(.y) \(.width)x\(.height)"') | ||
136 | APP_ID=$(echo "$FOCUSED" | jq -r '.app_id') | ||
137 | WHAT="$APP_ID window" | ||
138 | elif [ "$SUBJECT" = "screen" ] ; then | ||
139 | GEOM="" | ||
140 | WHAT="Screen" | ||
141 | elif [ "$SUBJECT" = "output" ] ; then | ||
142 | GEOM="" | ||
143 | OUTPUT=$(swaymsg -t get_outputs | jq -r '.[] | select(.focused)' | jq -r '.name') | ||
144 | WHAT="$OUTPUT" | ||
145 | elif [ "$SUBJECT" = "window" ] ; then | ||
146 | GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp) | ||
147 | # Check if user exited slurp without selecting the area | ||
148 | if [ -z "$GEOM" ]; then | ||
149 | exit 1 | ||
150 | fi | ||
151 | WHAT="Window" | ||
152 | else | ||
153 | die "Unknown subject to take a screen shot from" "$SUBJECT" | ||
154 | fi | ||
155 | |||
156 | if [ "$ACTION" = "copy" ] ; then | ||
157 | takeScreenshot - "$GEOM" "$OUTPUT" | wl-copy --type image/png || die "Clipboard error" | ||
158 | notifyOk "$WHAT copied to buffer" | ||
159 | else | ||
160 | if takeScreenshot "$FILE" "$GEOM" "$OUTPUT"; then | ||
161 | TITLE="Screenshot of $SUBJECT" | ||
162 | MESSAGE=$(basename "$FILE") | ||
163 | notifyOk "$MESSAGE" "$TITLE" | ||
164 | echo "$FILE" | ||
165 | else | ||
166 | notifyError "Error taking screenshot with grim" | ||
167 | fi | ||
168 | fi | ||
diff --git a/contrib/grimshot.1 b/contrib/grimshot.1 deleted file mode 100644 index 2c4c6a95..00000000 --- a/contrib/grimshot.1 +++ /dev/null | |||
@@ -1,109 +0,0 @@ | |||
1 | .\" Generated by scdoc 1.11.2 | ||
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" "2022-03-31" | ||
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] [--cursor] (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 | \fB--cursor\fR | ||
30 | .RS 4 | ||
31 | Include cursors in the screenshot.\& | ||
32 | .P | ||
33 | .RE | ||
34 | \fBsave\fR | ||
35 | .RS 4 | ||
36 | Save the screenshot into a regular file.\& Grimshot will write images | ||
37 | files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined | ||
38 | in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\& | ||
39 | Set FILE to '\&-'\& to pipe the output to STDOUT.\& | ||
40 | .P | ||
41 | .RE | ||
42 | \fBcopy\fR | ||
43 | .RS 4 | ||
44 | Copy the screenshot data (as image/png) into the clipboard.\& | ||
45 | .P | ||
46 | .RE | ||
47 | .SH DESCRIPTION | ||
48 | .P | ||
49 | Grimshot is an easy-to-use screenshot utility for sway.\& It provides a | ||
50 | convenient interface over grim, slurp and jq, and supports storing the | ||
51 | screenshot either directly to the clipboard using wl-copy or to a file.\& | ||
52 | .P | ||
53 | .SH EXAMPLES | ||
54 | .P | ||
55 | An example usage pattern is to add these bindings to your sway config: | ||
56 | .P | ||
57 | .nf | ||
58 | .RS 4 | ||
59 | # Screenshots: | ||
60 | # Super+P: Current window | ||
61 | # Super+Shift+p: Select area | ||
62 | # Super+Alt+p Current output | ||
63 | # Super+Ctrl+p Select a window | ||
64 | |||
65 | bindsym Mod4+p exec grimshot save active | ||
66 | bindsym Mod4+Shift+p exec grimshot save area | ||
67 | bindsym Mod4+Mod1+p exec grimshot save output | ||
68 | bindsym Mod4+Ctrl+p exec grimshot save window | ||
69 | .fi | ||
70 | .RE | ||
71 | .P | ||
72 | .SH TARGETS | ||
73 | .P | ||
74 | grimshot can capture the following named targets: | ||
75 | .P | ||
76 | \fIactive\fR | ||
77 | .RS 4 | ||
78 | Captures the currently active window.\& | ||
79 | .P | ||
80 | .RE | ||
81 | \fIscreen\fR | ||
82 | .RS 4 | ||
83 | Captures the entire screen.\& This includes all visible outputs.\& | ||
84 | .P | ||
85 | .RE | ||
86 | \fIarea\fR | ||
87 | .RS 4 | ||
88 | Allows manually selecting a rectangular region, and captures that.\& | ||
89 | .P | ||
90 | .RE | ||
91 | \fIwindow\fR | ||
92 | .RS 4 | ||
93 | Allows manually selecting a single window (by clicking on it), and | ||
94 | captures it.\& | ||
95 | .P | ||
96 | .RE | ||
97 | \fIoutput\fR | ||
98 | .RS 4 | ||
99 | Captures the currently active output.\& | ||
100 | .P | ||
101 | .RE | ||
102 | .SH OUTPUT | ||
103 | .P | ||
104 | Grimshot will print the filename of the captured screenshot to stdout if called | ||
105 | with the \fIsave\fR subcommand.\& | ||
106 | .P | ||
107 | .SH SEE ALSO | ||
108 | .P | ||
109 | \fBgrim\fR(1) | ||
diff --git a/contrib/grimshot.1.scd b/contrib/grimshot.1.scd deleted file mode 100644 index e356f99d..00000000 --- a/contrib/grimshot.1.scd +++ /dev/null | |||
@@ -1,80 +0,0 @@ | |||
1 | grimshot(1) | ||
2 | |||
3 | # NAME | ||
4 | |||
5 | grimshot - a helper for screenshots within sway | ||
6 | |||
7 | # SYNOPSIS | ||
8 | |||
9 | *grimshot* [--notify] [--cursor] (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 | *--cursor* | ||
19 | Include cursors in the screenshot. | ||
20 | |||
21 | *save* | ||
22 | Save the screenshot into a regular file. Grimshot will write image | ||
23 | files to *XDG_SCREENSHOTS_DIR* if this is set (or defined | ||
24 | in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*. | ||
25 | Set FILE to '-' to pipe the output to STDOUT. | ||
26 | |||
27 | *copy* | ||
28 | Copy the screenshot data (as image/png) into the clipboard. | ||
29 | |||
30 | # DESCRIPTION | ||
31 | |||
32 | Grimshot is an easy-to-use screenshot utility for sway. It provides a | ||
33 | convenient interface over grim, slurp and jq, and supports storing the | ||
34 | screenshot either directly to the clipboard using wl-copy or to a file. | ||
35 | |||
36 | # EXAMPLES | ||
37 | |||
38 | An example usage pattern is to add these bindings to your sway config: | ||
39 | |||
40 | ``` | ||
41 | # Screenshots: | ||
42 | # Super+P: Current window | ||
43 | # Super+Shift+p: Select area | ||
44 | # Super+Alt+p Current output | ||
45 | # Super+Ctrl+p Select a window | ||
46 | |||
47 | bindsym Mod4+p exec grimshot save active | ||
48 | bindsym Mod4+Shift+p exec grimshot save area | ||
49 | bindsym Mod4+Mod1+p exec grimshot save output | ||
50 | bindsym Mod4+Ctrl+p exec grimshot save window | ||
51 | ``` | ||
52 | |||
53 | # TARGETS | ||
54 | |||
55 | grimshot can capture the following named targets: | ||
56 | |||
57 | _active_ | ||
58 | Captures the currently active window. | ||
59 | |||
60 | _screen_ | ||
61 | Captures the entire screen. This includes all visible outputs. | ||
62 | |||
63 | _area_ | ||
64 | Allows manually selecting a rectangular region, and captures that. | ||
65 | |||
66 | _window_ | ||
67 | Allows manually selecting a single window (by clicking on it), and | ||
68 | captures it. | ||
69 | |||
70 | _output_ | ||
71 | Captures the currently active output. | ||
72 | |||
73 | # OUTPUT | ||
74 | |||
75 | Grimshot will print the filename of the captured screenshot to stdout if called | ||
76 | with the _save_ subcommand. | ||
77 | |||
78 | # SEE ALSO | ||
79 | |||
80 | *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/pango.h b/include/pango.h index 1db113c2..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 & < > ' ". |
@@ -16,9 +17,9 @@ size_t escape_markup_text(const char *src, char *dest); | |||
16 | PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, | 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 PangoFontDescription *desc, 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 get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline); | 21 | void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline); |
21 | void render_text(cairo_t *cairo, PangoFontDescription *desc, | 22 | void render_text(cairo_t *cairo, PangoFontDescription *desc, |
22 | double scale, bool markup, const char *fmt, ...); | 23 | double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(5, 6); |
23 | 24 | ||
24 | #endif | 25 | #endif |
diff --git a/include/stringop.h b/include/stringop.h index b29f59b2..19a50f23 100644 --- a/include/stringop.h +++ b/include/stringop.h | |||
@@ -5,6 +5,12 @@ | |||
5 | #include <stddef.h> | 5 | #include <stddef.h> |
6 | #include "list.h" | 6 | #include "list.h" |
7 | 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 | |||
8 | void strip_whitespace(char *str); | 14 | void strip_whitespace(char *str); |
9 | void strip_quotes(char *str); | 15 | void strip_quotes(char *str); |
10 | 16 | ||
@@ -31,4 +37,7 @@ char *argsep(char **stringp, const char *delim, char *matched_delim); | |||
31 | // Expand a path using shell replacements such as $HOME and ~ | 37 | // Expand a path using shell replacements such as $HOME and ~ |
32 | bool expand_path(char **path); | 38 | bool expand_path(char **path); |
33 | 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 | |||
34 | #endif | 43 | #endif |
diff --git a/include/sway/commands.h b/include/sway/commands.h index 013a7b82..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 | */ |
@@ -159,12 +160,11 @@ sway_cmd cmd_new_float; | |||
159 | sway_cmd cmd_new_window; | 160 | sway_cmd cmd_new_window; |
160 | sway_cmd cmd_nop; | 161 | sway_cmd cmd_nop; |
161 | sway_cmd cmd_opacity; | 162 | sway_cmd cmd_opacity; |
162 | sway_cmd cmd_new_float; | ||
163 | sway_cmd cmd_new_window; | ||
164 | sway_cmd cmd_no_focus; | 163 | sway_cmd cmd_no_focus; |
165 | sway_cmd cmd_output; | 164 | sway_cmd cmd_output; |
166 | sway_cmd cmd_permit; | 165 | sway_cmd cmd_permit; |
167 | sway_cmd cmd_popup_during_fullscreen; | 166 | sway_cmd cmd_popup_during_fullscreen; |
167 | sway_cmd cmd_primary_selection; | ||
168 | sway_cmd cmd_reject; | 168 | sway_cmd cmd_reject; |
169 | sway_cmd cmd_reload; | 169 | sway_cmd cmd_reload; |
170 | sway_cmd cmd_rename; | 170 | sway_cmd cmd_rename; |
@@ -252,6 +252,7 @@ sway_cmd input_cmd_click_method; | |||
252 | sway_cmd input_cmd_drag; | 252 | sway_cmd input_cmd_drag; |
253 | sway_cmd input_cmd_drag_lock; | 253 | sway_cmd input_cmd_drag_lock; |
254 | sway_cmd input_cmd_dwt; | 254 | sway_cmd input_cmd_dwt; |
255 | sway_cmd input_cmd_dwtp; | ||
255 | sway_cmd input_cmd_events; | 256 | sway_cmd input_cmd_events; |
256 | sway_cmd input_cmd_left_handed; | 257 | sway_cmd input_cmd_left_handed; |
257 | sway_cmd input_cmd_map_from_region; | 258 | sway_cmd input_cmd_map_from_region; |
@@ -260,10 +261,12 @@ sway_cmd input_cmd_map_to_region; | |||
260 | sway_cmd input_cmd_middle_emulation; | 261 | sway_cmd input_cmd_middle_emulation; |
261 | sway_cmd input_cmd_natural_scroll; | 262 | sway_cmd input_cmd_natural_scroll; |
262 | sway_cmd input_cmd_pointer_accel; | 263 | sway_cmd input_cmd_pointer_accel; |
264 | sway_cmd input_cmd_rotation_angle; | ||
263 | sway_cmd input_cmd_scroll_factor; | 265 | sway_cmd input_cmd_scroll_factor; |
264 | sway_cmd input_cmd_repeat_delay; | 266 | sway_cmd input_cmd_repeat_delay; |
265 | sway_cmd input_cmd_repeat_rate; | 267 | sway_cmd input_cmd_repeat_rate; |
266 | sway_cmd input_cmd_scroll_button; | 268 | sway_cmd input_cmd_scroll_button; |
269 | sway_cmd input_cmd_scroll_button_lock; | ||
267 | sway_cmd input_cmd_scroll_method; | 270 | sway_cmd input_cmd_scroll_method; |
268 | sway_cmd input_cmd_tap; | 271 | sway_cmd input_cmd_tap; |
269 | sway_cmd input_cmd_tap_button_map; | 272 | sway_cmd input_cmd_tap_button_map; |
@@ -294,6 +297,7 @@ sway_cmd output_cmd_scale_filter; | |||
294 | sway_cmd output_cmd_subpixel; | 297 | sway_cmd output_cmd_subpixel; |
295 | sway_cmd output_cmd_toggle; | 298 | sway_cmd output_cmd_toggle; |
296 | sway_cmd output_cmd_transform; | 299 | sway_cmd output_cmd_transform; |
300 | sway_cmd output_cmd_unplug; | ||
297 | 301 | ||
298 | sway_cmd seat_cmd_attach; | 302 | sway_cmd seat_cmd_attach; |
299 | sway_cmd seat_cmd_cursor; | 303 | sway_cmd seat_cmd_cursor; |
diff --git a/include/sway/config.h b/include/sway/config.h index 68c06846..0be1cd22 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "../include/config.h" | 12 | #include "../include/config.h" |
13 | #include "gesture.h" | 13 | #include "gesture.h" |
14 | #include "list.h" | 14 | #include "list.h" |
15 | #include "stringop.h" | ||
15 | #include "swaynag.h" | 16 | #include "swaynag.h" |
16 | #include "tree/container.h" | 17 | #include "tree/container.h" |
17 | #include "sway/input/tablet.h" | 18 | #include "sway/input/tablet.h" |
@@ -150,14 +151,17 @@ struct input_config { | |||
150 | int drag; | 151 | int drag; |
151 | int drag_lock; | 152 | int drag_lock; |
152 | int dwt; | 153 | int dwt; |
154 | int dwtp; | ||
153 | int left_handed; | 155 | int left_handed; |
154 | int middle_emulation; | 156 | int middle_emulation; |
155 | int natural_scroll; | 157 | int natural_scroll; |
156 | float pointer_accel; | 158 | float pointer_accel; |
159 | float rotation_angle; | ||
157 | float scroll_factor; | 160 | float scroll_factor; |
158 | int repeat_delay; | 161 | int repeat_delay; |
159 | int repeat_rate; | 162 | int repeat_rate; |
160 | int scroll_button; | 163 | int scroll_button; |
164 | int scroll_button_lock; | ||
161 | int scroll_method; | 165 | int scroll_method; |
162 | int send_events; | 166 | int send_events; |
163 | int tap; | 167 | int tap; |
@@ -288,6 +292,14 @@ struct output_config { | |||
288 | }; | 292 | }; |
289 | 293 | ||
290 | /** | 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; | ||
300 | }; | ||
301 | |||
302 | /** | ||
291 | * Stores size of gaps for each side | 303 | * Stores size of gaps for each side |
292 | */ | 304 | */ |
293 | struct side_gaps { | 305 | struct side_gaps { |
@@ -529,6 +541,7 @@ struct sway_config { | |||
529 | bool auto_back_and_forth; | 541 | bool auto_back_and_forth; |
530 | bool show_marks; | 542 | bool show_marks; |
531 | enum alignment title_align; | 543 | enum alignment title_align; |
544 | bool primary_selection; | ||
532 | 545 | ||
533 | bool tiling_drag; | 546 | bool tiling_drag; |
534 | int tiling_drag_threshold; | 547 | int tiling_drag_threshold; |
@@ -622,7 +635,7 @@ void run_deferred_bindings(void); | |||
622 | /** | 635 | /** |
623 | * Adds a warning entry to the swaynag instance used for errors. | 636 | * Adds a warning entry to the swaynag instance used for errors. |
624 | */ | 637 | */ |
625 | void config_add_swaynag_warning(char *fmt, ...); | 638 | void config_add_swaynag_warning(char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); |
626 | 639 | ||
627 | /** | 640 | /** |
628 | * Free config struct | 641 | * Free config struct |
@@ -675,20 +688,22 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt | |||
675 | 688 | ||
676 | struct output_config *new_output_config(const char *name); | 689 | struct output_config *new_output_config(const char *name); |
677 | 690 | ||
678 | void merge_output_config(struct output_config *dst, struct output_config *src); | 691 | bool apply_output_configs(struct matched_output_config *configs, |
679 | 692 | size_t configs_len, bool test_only); | |
680 | bool apply_output_config(struct output_config *oc, struct sway_output *output); | ||
681 | 693 | ||
682 | bool test_output_config(struct output_config *oc, struct sway_output *output); | 694 | void apply_all_output_configs(void); |
683 | 695 | ||
684 | 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); | ||
685 | 704 | ||
686 | struct output_config *find_output_config(struct sway_output *output); | 705 | struct output_config *find_output_config(struct sway_output *output); |
687 | 706 | ||
688 | void apply_output_config_to_outputs(struct output_config *oc); | ||
689 | |||
690 | void reset_outputs(void); | ||
691 | |||
692 | void free_output_config(struct output_config *oc); | 707 | void free_output_config(struct output_config *oc); |
693 | 708 | ||
694 | bool spawn_swaybg(void); | 709 | bool spawn_swaybg(void); |
@@ -718,7 +733,7 @@ void free_workspace_config(struct workspace_config *wsc); | |||
718 | /** | 733 | /** |
719 | * Updates the value of config->font_height based on the metrics for title's | 734 | * Updates the value of config->font_height based on the metrics for title's |
720 | * font as reported by pango. | 735 | * font as reported by pango. |
721 | * | 736 | * |
722 | * 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 |
723 | * new size. | 738 | * new size. |
724 | */ | 739 | */ |
diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 59f57f94..8da345ea 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h | |||
@@ -43,6 +43,7 @@ struct criteria { | |||
43 | struct pattern *window_role; | 43 | struct pattern *window_role; |
44 | enum atom_name window_type; | 44 | enum atom_name window_type; |
45 | #endif | 45 | #endif |
46 | bool all; | ||
46 | bool floating; | 47 | bool floating; |
47 | bool tiling; | 48 | bool tiling; |
48 | 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 7f2f5b3e..00000000 --- a/include/sway/desktop.h +++ /dev/null | |||
@@ -1,13 +0,0 @@ | |||
1 | #include <wlr/types/wlr_compositor.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 8a2898dd..527d0350 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h | |||
@@ -35,7 +35,6 @@ 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; | ||
39 | struct wl_listener hold_begin; | 38 | struct wl_listener hold_begin; |
40 | struct wl_listener hold_end; | 39 | struct wl_listener hold_end; |
41 | struct wl_listener pinch_begin; | 40 | struct wl_listener pinch_begin; |
@@ -53,6 +52,7 @@ struct sway_cursor { | |||
53 | 52 | ||
54 | struct wl_listener touch_down; | 53 | struct wl_listener touch_down; |
55 | struct wl_listener touch_up; | 54 | struct wl_listener touch_up; |
55 | struct wl_listener touch_cancel; | ||
56 | struct wl_listener touch_motion; | 56 | struct wl_listener touch_motion; |
57 | struct wl_listener touch_frame; | 57 | struct wl_listener touch_frame; |
58 | bool simulating_pointer_from_touch; | 58 | bool simulating_pointer_from_touch; |
@@ -64,6 +64,7 @@ struct sway_cursor { | |||
64 | struct wl_listener tool_proximity; | 64 | struct wl_listener tool_proximity; |
65 | struct wl_listener tool_button; | 65 | struct wl_listener tool_button; |
66 | bool simulating_pointer_from_tool_tip; | 66 | bool simulating_pointer_from_tool_tip; |
67 | bool simulating_pointer_from_tool_button; | ||
67 | uint32_t tool_buttons; | 68 | uint32_t tool_buttons; |
68 | 69 | ||
69 | struct wl_listener request_set_cursor; | 70 | struct wl_listener request_set_cursor; |
@@ -107,9 +108,13 @@ void cursor_unhide(struct sway_cursor *cursor); | |||
107 | int cursor_get_timeout(struct sway_cursor *cursor); | 108 | int cursor_get_timeout(struct sway_cursor *cursor); |
108 | void cursor_notify_key_press(struct sway_cursor *cursor); | 109 | void cursor_notify_key_press(struct sway_cursor *cursor); |
109 | 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 | |||
110 | void dispatch_cursor_button(struct sway_cursor *cursor, | 115 | void dispatch_cursor_button(struct sway_cursor *cursor, |
111 | 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, |
112 | enum wlr_button_state state); | 117 | enum wl_pointer_button_state state); |
113 | 118 | ||
114 | void dispatch_cursor_axis(struct sway_cursor *cursor, | 119 | void dispatch_cursor_axis(struct sway_cursor *cursor, |
115 | struct wlr_pointer_axis_event *event); | 120 | struct wlr_pointer_axis_event *event); |
@@ -140,4 +145,6 @@ uint32_t get_mouse_button(const char *name, char **error); | |||
140 | 145 | ||
141 | const char *get_mouse_button_name(uint32_t button); | 146 | const char *get_mouse_button_name(uint32_t button); |
142 | 147 | ||
148 | void handle_request_set_cursor_shape(struct wl_listener *listener, void *data); | ||
149 | |||
143 | #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/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 c2041742..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,7 +17,7 @@ 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_pointer_axis_event *event); | 23 | struct wlr_pointer_axis_event *event); |
@@ -36,14 +38,20 @@ struct sway_seatop_impl { | |||
36 | void (*swipe_end)(struct sway_seat *seat, | 38 | void (*swipe_end)(struct sway_seat *seat, |
37 | struct wlr_pointer_swipe_end_event *event); | 39 | struct wlr_pointer_swipe_end_event *event); |
38 | 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); | ||
39 | void (*tablet_tool_motion)(struct sway_seat *seat, | 49 | void (*tablet_tool_motion)(struct sway_seat *seat, |
40 | struct sway_tablet_tool *tool, uint32_t time_msec); | 50 | struct sway_tablet_tool *tool, uint32_t time_msec); |
41 | 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, |
42 | uint32_t time_msec, enum wlr_tablet_tool_tip_state state); | 52 | uint32_t time_msec, enum wlr_tablet_tool_tip_state state); |
43 | void (*end)(struct sway_seat *seat); | 53 | void (*end)(struct sway_seat *seat); |
44 | void (*unref)(struct sway_seat *seat, struct sway_container *con); | 54 | void (*unref)(struct sway_seat *seat, struct sway_container *con); |
45 | void (*render)(struct sway_seat *seat, struct sway_output *output, | ||
46 | pixman_region32_t *damage); | ||
47 | bool allow_set_cursor; | 55 | bool allow_set_cursor; |
48 | }; | 56 | }; |
49 | 57 | ||
@@ -66,19 +74,6 @@ struct sway_seat_node { | |||
66 | struct wl_listener destroy; | 74 | struct wl_listener destroy; |
67 | }; | 75 | }; |
68 | 76 | ||
69 | struct sway_drag_icon { | ||
70 | struct sway_seat *seat; | ||
71 | struct wlr_drag_icon *wlr_drag_icon; | ||
72 | struct wl_list link; // sway_root::drag_icons | ||
73 | |||
74 | double x, y; // in layout-local coordinates | ||
75 | |||
76 | struct wl_listener surface_commit; | ||
77 | struct wl_listener map; | ||
78 | struct wl_listener unmap; | ||
79 | struct wl_listener destroy; | ||
80 | }; | ||
81 | |||
82 | struct sway_drag { | 77 | struct sway_drag { |
83 | struct sway_seat *seat; | 78 | struct sway_seat *seat; |
84 | struct wlr_drag *wlr_drag; | 79 | struct wlr_drag *wlr_drag; |
@@ -89,16 +84,23 @@ struct sway_seat { | |||
89 | struct wlr_seat *wlr_seat; | 84 | struct wlr_seat *wlr_seat; |
90 | struct sway_cursor *cursor; | 85 | struct sway_cursor *cursor; |
91 | 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 | |||
92 | bool has_focus; | 96 | bool has_focus; |
93 | struct wl_list focus_stack; // list of containers in focus order | 97 | struct wl_list focus_stack; // list of containers in focus order |
94 | struct sway_workspace *workspace; | 98 | struct sway_workspace *workspace; |
95 | char *prev_workspace_name; // for workspace back_and_forth | 99 | char *prev_workspace_name; // for workspace back_and_forth |
96 | 100 | ||
97 | // If the focused layer is set, views cannot receive keyboard focus | ||
98 | struct wlr_layer_surface_v1 *focused_layer; | 101 | struct wlr_layer_surface_v1 *focused_layer; |
99 | 102 | // If the exclusive layer is set, views cannot receive keyboard focus | |
100 | // If exclusive_client is set, no other clients will receive input events | 103 | bool has_exclusive_layer; |
101 | struct wl_client *exclusive_client; | ||
102 | 104 | ||
103 | // Last touch point | 105 | // Last touch point |
104 | int32_t touch_id; | 106 | int32_t touch_id; |
@@ -122,6 +124,7 @@ struct sway_seat { | |||
122 | struct wl_listener start_drag; | 124 | struct wl_listener start_drag; |
123 | struct wl_listener request_set_selection; | 125 | struct wl_listener request_set_selection; |
124 | struct wl_listener request_set_primary_selection; | 126 | struct wl_listener request_set_primary_selection; |
127 | struct wl_listener destroy; | ||
125 | 128 | ||
126 | struct wl_list devices; // sway_seat_device::link | 129 | struct wl_list devices; // sway_seat_device::link |
127 | struct wl_list keyboard_groups; // sway_keyboard_group::link | 130 | struct wl_list keyboard_groups; // sway_keyboard_group::link |
@@ -157,6 +160,9 @@ void seat_add_device(struct sway_seat *seat, | |||
157 | void seat_configure_device(struct sway_seat *seat, | 160 | void seat_configure_device(struct sway_seat *seat, |
158 | struct sway_input_device *device); | 161 | struct sway_input_device *device); |
159 | 162 | ||
163 | void seat_configure_device_mapping(struct sway_seat *seat, | ||
164 | struct sway_input_device *input_device); | ||
165 | |||
160 | void seat_reset_device(struct sway_seat *seat, | 166 | void seat_reset_device(struct sway_seat *seat, |
161 | struct sway_input_device *input_device); | 167 | struct sway_input_device *input_device); |
162 | 168 | ||
@@ -187,8 +193,7 @@ void seat_set_focus_surface(struct sway_seat *seat, | |||
187 | void seat_set_focus_layer(struct sway_seat *seat, | 193 | void seat_set_focus_layer(struct sway_seat *seat, |
188 | struct wlr_layer_surface_v1 *layer); | 194 | struct wlr_layer_surface_v1 *layer); |
189 | 195 | ||
190 | void seat_set_exclusive_client(struct sway_seat *seat, | 196 | void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client); |
191 | struct wl_client *client); | ||
192 | 197 | ||
193 | struct sway_node *seat_get_focus(struct sway_seat *seat); | 198 | struct sway_node *seat_get_focus(struct sway_seat *seat); |
194 | 199 | ||
@@ -201,10 +206,6 @@ struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat); | |||
201 | 206 | ||
202 | struct sway_container *seat_get_focused_container(struct sway_seat *seat); | 207 | struct sway_container *seat_get_focused_container(struct sway_seat *seat); |
203 | 208 | ||
204 | // Force focus to a particular surface that is not part of the workspace | ||
205 | // hierarchy (used for lockscreen) | ||
206 | void sway_force_focus(struct wlr_surface *surface); | ||
207 | |||
208 | /** | 209 | /** |
209 | * Return the last container to be focused for the seat (or the most recently | 210 | * Return the last container to be focused for the seat (or the most recently |
210 | * opened if no container has received focused) that is a child of the given | 211 | * opened if no container has received focused) that is a child of the given |
@@ -251,7 +252,7 @@ void seat_idle_notify_activity(struct sway_seat *seat, | |||
251 | 252 | ||
252 | 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); |
253 | 254 | ||
254 | void drag_icon_update_position(struct sway_drag_icon *icon); | 255 | void drag_icons_update_position(struct sway_seat *seat); |
255 | 256 | ||
256 | enum wlr_edges find_resize_edge(struct sway_container *cont, | 257 | enum wlr_edges find_resize_edge(struct sway_container *cont, |
257 | struct wlr_surface *surface, struct sway_cursor *cursor); | 258 | struct wlr_surface *surface, struct sway_cursor *cursor); |
@@ -259,10 +260,13 @@ enum wlr_edges find_resize_edge(struct sway_container *cont, | |||
259 | void seatop_begin_default(struct sway_seat *seat); | 260 | void seatop_begin_default(struct sway_seat *seat); |
260 | 261 | ||
261 | 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, |
262 | uint32_t time_msec, double sx, double sy); | 263 | double sx, double sy); |
263 | 264 | ||
264 | void seatop_begin_down_on_surface(struct sway_seat *seat, | 265 | void seatop_begin_down_on_surface(struct sway_seat *seat, |
265 | struct wlr_surface *surface, uint32_t time_msec, double sx, double sy); | 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); | ||
266 | 270 | ||
267 | void seatop_begin_move_floating(struct sway_seat *seat, | 271 | void seatop_begin_move_floating(struct sway_seat *seat, |
268 | struct sway_container *con); | 272 | struct sway_container *con); |
@@ -283,13 +287,13 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, | |||
283 | struct sway_workspace *workspace); | 287 | struct sway_workspace *workspace); |
284 | 288 | ||
285 | 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, |
286 | uint32_t button, enum wlr_button_state state); | 290 | uint32_t button, enum wl_pointer_button_state state); |
287 | 291 | ||
288 | void seat_consider_warp_to_focus(struct sway_seat *seat); | 292 | void seat_consider_warp_to_focus(struct sway_seat *seat); |
289 | 293 | ||
290 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, | 294 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, |
291 | struct wlr_input_device *device, uint32_t button, | 295 | struct wlr_input_device *device, uint32_t button, |
292 | enum wlr_button_state state); | 296 | enum wl_pointer_button_state state); |
293 | 297 | ||
294 | 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); |
295 | 299 | ||
@@ -322,6 +326,18 @@ void seatop_swipe_update(struct sway_seat *seat, | |||
322 | void seatop_swipe_end(struct sway_seat *seat, | 326 | void seatop_swipe_end(struct sway_seat *seat, |
323 | struct wlr_pointer_swipe_end_event *event); | 327 | struct wlr_pointer_swipe_end_event *event); |
324 | 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 | |||
325 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); | 341 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); |
326 | 342 | ||
327 | /** | 343 | /** |
@@ -336,13 +352,6 @@ void seatop_end(struct sway_seat *seat); | |||
336 | */ | 352 | */ |
337 | void seatop_unref(struct sway_seat *seat, struct sway_container *con); | 353 | void seatop_unref(struct sway_seat *seat, struct sway_container *con); |
338 | 354 | ||
339 | /** | ||
340 | * Instructs a seatop to render anything that it needs to render | ||
341 | * (eg. dropzone for move-tiling) | ||
342 | */ | ||
343 | void seatop_render(struct sway_seat *seat, struct sway_output *output, | ||
344 | pixman_region32_t *damage); | ||
345 | |||
346 | bool seatop_allows_set_cursor(struct sway_seat *seat); | 355 | bool seatop_allows_set_cursor(struct sway_seat *seat); |
347 | 356 | ||
348 | /** | 357 | /** |
diff --git a/include/sway/input/tablet.h b/include/sway/input/tablet.h index c0a5aff7..2fa5db6d 100644 --- a/include/sway/input/tablet.h +++ b/include/sway/input/tablet.h | |||
@@ -63,7 +63,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad); | |||
63 | 63 | ||
64 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad); | 64 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad); |
65 | 65 | ||
66 | 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, |
67 | struct wlr_surface *surface); | 67 | struct wlr_surface *surface); |
68 | 68 | ||
69 | #endif | 69 | #endif |
diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h index c70fd935..1993f928 100644 --- a/include/sway/input/text_input.h +++ b/include/sway/input/text_input.h | |||
@@ -4,12 +4,11 @@ | |||
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_compositor.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 f8508493..fd6384e0 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h | |||
@@ -3,61 +3,42 @@ | |||
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include <wlr/types/wlr_compositor.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; | ||
25 | bool mapped; | 16 | bool mapped; |
26 | struct wlr_box extent; | ||
27 | enum zwlr_layer_shell_v1_layer layer; | ||
28 | 17 | ||
29 | struct wl_list subsurfaces; | 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; | ||
30 | }; | 25 | }; |
31 | 26 | ||
32 | struct sway_layer_popup { | 27 | struct sway_layer_popup { |
33 | struct wlr_xdg_popup *wlr_popup; | 28 | struct wlr_xdg_popup *wlr_popup; |
34 | enum layer_parent parent_type; | 29 | struct wlr_scene_tree *scene; |
35 | union { | 30 | struct sway_layer_surface *toplevel; |
36 | struct sway_layer_surface *parent_layer; | ||
37 | struct sway_layer_popup *parent_popup; | ||
38 | }; | ||
39 | struct wl_listener map; | ||
40 | struct wl_listener unmap; | ||
41 | struct wl_listener destroy; | ||
42 | struct wl_listener commit; | ||
43 | struct wl_listener new_popup; | ||
44 | }; | ||
45 | 31 | ||
46 | struct sway_layer_subsurface { | ||
47 | struct wlr_subsurface *wlr_subsurface; | ||
48 | struct sway_layer_surface *layer_surface; | ||
49 | struct wl_list link; | ||
50 | |||
51 | struct wl_listener map; | ||
52 | struct wl_listener unmap; | ||
53 | struct wl_listener destroy; | 32 | struct wl_listener destroy; |
33 | struct wl_listener new_popup; | ||
54 | struct wl_listener commit; | 34 | struct wl_listener commit; |
55 | }; | 35 | }; |
56 | 36 | ||
57 | struct sway_output; | 37 | struct sway_output; |
58 | void arrange_layers(struct sway_output *output); | ||
59 | 38 | ||
60 | struct sway_layer_surface *layer_from_wlr_layer_surface_v1( | 39 | struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( |
61 | struct wlr_layer_surface_v1 *layer_surface); | 40 | struct wlr_surface *surface); |
41 | |||
42 | void arrange_layers(struct sway_output *output); | ||
62 | 43 | ||
63 | #endif | 44 | #endif |
diff --git a/include/sway/output.h b/include/sway/output.h index 6d8319bf..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,34 +20,47 @@ 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 powered off | ||
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 disable; | 66 | struct wl_signal disable; |
@@ -55,6 +70,13 @@ struct sway_output { | |||
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 4cce17cc..c71851f6 100644 --- a/include/sway/server.h +++ b/include/sway/server.h | |||
@@ -2,45 +2,41 @@ | |||
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/allocator.h> | ||
8 | #include <wlr/render/wlr_renderer.h> | ||
9 | #include <wlr/types/wlr_compositor.h> | ||
10 | #include <wlr/types/wlr_data_device.h> | ||
11 | #include <wlr/types/wlr_input_method_v2.h> | ||
12 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
13 | #include <wlr/types/wlr_drm_lease_v1.h> | ||
14 | #include <wlr/types/wlr_layer_shell_v1.h> | ||
15 | #include <wlr/types/wlr_output_management_v1.h> | ||
16 | #include <wlr/types/wlr_output_power_management_v1.h> | ||
17 | #include <wlr/types/wlr_presentation_time.h> | ||
18 | #include <wlr/types/wlr_relative_pointer_v1.h> | ||
19 | #include <wlr/types/wlr_session_lock_v1.h> | ||
20 | #include <wlr/types/wlr_server_decoration.h> | ||
21 | #include <wlr/types/wlr_text_input_v3.h> | ||
22 | #include <wlr/types/wlr_xdg_shell.h> | ||
23 | #include "config.h" | 5 | #include "config.h" |
24 | #include "list.h" | 6 | #include "list.h" |
7 | #include "sway/desktop/idle_inhibit_v1.h" | ||
25 | #if HAVE_XWAYLAND | 8 | #if HAVE_XWAYLAND |
26 | #include "sway/xwayland.h" | 9 | #include "sway/xwayland.h" |
27 | #endif | 10 | #endif |
28 | 11 | ||
29 | struct sway_transaction; | 12 | struct sway_transaction; |
30 | 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 | |||
31 | struct sway_server { | 27 | struct sway_server { |
32 | struct wl_display *wl_display; | 28 | struct wl_display *wl_display; |
33 | struct wl_event_loop *wl_event_loop; | 29 | struct wl_event_loop *wl_event_loop; |
34 | const char *socket; | 30 | const char *socket; |
35 | 31 | ||
36 | struct wlr_backend *backend; | 32 | struct wlr_backend *backend; |
33 | struct wlr_session *session; | ||
37 | // secondary headless backend used for creating virtual outputs on-the-fly | 34 | // secondary headless backend used for creating virtual outputs on-the-fly |
38 | struct wlr_backend *headless_backend; | 35 | struct wlr_backend *headless_backend; |
39 | struct wlr_renderer *renderer; | 36 | struct wlr_renderer *renderer; |
40 | struct wlr_allocator *allocator; | 37 | struct wlr_allocator *allocator; |
41 | 38 | ||
42 | struct wlr_compositor *compositor; | 39 | struct wlr_compositor *compositor; |
43 | struct wl_listener compositor_new_surface; | ||
44 | 40 | ||
45 | struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; | 41 | struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; |
46 | 42 | ||
@@ -50,15 +46,16 @@ struct sway_server { | |||
50 | 46 | ||
51 | struct wl_listener new_output; | 47 | struct wl_listener new_output; |
52 | struct wl_listener output_layout_change; | 48 | struct wl_listener output_layout_change; |
49 | struct wl_listener renderer_lost; | ||
53 | 50 | ||
54 | struct wlr_idle *idle; | 51 | struct wlr_idle_notifier_v1 *idle_notifier_v1; |
55 | struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1; | 52 | struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; |
56 | 53 | ||
57 | struct wlr_layer_shell_v1 *layer_shell; | 54 | struct wlr_layer_shell_v1 *layer_shell; |
58 | struct wl_listener layer_shell_surface; | 55 | struct wl_listener layer_shell_surface; |
59 | 56 | ||
60 | struct wlr_xdg_shell *xdg_shell; | 57 | struct wlr_xdg_shell *xdg_shell; |
61 | struct wl_listener xdg_shell_surface; | 58 | struct wl_listener xdg_shell_toplevel; |
62 | 59 | ||
63 | struct wlr_tablet_manager_v2 *tablet_v2; | 60 | struct wlr_tablet_manager_v2 *tablet_v2; |
64 | 61 | ||
@@ -81,8 +78,6 @@ struct sway_server { | |||
81 | struct wlr_drm_lease_v1_manager *drm_lease_manager; | 78 | struct wlr_drm_lease_v1_manager *drm_lease_manager; |
82 | struct wl_listener drm_lease_request; | 79 | struct wl_listener drm_lease_request; |
83 | 80 | ||
84 | struct wlr_presentation *presentation; | ||
85 | |||
86 | struct wlr_pointer_constraints_v1 *pointer_constraints; | 81 | struct wlr_pointer_constraints_v1 *pointer_constraints; |
87 | struct wl_listener pointer_constraint; | 82 | struct wl_listener pointer_constraint; |
88 | 83 | ||
@@ -90,15 +85,13 @@ struct sway_server { | |||
90 | struct wl_listener output_manager_apply; | 85 | struct wl_listener output_manager_apply; |
91 | struct wl_listener output_manager_test; | 86 | struct wl_listener output_manager_test; |
92 | 87 | ||
88 | struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; | ||
89 | struct wl_listener gamma_control_set_gamma; | ||
90 | |||
93 | struct { | 91 | struct { |
94 | bool locked; | 92 | struct sway_session_lock *lock; |
95 | struct wlr_session_lock_manager_v1 *manager; | 93 | struct wlr_session_lock_manager_v1 *manager; |
96 | 94 | ||
97 | struct wlr_session_lock_v1 *lock; | ||
98 | struct wl_listener lock_new_surface; | ||
99 | struct wl_listener lock_unlock; | ||
100 | struct wl_listener lock_destroy; | ||
101 | |||
102 | struct wl_listener new_lock; | 95 | struct wl_listener new_lock; |
103 | struct wl_listener manager_destroy; | 96 | struct wl_listener manager_destroy; |
104 | } session_lock; | 97 | } session_lock; |
@@ -107,10 +100,21 @@ struct sway_server { | |||
107 | struct wl_listener output_power_manager_set_mode; | 100 | struct wl_listener output_power_manager_set_mode; |
108 | struct wlr_input_method_manager_v2 *input_method; | 101 | struct wlr_input_method_manager_v2 *input_method; |
109 | 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; | ||
110 | 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; | ||
111 | 110 | ||
112 | struct wlr_xdg_activation_v1 *xdg_activation_v1; | 111 | struct wlr_xdg_activation_v1 *xdg_activation_v1; |
113 | 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 | ||
114 | 118 | ||
115 | // The timeout for transactions, after which a transaction is applied | 119 | // The timeout for transactions, after which a transaction is applied |
116 | // regardless of readiness. | 120 | // regardless of readiness. |
@@ -137,17 +141,13 @@ struct sway_debug { | |||
137 | bool noatomic; // Ignore atomic layout updates | 141 | bool noatomic; // Ignore atomic layout updates |
138 | bool txn_timings; // Log verbose messages about transactions | 142 | bool txn_timings; // Log verbose messages about transactions |
139 | bool txn_wait; // Always wait for the timeout before applying | 143 | bool txn_wait; // Always wait for the timeout before applying |
140 | bool noscanout; // Disable direct scan-out | 144 | bool legacy_wl_drm; // Enable the legacy wl_drm interface |
141 | |||
142 | enum { | ||
143 | DAMAGE_DEFAULT, // Default behaviour | ||
144 | DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged | ||
145 | DAMAGE_RERENDER, // Render the full output when any damage occurs | ||
146 | } damage; | ||
147 | }; | 145 | }; |
148 | 146 | ||
149 | extern struct sway_debug debug; | 147 | extern struct sway_debug debug; |
150 | 148 | ||
149 | extern bool allow_unsupported_gpu; | ||
150 | |||
151 | bool server_init(struct sway_server *server); | 151 | bool server_init(struct sway_server *server); |
152 | void server_fini(struct sway_server *server); | 152 | void server_fini(struct sway_server *server); |
153 | bool server_start(struct sway_server *server); | 153 | bool server_start(struct sway_server *server); |
@@ -155,13 +155,16 @@ void server_run(struct sway_server *server); | |||
155 | 155 | ||
156 | void restore_nofile_limit(void); | 156 | void restore_nofile_limit(void); |
157 | 157 | ||
158 | void handle_compositor_new_surface(struct wl_listener *listener, void *data); | ||
159 | void handle_new_output(struct wl_listener *listener, void *data); | 158 | void handle_new_output(struct wl_listener *listener, void *data); |
160 | 159 | ||
161 | void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); | 160 | void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); |
162 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); | 161 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); |
163 | void sway_session_lock_init(void); | 162 | void sway_session_lock_init(void); |
164 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data); | 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); | ||
165 | #if HAVE_XWAYLAND | 168 | #if HAVE_XWAYLAND |
166 | void handle_xwayland_surface(struct wl_listener *listener, void *data); | 169 | void handle_xwayland_surface(struct wl_listener *listener, void *data); |
167 | #endif | 170 | #endif |
@@ -170,6 +173,8 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data); | |||
170 | void handle_pointer_constraint(struct wl_listener *listener, void *data); | 173 | void handle_pointer_constraint(struct wl_listener *listener, void *data); |
171 | void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, | 174 | void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, |
172 | void *data); | 175 | void *data); |
176 | void xdg_activation_v1_handle_new_token(struct wl_listener *listener, | ||
177 | void *data); | ||
173 | 178 | ||
174 | void set_rr_scheduling(void); | 179 | void set_rr_scheduling(void); |
175 | 180 | ||
diff --git a/include/sway/surface.h b/include/sway/surface.h deleted file mode 100644 index fb1cd775..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_compositor.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 751612e2..93f6bfbb 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <stdint.h> | 3 | #include <stdint.h> |
4 | #include <sys/types.h> | 4 | #include <sys/types.h> |
5 | #include <wlr/types/wlr_compositor.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_focused_tab_title; | ||
121 | struct wlr_texture *title_unfocused; | ||
122 | struct wlr_texture *title_urgent; | ||
123 | 143 | ||
124 | list_t *marks; // char * | 144 | list_t *marks; // char * |
125 | struct wlr_texture *marks_focused; | ||
126 | struct wlr_texture *marks_focused_inactive; | ||
127 | struct wlr_texture *marks_focused_tab_title; | ||
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,13 +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 | |||
186 | void container_update_marks(struct sway_container *container); | ||
185 | 187 | ||
186 | size_t container_build_representation(enum sway_container_layout layout, | 188 | size_t container_build_representation(enum sway_container_layout layout, |
187 | list_t *children, char *buffer); | 189 | list_t *children, char *buffer); |
@@ -196,6 +198,9 @@ size_t container_titlebar_height(void); | |||
196 | void floating_calculate_constraints(int *min_width, int *max_width, | 198 | void floating_calculate_constraints(int *min_width, int *max_width, |
197 | int *min_height, int *max_height); | 199 | int *min_height, int *max_height); |
198 | 200 | ||
201 | void floating_fix_coordinates(struct sway_container *con, | ||
202 | struct wlr_box *old, struct wlr_box *new); | ||
203 | |||
199 | void container_floating_resize_and_center(struct sway_container *con); | 204 | void container_floating_resize_and_center(struct sway_container *con); |
200 | 205 | ||
201 | void container_floating_set_default_size(struct sway_container *con); | 206 | void container_floating_set_default_size(struct sway_container *con); |
@@ -215,11 +220,6 @@ void container_set_geometry_from_content(struct sway_container *con); | |||
215 | bool container_is_floating(struct sway_container *container); | 220 | bool container_is_floating(struct sway_container *container); |
216 | 221 | ||
217 | /** | 222 | /** |
218 | * Same as above, but for current container state. | ||
219 | */ | ||
220 | bool container_is_current_floating(struct sway_container *container); | ||
221 | |||
222 | /** | ||
223 | * Get a container's box in layout coordinates. | 223 | * Get a container's box in layout coordinates. |
224 | */ | 224 | */ |
225 | 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); |
@@ -281,26 +281,12 @@ bool container_is_floating_or_child(struct sway_container *container); | |||
281 | */ | 281 | */ |
282 | bool container_is_fullscreen_or_child(struct sway_container *container); | 282 | bool container_is_fullscreen_or_child(struct sway_container *container); |
283 | 283 | ||
284 | /** | ||
285 | * Return the output which will be used for scale purposes. | ||
286 | * This is the most recently entered output. | ||
287 | * If the container is not on any output, return NULL. | ||
288 | */ | ||
289 | struct sway_output *container_get_effective_output(struct sway_container *con); | ||
290 | |||
291 | void container_discover_outputs(struct sway_container *con); | ||
292 | |||
293 | enum sway_container_layout container_parent_layout(struct sway_container *con); | 284 | enum sway_container_layout container_parent_layout(struct sway_container *con); |
294 | 285 | ||
295 | enum sway_container_layout container_current_parent_layout( | ||
296 | struct sway_container *con); | ||
297 | |||
298 | list_t *container_get_siblings(struct sway_container *container); | 286 | list_t *container_get_siblings(struct sway_container *container); |
299 | 287 | ||
300 | int container_sibling_index(struct sway_container *child); | 288 | int container_sibling_index(struct sway_container *child); |
301 | 289 | ||
302 | list_t *container_get_current_siblings(struct sway_container *container); | ||
303 | |||
304 | void container_handle_fullscreen_reparent(struct sway_container *con); | 290 | void container_handle_fullscreen_reparent(struct sway_container *con); |
305 | 291 | ||
306 | void container_add_child(struct sway_container *parent, | 292 | void container_add_child(struct sway_container *parent, |
@@ -348,8 +334,6 @@ bool container_has_mark(struct sway_container *container, char *mark); | |||
348 | 334 | ||
349 | void container_add_mark(struct sway_container *container, char *mark); | 335 | void container_add_mark(struct sway_container *container, char *mark); |
350 | 336 | ||
351 | void container_update_marks_textures(struct sway_container *container); | ||
352 | |||
353 | void container_raise_floating(struct sway_container *con); | 337 | void container_raise_floating(struct sway_container *con); |
354 | 338 | ||
355 | bool container_is_scratchpad_hidden(struct sway_container *con); | 339 | bool container_is_scratchpad_hidden(struct sway_container *con); |
@@ -373,4 +357,10 @@ bool container_is_sticky_or_child(struct sway_container *con); | |||
373 | */ | 357 | */ |
374 | int container_squash(struct sway_container *con); | 358 | int container_squash(struct sway_container *con); |
375 | 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 | |||
376 | #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 5d4a2f2d..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,6 +63,7 @@ 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 |
@@ -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 0dcbf1aa..7faacdcc 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -2,7 +2,8 @@ | |||
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_compositor.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; |
@@ -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,72 +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 | ||
173 | struct wl_listener request_activate; | 166 | struct wl_listener request_activate; |
174 | struct wl_listener request_configure; | 167 | struct wl_listener request_configure; |
175 | struct wl_listener request_fullscreen; | 168 | struct wl_listener request_fullscreen; |
176 | struct wl_listener commit; | ||
177 | struct wl_listener set_geometry; | 169 | struct wl_listener set_geometry; |
170 | struct wl_listener associate; | ||
171 | struct wl_listener dissociate; | ||
178 | struct wl_listener map; | 172 | struct wl_listener map; |
179 | struct wl_listener unmap; | 173 | struct wl_listener unmap; |
180 | struct wl_listener destroy; | 174 | struct wl_listener destroy; |
181 | struct wl_listener override_redirect; | 175 | struct wl_listener override_redirect; |
182 | }; | 176 | }; |
183 | #endif | 177 | #endif |
184 | struct sway_view_child; | ||
185 | |||
186 | struct sway_view_child_impl { | ||
187 | void (*get_view_coords)(struct sway_view_child *child, int *sx, int *sy); | ||
188 | void (*destroy)(struct sway_view_child *child); | ||
189 | }; | ||
190 | |||
191 | /** | ||
192 | * A view child is a surface in the view tree, such as a subsurface or a popup. | ||
193 | */ | ||
194 | struct sway_view_child { | ||
195 | const struct sway_view_child_impl *impl; | ||
196 | struct wl_list link; | ||
197 | 178 | ||
179 | struct sway_popup_desc { | ||
180 | struct wlr_scene_node *relative; | ||
198 | struct sway_view *view; | 181 | struct sway_view *view; |
199 | struct sway_view_child *parent; | ||
200 | struct wl_list children; // sway_view_child::link | ||
201 | struct wlr_surface *surface; | ||
202 | bool mapped; | ||
203 | |||
204 | struct wl_listener surface_commit; | ||
205 | struct wl_listener surface_new_subsurface; | ||
206 | struct wl_listener surface_map; | ||
207 | struct wl_listener surface_unmap; | ||
208 | struct wl_listener surface_destroy; | ||
209 | struct wl_listener view_unmap; | ||
210 | }; | ||
211 | |||
212 | struct sway_subsurface { | ||
213 | struct sway_view_child child; | ||
214 | |||
215 | struct wl_listener destroy; | ||
216 | }; | 182 | }; |
217 | 183 | ||
218 | struct sway_xdg_popup { | 184 | struct sway_xdg_popup { |
219 | struct sway_view_child child; | 185 | struct sway_view *view; |
220 | 186 | ||
187 | struct wlr_scene_tree *scene_tree; | ||
188 | struct wlr_scene_tree *xdg_surface_tree; | ||
221 | struct wlr_xdg_popup *wlr_xdg_popup; | 189 | struct wlr_xdg_popup *wlr_xdg_popup; |
222 | 190 | ||
191 | struct sway_popup_desc desc; | ||
192 | |||
193 | struct wl_listener surface_commit; | ||
223 | struct wl_listener new_popup; | 194 | struct wl_listener new_popup; |
195 | struct wl_listener reposition; | ||
224 | struct wl_listener destroy; | 196 | struct wl_listener destroy; |
225 | }; | 197 | }; |
226 | 198 | ||
@@ -269,7 +241,12 @@ void view_set_activated(struct sway_view *view, bool activated); | |||
269 | /** | 241 | /** |
270 | * Called when the view requests to be focused. | 242 | * Called when the view requests to be focused. |
271 | */ | 243 | */ |
272 | 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); | ||
273 | 250 | ||
274 | /** | 251 | /** |
275 | * If possible, instructs the client to change their decoration mode. | 252 | * If possible, instructs the client to change their decoration mode. |
@@ -288,23 +265,9 @@ void view_close(struct sway_view *view); | |||
288 | 265 | ||
289 | void view_close_popups(struct sway_view *view); | 266 | void view_close_popups(struct sway_view *view); |
290 | 267 | ||
291 | void view_damage_from(struct sway_view *view); | ||
292 | |||
293 | /** | ||
294 | * Iterate all surfaces of a view (toplevels + popups). | ||
295 | */ | ||
296 | void view_for_each_surface(struct sway_view *view, | ||
297 | wlr_surface_iterator_func_t iterator, void *user_data); | ||
298 | |||
299 | /** | ||
300 | * Iterate all popup surfaces of a view. | ||
301 | */ | ||
302 | void view_for_each_popup_surface(struct sway_view *view, | ||
303 | wlr_surface_iterator_func_t iterator, void *user_data); | ||
304 | |||
305 | // view implementation | 268 | // view implementation |
306 | 269 | ||
307 | 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, |
308 | const struct sway_view_impl *impl); | 271 | const struct sway_view_impl *impl); |
309 | 272 | ||
310 | void view_destroy(struct sway_view *view); | 273 | void view_destroy(struct sway_view *view); |
@@ -326,14 +289,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
326 | void view_unmap(struct sway_view *view); | 289 | void view_unmap(struct sway_view *view); |
327 | 290 | ||
328 | void view_update_size(struct sway_view *view); | 291 | void view_update_size(struct sway_view *view); |
329 | void view_center_surface(struct sway_view *view); | 292 | void view_center_and_clip_surface(struct sway_view *view); |
330 | |||
331 | void view_child_init(struct sway_view_child *child, | ||
332 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
333 | struct wlr_surface *surface); | ||
334 | |||
335 | void view_child_destroy(struct sway_view_child *child); | ||
336 | |||
337 | 293 | ||
338 | struct sway_view *view_from_wlr_xdg_surface( | 294 | struct sway_view *view_from_wlr_xdg_surface( |
339 | struct wlr_xdg_surface *xdg_surface); | 295 | struct wlr_xdg_surface *xdg_surface); |
@@ -343,6 +299,8 @@ struct sway_view *view_from_wlr_xwayland_surface( | |||
343 | #endif | 299 | #endif |
344 | struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); | 300 | struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); |
345 | 301 | ||
302 | void view_update_app_id(struct sway_view *view); | ||
303 | |||
346 | /** | 304 | /** |
347 | * 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. |
348 | * 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 |
@@ -372,4 +330,8 @@ void view_save_buffer(struct sway_view *view); | |||
372 | 330 | ||
373 | 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); |
374 | 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 | |||
375 | #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 3ad0bdf3..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; |
diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h index 1aec6d6c..dced2a6c 100644 --- a/include/swaybar/i3bar.h +++ b/include/swaybar/i3bar.h | |||
@@ -30,6 +30,6 @@ void i3bar_block_unref(struct i3bar_block *block); | |||
30 | bool i3bar_handle_readable(struct status_line *status); | 30 | bool i3bar_handle_readable(struct status_line *status); |
31 | 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, |
32 | 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, |
33 | double w, double h, int scale, uint32_t button); | 33 | double w, double h, int scale, uint32_t button, bool released); |
34 | 34 | ||
35 | #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 2d68b6c9..fb9e9c21 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h | |||
@@ -4,6 +4,8 @@ | |||
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 | 10 | ||
9 | #define SWAYNAG_MAX_HEIGHT 500 | 11 | #define SWAYNAG_MAX_HEIGHT 500 |
@@ -58,6 +60,7 @@ struct swaynag_button { | |||
58 | struct swaynag_details { | 60 | struct swaynag_details { |
59 | bool visible; | 61 | bool visible; |
60 | char *message; | 62 | char *message; |
63 | char *details_text; | ||
61 | 64 | ||
62 | int x; | 65 | int x; |
63 | int y; | 66 | int y; |
@@ -67,7 +70,7 @@ struct swaynag_details { | |||
67 | int offset; | 70 | int offset; |
68 | int visible_lines; | 71 | int visible_lines; |
69 | int total_lines; | 72 | int total_lines; |
70 | struct swaynag_button button_details; | 73 | struct swaynag_button *button_details; |
71 | struct swaynag_button button_up; | 74 | struct swaynag_button button_up; |
72 | struct swaynag_button button_down; | 75 | struct swaynag_button button_down; |
73 | }; | 76 | }; |
@@ -84,6 +87,7 @@ struct swaynag { | |||
84 | struct swaynag_output *output; | 87 | struct swaynag_output *output; |
85 | struct zwlr_layer_shell_v1 *layer_shell; | 88 | struct zwlr_layer_shell_v1 *layer_shell; |
86 | 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; | ||
87 | struct wl_surface *surface; | 91 | struct wl_surface *surface; |
88 | 92 | ||
89 | uint32_t width; | 93 | uint32_t width; |
diff --git a/include/swaynag/types.h b/include/swaynag/types.h index 18f218e0..9c3c50db 100644 --- a/include/swaynag/types.h +++ b/include/swaynag/types.h | |||
@@ -1,10 +1,13 @@ | |||
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; // Used for debugging. | ||
8 | PangoFontDescription *font_description; | 11 | PangoFontDescription *font_description; |
9 | char *output; | 12 | char *output; |
10 | uint32_t anchors; | 13 | uint32_t anchors; |
diff --git a/meson.build b/meson.build index d23300be..1043e4ba 100644 --- a/meson.build +++ b/meson.build | |||
@@ -1,7 +1,7 @@ | |||
1 | project( | 1 | project( |
2 | 'sway', | 2 | 'sway', |
3 | 'c', | 3 | 'c', |
4 | version: '1.8-dev', | 4 | version: '1.10-dev', |
5 | license: 'MIT', | 5 | license: 'MIT', |
6 | meson_version: '>=0.60.0', | 6 | meson_version: '>=0.60.0', |
7 | default_options: [ | 7 | default_options: [ |
@@ -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 | ], |
@@ -36,55 +38,54 @@ if is_freebsd | |||
36 | endif | 38 | endif |
37 | 39 | ||
38 | # Execute the wlroots subproject, if any | 40 | # Execute the wlroots subproject, if any |
39 | wlroots_version = ['>=0.16.0', '<0.17.0'] | 41 | wlroots_version = ['>=0.18.0', '<0.19.0'] |
40 | subproject( | 42 | subproject( |
41 | 'wlroots', | 43 | 'wlroots', |
42 | default_options: ['examples=false'], | 44 | default_options: ['examples=false'], |
43 | required: false, | 45 | required: false, |
44 | version: wlroots_version, | 46 | version: wlroots_version, |
45 | ) | 47 | ) |
48 | wlroots = dependency('wlroots', version: wlroots_version) | ||
49 | wlroots_features = { | ||
50 | 'xwayland': false, | ||
51 | 'libinput_backend': false, | ||
52 | 'session': false, | ||
53 | } | ||
54 | foreach name, _ : wlroots_features | ||
55 | var_name = 'have_' + name.underscorify() | ||
56 | have = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true' | ||
57 | wlroots_features += { name: have } | ||
58 | endforeach | ||
59 | |||
60 | if get_option('xwayland').enabled() and not wlroots_features['xwayland'] | ||
61 | error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') | ||
62 | endif | ||
63 | |||
64 | null_dep = dependency('', required: false) | ||
46 | 65 | ||
47 | jsonc = dependency('json-c', version: '>=0.13') | 66 | jsonc = dependency('json-c', version: '>=0.13') |
48 | pcre2 = dependency('libpcre2-8') | 67 | pcre2 = dependency('libpcre2-8') |
49 | wayland_server = dependency('wayland-server', version: '>=1.20.0') | 68 | wayland_server = dependency('wayland-server', version: '>=1.21.0') |
50 | wayland_client = dependency('wayland-client') | 69 | wayland_client = dependency('wayland-client') |
51 | wayland_cursor = dependency('wayland-cursor') | 70 | wayland_cursor = dependency('wayland-cursor') |
52 | wayland_egl = dependency('wayland-egl') | ||
53 | wayland_protos = dependency('wayland-protocols', version: '>=1.24') | 71 | wayland_protos = dependency('wayland-protocols', version: '>=1.24') |
54 | wlroots = dependency('wlroots', version: wlroots_version) | 72 | xkbcommon = dependency('xkbcommon', version: '>=1.5.0') |
55 | xkbcommon = dependency('xkbcommon') | ||
56 | cairo = dependency('cairo') | 73 | cairo = dependency('cairo') |
57 | pango = dependency('pango') | 74 | pango = dependency('pango') |
58 | pangocairo = dependency('pangocairo') | 75 | pangocairo = dependency('pangocairo') |
59 | gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) | 76 | gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) |
60 | pixman = dependency('pixman-1') | 77 | pixman = dependency('pixman-1') |
61 | glesv2 = dependency('glesv2') | ||
62 | libevdev = dependency('libevdev') | 78 | libevdev = dependency('libevdev') |
63 | libinput = dependency('libinput', version: '>=1.6.0') | 79 | libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.21.0') : null_dep |
64 | xcb = dependency('xcb', required: get_option('xwayland')) | 80 | xcb = dependency('xcb', required: get_option('xwayland')) |
65 | drm_full = dependency('libdrm') # only needed for drm_fourcc.h | 81 | drm = dependency('libdrm') |
66 | drm = drm_full.partial_dependency(compile_args: true, includes: true) | 82 | libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep |
67 | libudev = dependency('libudev') | ||
68 | bash_comp = dependency('bash-completion', required: false) | ||
69 | fish_comp = dependency('fish', required: false) | ||
70 | math = cc.find_library('m') | 83 | math = cc.find_library('m') |
71 | rt = cc.find_library('rt') | 84 | rt = cc.find_library('rt') |
72 | xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) | 85 | xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) |
73 | threads = dependency('threads') # for pthread_setschedparam | 86 | threads = dependency('threads') # for pthread_setschedparam |
74 | 87 | ||
75 | wlroots_features = { | 88 | have_xwayland = xcb.found() and xcb_icccm.found() and wlroots_features['xwayland'] |
76 | 'xwayland': false, | ||
77 | } | ||
78 | foreach name, _ : wlroots_features | ||
79 | var_name = 'have_' + name.underscorify() | ||
80 | have = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true' | ||
81 | wlroots_features += { name: have } | ||
82 | endforeach | ||
83 | |||
84 | if get_option('xwayland').enabled() and not wlroots_features['xwayland'] | ||
85 | error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') | ||
86 | endif | ||
87 | have_xwayland = xcb.found() and wlroots_features['xwayland'] | ||
88 | 89 | ||
89 | if get_option('sd-bus-provider') == 'auto' | 90 | if get_option('sd-bus-provider') == 'auto' |
90 | if not get_option('tray').disabled() | 91 | if not get_option('tray').disabled() |
@@ -115,6 +116,11 @@ conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd | |||
115 | conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') | 116 | conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') |
116 | conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') | 117 | conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') |
117 | 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 | )) | ||
118 | 124 | ||
119 | 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')) |
120 | if scdoc.found() | 126 | if scdoc.found() |
@@ -262,59 +268,7 @@ if get_option('default-wallpaper') | |||
262 | install_data(wallpaper_files, install_dir: wallpaper_install_dir) | 268 | install_data(wallpaper_files, install_dir: wallpaper_install_dir) |
263 | endif | 269 | endif |
264 | 270 | ||
265 | if get_option('zsh-completions') | 271 | subdir('completions') |
266 | zsh_files = files( | ||
267 | 'completions/zsh/_sway', | ||
268 | 'completions/zsh/_swaymsg', | ||
269 | ) | ||
270 | zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions') | ||
271 | |||
272 | install_data(zsh_files, install_dir: zsh_install_dir) | ||
273 | endif | ||
274 | |||
275 | if get_option('bash-completions') | ||
276 | bash_files = files( | ||
277 | 'completions/bash/sway', | ||
278 | 'completions/bash/swaymsg', | ||
279 | ) | ||
280 | |||
281 | if get_option('swaybar') | ||
282 | bash_files += files('completions/bash/swaybar') | ||
283 | endif | ||
284 | |||
285 | if bash_comp.found() | ||
286 | bash_install_dir = bash_comp.get_variable( | ||
287 | pkgconfig: 'completionsdir', | ||
288 | pkgconfig_define: ['datadir', datadir] | ||
289 | ) | ||
290 | else | ||
291 | bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') | ||
292 | endif | ||
293 | |||
294 | install_data(bash_files, install_dir: bash_install_dir) | ||
295 | endif | ||
296 | |||
297 | if get_option('fish-completions') | ||
298 | fish_files = files( | ||
299 | 'completions/fish/sway.fish', | ||
300 | 'completions/fish/swaymsg.fish', | ||
301 | ) | ||
302 | |||
303 | if get_option('swaynag') | ||
304 | fish_files += files('completions/fish/swaynag.fish') | ||
305 | endif | ||
306 | |||
307 | if fish_comp.found() | ||
308 | fish_install_dir = fish_comp.get_variable( | ||
309 | pkgconfig: 'completionsdir', | ||
310 | pkgconfig_define: ['datadir', datadir] | ||
311 | ) | ||
312 | else | ||
313 | fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') | ||
314 | endif | ||
315 | |||
316 | install_data(fish_files, install_dir: fish_install_dir) | ||
317 | endif | ||
318 | 272 | ||
319 | summary({ | 273 | summary({ |
320 | 'xwayland': have_xwayland, | 274 | 'xwayland': have_xwayland, |
@@ -322,4 +276,3 @@ summary({ | |||
322 | 'tray': have_tray, | 276 | 'tray': have_tray, |
323 | 'man-pages': scdoc.found(), | 277 | 'man-pages': scdoc.found(), |
324 | }, bool_yn: true) | 278 | }, bool_yn: true) |
325 | |||
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 df24a4e5..81edb584 100644 --- a/protocols/meson.build +++ b/protocols/meson.build | |||
@@ -1,81 +1,43 @@ | |||
1 | wl_protocol_dir = wayland_protos.get_variable('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 | [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], | 14 | wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', |
19 | ['wlr-layer-shell-unstable-v1.xml'], | 15 | wl_protocol_dir / 'staging/content-type/content-type-v1.xml', |
20 | ['idle.xml'], | 16 | wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', |
21 | ['wlr-input-inhibitor-unstable-v1.xml'], | 17 | 'wlr-layer-shell-unstable-v1.xml', |
22 | ['wlr-output-power-management-unstable-v1.xml'], | 18 | 'idle.xml', |
23 | ] | 19 | 'wlr-output-power-management-unstable-v1.xml', |
24 | |||
25 | client_protocols = [ | ||
26 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | ||
27 | [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], | ||
28 | ['wlr-layer-shell-unstable-v1.xml'], | ||
29 | ['wlr-input-inhibitor-unstable-v1.xml'], | ||
30 | ] | 20 | ] |
31 | 21 | ||
32 | wl_protos_src = [] | 22 | wl_protos_src = [] |
33 | wl_protos_headers = [] | ||
34 | 23 | ||
35 | foreach p : protocols | 24 | foreach xml : protocols |
36 | xml = join_paths(p) | ||
37 | wl_protos_src += custom_target( | 25 | wl_protos_src += custom_target( |
38 | xml.underscorify() + '_server_c', | 26 | xml.underscorify() + '_c', |
39 | input: xml, | 27 | input: xml, |
40 | output: '@BASENAME@-protocol.c', | 28 | output: '@BASENAME@-protocol.c', |
41 | command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], | 29 | command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], |
42 | ) | 30 | ) |
43 | wl_protos_headers += custom_target( | 31 | wl_protos_src += custom_target( |
44 | xml.underscorify() + '_server_h', | 32 | xml.underscorify() + '_server_h', |
45 | input: xml, | 33 | input: xml, |
46 | output: '@BASENAME@-protocol.h', | 34 | output: '@BASENAME@-protocol.h', |
47 | command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], | 35 | command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], |
48 | ) | 36 | ) |
49 | endforeach | 37 | wl_protos_src += custom_target( |
50 | |||
51 | foreach p : client_protocols | ||
52 | xml = join_paths(p) | ||
53 | wl_protos_headers += custom_target( | ||
54 | xml.underscorify() + '_client_h', | 38 | xml.underscorify() + '_client_h', |
55 | input: xml, | 39 | input: xml, |
56 | output: '@BASENAME@-client-protocol.h', | 40 | output: '@BASENAME@-client-protocol.h', |
57 | command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], | 41 | command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], |
58 | ) | 42 | ) |
59 | endforeach | 43 | endforeach |
60 | |||
61 | lib_client_protos = static_library( | ||
62 | 'client_protos', | ||
63 | wl_protos_src + wl_protos_headers, | ||
64 | dependencies: wayland_client.partial_dependency(compile_args: true), | ||
65 | ) | ||
66 | |||
67 | client_protos = declare_dependency( | ||
68 | link_with: lib_client_protos, | ||
69 | sources: wl_protos_headers, | ||
70 | ) | ||
71 | |||
72 | lib_server_protos = static_library( | ||
73 | 'server_protos', | ||
74 | wl_protos_src + wl_protos_headers, | ||
75 | dependencies: wayland_server.partial_dependency(compile_args: true), | ||
76 | ) | ||
77 | |||
78 | server_protos = declare_dependency( | ||
79 | link_with: lib_server_protos, | ||
80 | sources: wl_protos_headers, | ||
81 | ) | ||
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 2160a970..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> |
@@ -104,6 +103,7 @@ static const struct cmd_handler handlers[] = { | |||
104 | static const struct cmd_handler config_handlers[] = { | 103 | static const struct cmd_handler config_handlers[] = { |
105 | { "default_orientation", cmd_default_orientation }, | 104 | { "default_orientation", cmd_default_orientation }, |
106 | { "include", cmd_include }, | 105 | { "include", cmd_include }, |
106 | { "primary_selection", cmd_primary_selection }, | ||
107 | { "swaybg_command", cmd_swaybg_command }, | 107 | { "swaybg_command", cmd_swaybg_command }, |
108 | { "swaynag_command", cmd_swaynag_command }, | 108 | { "swaynag_command", cmd_swaynag_command }, |
109 | { "workspace_layout", cmd_workspace_layout }, | 109 | { "workspace_layout", cmd_workspace_layout }, |
@@ -147,7 +147,7 @@ static int handler_compare(const void *_a, const void *_b) { | |||
147 | return strcasecmp(a->command, b->command); | 147 | return strcasecmp(a->command, b->command); |
148 | } | 148 | } |
149 | 149 | ||
150 | const struct cmd_handler *find_handler(char *line, | 150 | const struct cmd_handler *find_handler(const char *line, |
151 | const struct cmd_handler *handlers, size_t handlers_size) { | 151 | const struct cmd_handler *handlers, size_t handlers_size) { |
152 | if (!handlers || !handlers_size) { | 152 | if (!handlers || !handlers_size) { |
153 | return NULL; | 153 | return NULL; |
@@ -380,10 +380,13 @@ struct cmd_results *config_command(char *exec, char **new_block) { | |||
380 | sway_log(SWAY_INFO, "Config command: %s", exec); | 380 | sway_log(SWAY_INFO, "Config command: %s", exec); |
381 | const struct cmd_handler *handler = find_core_handler(argv[0]); | 381 | const struct cmd_handler *handler = find_core_handler(argv[0]); |
382 | if (!handler || !handler->handle) { | 382 | if (!handler || !handler->handle) { |
383 | const char *error = handler | 383 | if (handler) { |
384 | ? "Command '%s' is shimmed, but unimplemented" | 384 | results = cmd_results_new(CMD_INVALID, |
385 | : "Unknown/invalid command '%s'"; | 385 | "Command '%s' is shimmed, but unimplemented", argv[0]); |
386 | 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 | } | ||
387 | goto cleanup; | 390 | goto cleanup; |
388 | } | 391 | } |
389 | 392 | ||
@@ -485,20 +488,10 @@ struct cmd_results *cmd_results_new(enum cmd_status status, | |||
485 | } | 488 | } |
486 | results->status = status; | 489 | results->status = status; |
487 | if (format) { | 490 | if (format) { |
488 | char *error = NULL; | ||
489 | va_list args; | 491 | va_list args; |
490 | va_start(args, format); | 492 | va_start(args, format); |
491 | int slen = vsnprintf(NULL, 0, format, args); | 493 | results->error = vformat_str(format, args); |
492 | va_end(args); | 494 | va_end(args); |
493 | if (slen > 0) { | ||
494 | error = malloc(slen + 1); | ||
495 | if (error != NULL) { | ||
496 | va_start(args, format); | ||
497 | vsnprintf(error, slen + 1, format, args); | ||
498 | va_end(args); | ||
499 | } | ||
500 | } | ||
501 | results->error = error; | ||
502 | } else { | 495 | } else { |
503 | results->error = NULL; | 496 | results->error = NULL; |
504 | } | 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 8b661e3a..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" |
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 7c2f423b..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" |
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 c0b383db..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> |
@@ -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) { |
@@ -539,7 +538,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, | |||
539 | free_switch_binding(binding); | 538 | free_switch_binding(binding); |
540 | return cmd_results_new(CMD_FAILURE, | 539 | return cmd_results_new(CMD_FAILURE, |
541 | "Invalid %s command (expected binding with the form " | 540 | "Invalid %s command (expected binding with the form " |
542 | "<switch>:<state>)", bindtype, argc); | 541 | "<switch>:<state>)", bindtype); |
543 | } | 542 | } |
544 | if (strcmp(split->items[0], "tablet") == 0) { | 543 | if (strcmp(split->items[0], "tablet") == 0) { |
545 | binding->type = WLR_SWITCH_TYPE_TABLET_MODE; | 544 | binding->type = WLR_SWITCH_TYPE_TABLET_MODE; |
@@ -549,7 +548,8 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, | |||
549 | free_switch_binding(binding); | 548 | free_switch_binding(binding); |
550 | return cmd_results_new(CMD_FAILURE, | 549 | return cmd_results_new(CMD_FAILURE, |
551 | "Invalid %s command (expected switch binding: " | 550 | "Invalid %s command (expected switch binding: " |
552 | "unknown switch %s)", bindtype, split->items[0]); | 551 | "unknown switch %s)", bindtype, |
552 | (const char *)split->items[0]); | ||
553 | } | 553 | } |
554 | if (strcmp(split->items[1], "on") == 0) { | 554 | if (strcmp(split->items[1], "on") == 0) { |
555 | binding->trigger = SWAY_SWITCH_TRIGGER_ON; | 555 | binding->trigger = SWAY_SWITCH_TRIGGER_ON; |
@@ -562,7 +562,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, | |||
562 | return cmd_results_new(CMD_FAILURE, | 562 | return cmd_results_new(CMD_FAILURE, |
563 | "Invalid %s command " | 563 | "Invalid %s command " |
564 | "(expected switch state: unknown state %s)", | 564 | "(expected switch state: unknown state %s)", |
565 | bindtype, split->items[1]); | 565 | bindtype, (const char *)split->items[1]); |
566 | } | 566 | } |
567 | list_free_items_and_destroy(split); | 567 | list_free_items_and_destroy(split); |
568 | 568 | ||
diff --git a/sway/commands/client.c b/sway/commands/client.c index 77263145..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, |
@@ -51,12 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | |||
51 | memcpy(class, &colors, sizeof(struct border_colors)); | 50 | memcpy(class, &colors, sizeof(struct border_colors)); |
52 | 51 | ||
53 | if (config->active) { | 52 | if (config->active) { |
54 | root_for_each_container(rebuild_textures_iterator, NULL); | 53 | root_for_each_container(container_update_iterator, NULL); |
55 | |||
56 | for (int i = 0; i < root->outputs->length; ++i) { | ||
57 | struct sway_output *output = root->outputs->items[i]; | ||
58 | output_damage_whole(output); | ||
59 | } | ||
60 | } | 54 | } |
61 | 55 | ||
62 | return cmd_results_new(CMD_SUCCESS, NULL); | 56 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index b35065c1..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> |
@@ -8,6 +7,7 @@ | |||
8 | #include "sway/commands.h" | 7 | #include "sway/commands.h" |
9 | #include "sway/config.h" | 8 | #include "sway/config.h" |
10 | #include "sway/server.h" | 9 | #include "sway/server.h" |
10 | #include "sway/desktop/launcher.h" | ||
11 | #include "sway/tree/container.h" | 11 | #include "sway/tree/container.h" |
12 | #include "sway/tree/root.h" | 12 | #include "sway/tree/root.h" |
13 | #include "sway/tree/workspace.h" | 13 | #include "sway/tree/workspace.h" |
@@ -25,11 +25,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) { | |||
25 | return error; | 25 | return error; |
26 | } | 26 | } |
27 | 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 | |||
28 | struct cmd_results *cmd_exec_process(int argc, char **argv) { | 38 | struct cmd_results *cmd_exec_process(int argc, char **argv) { |
29 | struct cmd_results *error = NULL; | 39 | struct cmd_results *error = NULL; |
30 | char *cmd = NULL; | 40 | char *cmd = NULL; |
41 | bool no_startup_id = false; | ||
31 | if (strcmp(argv[0], "--no-startup-id") == 0) { | 42 | if (strcmp(argv[0], "--no-startup-id") == 0) { |
32 | sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); | 43 | no_startup_id = true; |
33 | --argc; ++argv; | 44 | --argc; ++argv; |
34 | if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { | 45 | if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { |
35 | return error; | 46 | return error; |
@@ -51,6 +62,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
51 | } | 62 | } |
52 | 63 | ||
53 | pid_t pid, child; | 64 | pid_t pid, child; |
65 | struct launcher_ctx *ctx = launcher_ctx_create_internal(); | ||
54 | // Fork process | 66 | // Fork process |
55 | if ((pid = fork()) == 0) { | 67 | if ((pid = fork()) == 0) { |
56 | // Fork child process again | 68 | // Fork child process again |
@@ -63,6 +75,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
63 | close(fd[0]); | 75 | close(fd[0]); |
64 | if ((child = fork()) == 0) { | 76 | if ((child = fork()) == 0) { |
65 | 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 | } | ||
66 | execlp("sh", "sh", "-c", cmd, (void *)NULL); | 84 | execlp("sh", "sh", "-c", cmd, (void *)NULL); |
67 | sway_log_errno(SWAY_ERROR, "execlp failed"); | 85 | sway_log_errno(SWAY_ERROR, "execlp failed"); |
68 | _exit(1); | 86 | _exit(1); |
@@ -90,8 +108,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
90 | waitpid(pid, NULL, 0); | 108 | waitpid(pid, NULL, 0); |
91 | if (child > 0) { | 109 | if (child > 0) { |
92 | sway_log(SWAY_DEBUG, "Child process created with pid %d", child); | 110 | sway_log(SWAY_DEBUG, "Child process created with pid %d", child); |
93 | 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 | } | ||
94 | } else { | 115 | } else { |
116 | launcher_ctx_destroy(ctx); | ||
95 | return cmd_results_new(CMD_FAILURE, "Second fork() failed"); | 117 | return cmd_results_new(CMD_FAILURE, "Second fork() failed"); |
96 | } | 118 | } |
97 | 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/font.c b/sway/commands/font.c index 74bb6b9f..9920d03e 100644 --- a/sway/commands/font.c +++ b/sway/commands/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 "sway/config.h" | 3 | #include "sway/config.h" |
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 index d4442cc3..90a20716 100644 --- a/sway/commands/gesture.c +++ b/sway/commands/gesture.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | 2 | ||
4 | #include "gesture.h" | 3 | #include "gesture.h" |
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 3cce4ec8..ecac8e6c 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <wlr/interfaces/wlr_keyboard.h> | 2 | #include <wlr/interfaces/wlr_keyboard.h> |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
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 7263efcb..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" |
diff --git a/sway/commands/move.c b/sway/commands/move.c index 0d0d9727..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); |
@@ -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 }; |
@@ -875,7 +876,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
875 | } | 876 | } |
876 | 877 | ||
877 | if (argc < 1) { | 878 | if (argc < 1) { |
878 | return cmd_results_new(CMD_FAILURE, expected_position_syntax); | 879 | return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); |
879 | } | 880 | } |
880 | 881 | ||
881 | struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; | 882 | struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; |
@@ -884,7 +885,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
884 | argc -= num_consumed_args; | 885 | argc -= num_consumed_args; |
885 | argv += num_consumed_args; | 886 | argv += num_consumed_args; |
886 | if (argc > 0) { | 887 | if (argc > 0) { |
887 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 888 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
888 | } | 889 | } |
889 | if (ly.unit == MOVEMENT_UNIT_INVALID) { | 890 | if (ly.unit == MOVEMENT_UNIT_INVALID) { |
890 | return cmd_results_new(CMD_INVALID, "Invalid y position specified"); | 891 | return cmd_results_new(CMD_INVALID, "Invalid y position specified"); |
@@ -1030,13 +1031,13 @@ struct cmd_results *cmd_move(int argc, char **argv) { | |||
1030 | } | 1031 | } |
1031 | 1032 | ||
1032 | if (!argc) { | 1033 | if (!argc) { |
1033 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1034 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1034 | } | 1035 | } |
1035 | 1036 | ||
1036 | // Only `move [window|container] [to] workspace` supports | 1037 | // Only `move [window|container] [to] workspace` supports |
1037 | // `--no-auto-back-and-forth` so treat others as invalid syntax | 1038 | // `--no-auto-back-and-forth` so treat others as invalid syntax |
1038 | if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { | 1039 | if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { |
1039 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1040 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1040 | } | 1041 | } |
1041 | 1042 | ||
1042 | if (strcasecmp(argv[0], "workspace") == 0 || | 1043 | if (strcasecmp(argv[0], "workspace") == 0 || |
@@ -1050,5 +1051,5 @@ struct cmd_results *cmd_move(int argc, char **argv) { | |||
1050 | strcasecmp(argv[1], "position") == 0)) { | 1051 | strcasecmp(argv[1], "position") == 0)) { |
1051 | return cmd_move_to_position(argc, argv); | 1052 | return cmd_move_to_position(argc, argv); |
1052 | } | 1053 | } |
1053 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1054 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1054 | } | 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 c102344d..5e5d31b3 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c | |||
@@ -27,6 +27,7 @@ static const struct cmd_handler output_handlers[] = { | |||
27 | { "subpixel", output_cmd_subpixel }, | 27 | { "subpixel", output_cmd_subpixel }, |
28 | { "toggle", output_cmd_toggle }, | 28 | { "toggle", output_cmd_toggle }, |
29 | { "transform", output_cmd_transform }, | 29 | { "transform", output_cmd_transform }, |
30 | { "unplug", output_cmd_unplug }, | ||
30 | }; | 31 | }; |
31 | 32 | ||
32 | struct cmd_results *cmd_output(int argc, char **argv) { | 33 | struct cmd_results *cmd_output(int argc, char **argv) { |
@@ -102,15 +103,18 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
102 | 103 | ||
103 | bool background = output->background; | 104 | bool background = output->background; |
104 | 105 | ||
105 | output = store_output_config(output); | 106 | store_output_config(output); |
106 | 107 | ||
107 | // If reloading, the output configs will be applied after reading the | 108 | // If reloading, the output configs will be applied after reading the |
108 | // 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 |
109 | // workspace name is not given to re-enabled outputs. | 110 | // workspace name is not given to re-enabled outputs. |
110 | if (!config->reloading && !config->validating) { | 111 | if (!config->reloading && !config->validating) { |
111 | apply_output_config_to_outputs(output); | 112 | apply_all_output_configs(); |
112 | if (background) { | 113 | if (background) { |
113 | spawn_swaybg(); | 114 | if (!spawn_swaybg()) { |
115 | return cmd_results_new(CMD_FAILURE, | ||
116 | "Failed to apply background configuration"); | ||
117 | } | ||
114 | } | 118 | } |
115 | } | 119 | } |
116 | 120 | ||
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 67f212ff..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> |
@@ -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/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 76f14bba..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,7 +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 | root_for_each_container(rebuild_textures_iterator, NULL); | 49 | root_for_each_container(title_bar_update_iterator, NULL); |
52 | 50 | ||
53 | arrange_root(); | 51 | arrange_root(); |
54 | } | 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 504a9f5e..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_pointer_axis_event event = { | 113 | struct wlr_pointer_axis_event event = { |
115 | .pointer = 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/split.c b/sway/commands/split.c index c8a2cfc1..500a497d 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c | |||
@@ -32,7 +32,7 @@ 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() { | 35 | static struct cmd_results *do_unsplit(void) { |
36 | struct sway_container *con = config->handler_context.container; | 36 | struct sway_container *con = config->handler_context.container; |
37 | struct sway_workspace *ws = config->handler_context.workspace; | 37 | struct sway_workspace *ws = config->handler_context.workspace; |
38 | 38 | ||
diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 9355944d..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 | if (fs1) { | ||
130 | container_fullscreen_disable(con1); | ||
131 | } | ||
132 | enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; | ||
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) { |
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 a2446b7e..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" |
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 b41dd871..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 | } |
@@ -273,6 +281,7 @@ static void config_defaults(struct sway_config *config) { | |||
273 | config->title_align = ALIGN_LEFT; | 281 | config->title_align = ALIGN_LEFT; |
274 | config->tiling_drag = true; | 282 | config->tiling_drag = true; |
275 | config->tiling_drag_threshold = 9; | 283 | config->tiling_drag_threshold = 9; |
284 | config->primary_selection = true; | ||
276 | 285 | ||
277 | config->smart_gaps = SMART_GAPS_OFF; | 286 | config->smart_gaps = SMART_GAPS_OFF; |
278 | config->gaps_inner = 0; | 287 | config->gaps_inner = 0; |
@@ -335,8 +344,14 @@ static void config_defaults(struct sway_config *config) { | |||
335 | 344 | ||
336 | // The keysym to keycode translation | 345 | // The keysym to keycode translation |
337 | struct xkb_rule_names rules = {0}; | 346 | struct xkb_rule_names rules = {0}; |
338 | config->keysym_translation_state = | 347 | config->keysym_translation_state = keysym_translation_state_create(rules, 0); |
339 | 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 | } | ||
340 | 355 | ||
341 | return; | 356 | return; |
342 | cleanup: | 357 | cleanup: |
@@ -351,13 +366,7 @@ static char *config_path(const char *prefix, const char *config_folder) { | |||
351 | if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { | 366 | if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { |
352 | return NULL; | 367 | return NULL; |
353 | } | 368 | } |
354 | 369 | return format_str("%s/%s/config", prefix, config_folder); | |
355 | const char *filename = "config"; | ||
356 | |||
357 | size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename); | ||
358 | char *path = calloc(size, sizeof(char)); | ||
359 | snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename); | ||
360 | return path; | ||
361 | } | 370 | } |
362 | 371 | ||
363 | static char *get_config_path(void) { | 372 | static char *get_config_path(void) { |
@@ -367,10 +376,7 @@ static char *get_config_path(void) { | |||
367 | 376 | ||
368 | const char *config_home = getenv("XDG_CONFIG_HOME"); | 377 | const char *config_home = getenv("XDG_CONFIG_HOME"); |
369 | if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { | 378 | if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { |
370 | size_t size_fallback = 1 + strlen(home) + strlen("/.config"); | 379 | config_home_fallback = format_str("%s/.config", home); |
371 | config_home_fallback = calloc(size_fallback, sizeof(char)); | ||
372 | if (config_home_fallback != NULL) | ||
373 | snprintf(config_home_fallback, size_fallback, "%s/.config", home); | ||
374 | config_home = config_home_fallback; | 380 | config_home = config_home_fallback; |
375 | } | 381 | } |
376 | 382 | ||
@@ -474,6 +480,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
474 | old_config->xwayland ? "enabled" : "disabled"); | 480 | old_config->xwayland ? "enabled" : "disabled"); |
475 | config->xwayland = old_config->xwayland; | 481 | config->xwayland = old_config->xwayland; |
476 | 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 | |||
477 | if (!config->validating) { | 488 | if (!config->validating) { |
478 | if (old_config->swaybg_client != NULL) { | 489 | if (old_config->swaybg_client != NULL) { |
479 | wl_client_destroy(old_config->swaybg_client); | 490 | wl_client_destroy(old_config->swaybg_client); |
@@ -493,56 +504,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
493 | 504 | ||
494 | config->reading = true; | 505 | config->reading = true; |
495 | 506 | ||
496 | // Read security configs | 507 | bool success = load_config(path, config, &config->swaynag_config_errors); |
497 | // TODO: Security | ||
498 | bool success = true; | ||
499 | /* | ||
500 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); | ||
501 | if (!dir) { | ||
502 | sway_log(SWAY_ERROR, | ||
503 | "%s does not exist, sway will have no security configuration" | ||
504 | " and will probably be broken", SYSCONFDIR "/sway/security.d"); | ||
505 | } else { | ||
506 | list_t *secconfigs = create_list(); | ||
507 | char *base = SYSCONFDIR "/sway/security.d/"; | ||
508 | struct dirent *ent = readdir(dir); | ||
509 | struct stat s; | ||
510 | while (ent != NULL) { | ||
511 | char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1); | ||
512 | strcpy(_path, base); | ||
513 | strcat(_path, ent->d_name); | ||
514 | lstat(_path, &s); | ||
515 | if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') { | ||
516 | list_add(secconfigs, _path); | ||
517 | } | ||
518 | else { | ||
519 | free(_path); | ||
520 | } | ||
521 | ent = readdir(dir); | ||
522 | } | ||
523 | closedir(dir); | ||
524 | |||
525 | list_qsort(secconfigs, qstrcmp); | ||
526 | for (int i = 0; i < secconfigs->length; ++i) { | ||
527 | char *_path = secconfigs->items[i]; | ||
528 | if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || | ||
529 | (((s.st_mode & 0777) != 0644) && | ||
530 | (s.st_mode & 0777) != 0444)) { | ||
531 | sway_log(SWAY_ERROR, | ||
532 | "Refusing to load %s - it must be owned by root " | ||
533 | "and mode 644 or 444", _path); | ||
534 | success = false; | ||
535 | } else { | ||
536 | success = success && load_config(_path, config); | ||
537 | } | ||
538 | } | ||
539 | |||
540 | list_free_items_and_destroy(secconfigs); | ||
541 | } | ||
542 | */ | ||
543 | |||
544 | success = success && load_config(path, config, | ||
545 | &config->swaynag_config_errors); | ||
546 | 508 | ||
547 | if (validating) { | 509 | if (validating) { |
548 | free_config(config); | 510 | free_config(config); |
@@ -570,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
570 | } | 532 | } |
571 | sway_switch_retrigger_bindings_for_all(); | 533 | sway_switch_retrigger_bindings_for_all(); |
572 | 534 | ||
573 | reset_outputs(); | 535 | apply_all_output_configs(); |
574 | spawn_swaybg(); | 536 | spawn_swaybg(); |
575 | 537 | ||
576 | config->reloading = false; | 538 | config->reloading = false; |
@@ -923,23 +885,18 @@ void config_add_swaynag_warning(char *fmt, ...) { | |||
923 | if (config->reading && !config->validating) { | 885 | if (config->reading && !config->validating) { |
924 | va_list args; | 886 | va_list args; |
925 | va_start(args, fmt); | 887 | va_start(args, fmt); |
926 | size_t length = vsnprintf(NULL, 0, fmt, args) + 1; | 888 | char *str = vformat_str(fmt, args); |
927 | va_end(args); | 889 | va_end(args); |
928 | 890 | if (str == NULL) { | |
929 | char *temp = malloc(length + 1); | ||
930 | if (!temp) { | ||
931 | sway_log(SWAY_ERROR, "Failed to allocate buffer for warning."); | ||
932 | return; | 891 | return; |
933 | } | 892 | } |
934 | 893 | ||
935 | va_start(args, fmt); | ||
936 | vsnprintf(temp, length, fmt, args); | ||
937 | va_end(args); | ||
938 | |||
939 | swaynag_log(config->swaynag_command, &config->swaynag_config_errors, | 894 | swaynag_log(config->swaynag_command, &config->swaynag_config_errors, |
940 | "Warning on line %i (%s) '%s': %s", | 895 | "Warning on line %i (%s) '%s': %s", |
941 | config->current_config_line_number, config->current_config_path, | 896 | config->current_config_line_number, config->current_config_path, |
942 | config->current_config_line, temp); | 897 | config->current_config_line, str); |
898 | |||
899 | free(str); | ||
943 | } | 900 | } |
944 | } | 901 | } |
945 | 902 | ||
@@ -979,7 +936,7 @@ char *do_var_replacement(char *str) { | |||
979 | int offset = find - str; | 936 | int offset = find - str; |
980 | strncpy(newptr, str, offset); | 937 | strncpy(newptr, str, offset); |
981 | newptr += offset; | 938 | newptr += offset; |
982 | strncpy(newptr, var->value, vvlen); | 939 | memcpy(newptr, var->value, vvlen); |
983 | newptr += vvlen; | 940 | newptr += vvlen; |
984 | strcpy(newptr, find + vnlen); | 941 | strcpy(newptr, find + vnlen); |
985 | free(str); | 942 | free(str); |
@@ -1041,8 +998,12 @@ void translate_keysyms(struct input_config *input_config) { | |||
1041 | 998 | ||
1042 | struct xkb_rule_names rules = {0}; | 999 | struct xkb_rule_names rules = {0}; |
1043 | input_config_fill_rule_names(input_config, &rules); | 1000 | input_config_fill_rule_names(input_config, &rules); |
1044 | config->keysym_translation_state = | 1001 | config->keysym_translation_state = keysym_translation_state_create(rules, 0); |
1045 | 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 | } | ||
1046 | 1007 | ||
1047 | for (int i = 0; i < config->modes->length; ++i) { | 1008 | for (int i = 0; i < config->modes->length; ++i) { |
1048 | 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 d1b342e6..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> |
@@ -256,7 +255,6 @@ static void invoke_swaybar(struct bar_config *bar) { | |||
256 | } | 255 | } |
257 | 256 | ||
258 | sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); | 257 | sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); |
259 | return; | ||
260 | } | 258 | } |
261 | 259 | ||
262 | 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 b3e8371e..54af5d8e 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -1,4 +1,3 @@ | |||
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 <stdbool.h> | 3 | #include <stdbool.h> |
@@ -6,10 +5,11 @@ | |||
6 | #include <sys/socket.h> | 5 | #include <sys/socket.h> |
7 | #include <sys/wait.h> | 6 | #include <sys/wait.h> |
8 | #include <unistd.h> | 7 | #include <unistd.h> |
8 | #include <wlr/config.h> | ||
9 | #include <wlr/types/wlr_cursor.h> | 9 | #include <wlr/types/wlr_cursor.h> |
10 | #include <wlr/types/wlr_output_layout.h> | 10 | #include <wlr/types/wlr_output_layout.h> |
11 | #include <wlr/types/wlr_output.h> | 11 | #include <wlr/types/wlr_output.h> |
12 | #include <wlr/backend/drm.h> | 12 | #include <wlr/types/wlr_output_swapchain_manager.h> |
13 | #include "sway/config.h" | 13 | #include "sway/config.h" |
14 | #include "sway/input/cursor.h" | 14 | #include "sway/input/cursor.h" |
15 | #include "sway/output.h" | 15 | #include "sway/output.h" |
@@ -17,6 +17,10 @@ | |||
17 | #include "log.h" | 17 | #include "log.h" |
18 | #include "util.h" | 18 | #include "util.h" |
19 | 19 | ||
20 | #if WLR_HAS_DRM_BACKEND | ||
21 | #include <wlr/backend/drm.h> | ||
22 | #endif | ||
23 | |||
20 | int output_name_cmp(const void *item, const void *data) { | 24 | int output_name_cmp(const void *item, const void *data) { |
21 | const struct output_config *output = item; | 25 | const struct output_config *output = item; |
22 | const char *name = data; | 26 | const char *name = data; |
@@ -75,7 +79,72 @@ struct output_config *new_output_config(const char *name) { | |||
75 | return oc; | 79 | return oc; |
76 | } | 80 | } |
77 | 81 | ||
78 | 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) { | ||
79 | if (src->enabled != -1) { | 148 | if (src->enabled != -1) { |
80 | dst->enabled = src->enabled; | 149 | dst->enabled = src->enabled; |
81 | } | 150 | } |
@@ -138,103 +207,42 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
138 | } | 207 | } |
139 | } | 208 | } |
140 | 209 | ||
141 | static void merge_wildcard_on_all(struct output_config *wildcard) { | 210 | void store_output_config(struct output_config *oc) { |
142 | for (int i = 0; i < config->output_configs->length; i++) { | 211 | bool merged = false; |
143 | struct output_config *oc = config->output_configs->items[i]; | 212 | bool wildcard = strcmp(oc->name, "*") == 0; |
144 | if (strcmp(wildcard->name, oc->name) != 0) { | 213 | struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name); |
145 | sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name); | ||
146 | merge_output_config(oc, wildcard); | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | 214 | ||
151 | static void merge_id_on_name(struct output_config *oc) { | ||
152 | char *id_on_name = NULL; | ||
153 | char id[128]; | 215 | char id[128]; |
154 | char *name = NULL; | 216 | if (output) { |
155 | struct sway_output *output; | ||
156 | wl_list_for_each(output, &root->all_outputs, link) { | ||
157 | name = output->wlr_output->name; | ||
158 | output_get_identifier(id, sizeof(id), output); | 217 | output_get_identifier(id, sizeof(id), output); |
159 | if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) { | ||
160 | size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1; | ||
161 | id_on_name = malloc(length); | ||
162 | if (!id_on_name) { | ||
163 | sway_log(SWAY_ERROR, "Failed to allocate id on name string"); | ||
164 | return; | ||
165 | } | ||
166 | snprintf(id_on_name, length, "%s on %s", id, name); | ||
167 | break; | ||
168 | } | ||
169 | } | 218 | } |
170 | 219 | ||
171 | if (!id_on_name) { | 220 | for (int i = 0; i < config->output_configs->length; i++) { |
172 | return; | 221 | struct output_config *old = config->output_configs->items[i]; |
173 | } | 222 | |
174 | 223 | // If the old config matches the new config's name, regardless of | |
175 | int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); | 224 | // whether it was name or identifier, merge on top of the existing |
176 | if (i >= 0) { | 225 | // config. If the new config is a wildcard, this also merges on top of |
177 | sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); | 226 | // old wildcard configs. |
178 | merge_output_config(config->output_configs->items[i], oc); | 227 | if (strcmp(old->name, oc->name) == 0) { |
179 | } else { | 228 | merge_output_config(old, oc); |
180 | // If both a name and identifier config, exist generate an id on name | 229 | merged = true; |
181 | int ni = list_seq_find(config->output_configs, output_name_cmp, name); | 230 | continue; |
182 | int ii = list_seq_find(config->output_configs, output_name_cmp, id); | ||
183 | if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) | ||
184 | || (ii >= 0 && strcmp(oc->name, name) == 0)) { | ||
185 | struct output_config *ion_oc = new_output_config(id_on_name); | ||
186 | if (ni >= 0) { | ||
187 | merge_output_config(ion_oc, config->output_configs->items[ni]); | ||
188 | } | ||
189 | if (ii >= 0) { | ||
190 | merge_output_config(ion_oc, config->output_configs->items[ii]); | ||
191 | } | ||
192 | merge_output_config(ion_oc, oc); | ||
193 | list_add(config->output_configs, ion_oc); | ||
194 | sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" | ||
195 | " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " | ||
196 | "transform %d) (bg %s %s) (power %d) (max render time: %d)", | ||
197 | ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, | ||
198 | ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, | ||
199 | ion_oc->transform, ion_oc->background, | ||
200 | ion_oc->background_option, ion_oc->power, | ||
201 | ion_oc->max_render_time); | ||
202 | } | 231 | } |
203 | } | ||
204 | free(id_on_name); | ||
205 | } | ||
206 | 232 | ||
207 | struct output_config *store_output_config(struct output_config *oc) { | 233 | // If the new config is a wildcard config we supersede all non-wildcard |
208 | bool wildcard = strcmp(oc->name, "*") == 0; | 234 | // configs. Old wildcard configs have already been handled above. |
209 | if (wildcard) { | 235 | if (wildcard) { |
210 | merge_wildcard_on_all(oc); | 236 | supersede_output_config(old, oc); |
211 | } else { | 237 | continue; |
212 | merge_id_on_name(oc); | 238 | } |
213 | } | ||
214 | 239 | ||
215 | 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 |
216 | if (i >= 0) { | 241 | // matches on that output's identifier, supersede it. |
217 | sway_log(SWAY_DEBUG, "Merging on top of existing output config"); | 242 | if (output && strcmp(old->name, id) == 0 && |
218 | struct output_config *current = config->output_configs->items[i]; | 243 | strcmp(oc->name, output->wlr_output->name) == 0) { |
219 | merge_output_config(current, oc); | 244 | supersede_output_config(old, oc); |
220 | free_output_config(oc); | ||
221 | oc = current; | ||
222 | } else if (!wildcard) { | ||
223 | sway_log(SWAY_DEBUG, "Adding non-wildcard output config"); | ||
224 | i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
225 | if (i >= 0) { | ||
226 | sway_log(SWAY_DEBUG, "Merging on top of output * config"); | ||
227 | struct output_config *current = new_output_config(oc->name); | ||
228 | merge_output_config(current, config->output_configs->items[i]); | ||
229 | merge_output_config(current, oc); | ||
230 | free_output_config(oc); | ||
231 | oc = current; | ||
232 | } | 245 | } |
233 | list_add(config->output_configs, oc); | ||
234 | } else { | ||
235 | // New wildcard config. Just add it | ||
236 | sway_log(SWAY_DEBUG, "Adding output * config"); | ||
237 | list_add(config->output_configs, oc); | ||
238 | } | 246 | } |
239 | 247 | ||
240 | 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 " |
@@ -245,7 +253,13 @@ struct output_config *store_output_config(struct output_config *oc) { | |||
245 | oc->transform, oc->background, oc->background_option, oc->power, | 253 | oc->transform, oc->background, oc->background_option, oc->power, |
246 | oc->max_render_time); | 254 | oc->max_render_time); |
247 | 255 | ||
248 | 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 | } | ||
249 | } | 263 | } |
250 | 264 | ||
251 | static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, | 265 | static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, |
@@ -253,7 +267,9 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending | |||
253 | // Not all floating point integers can be represented exactly | 267 | // Not all floating point integers can be represented exactly |
254 | // as (int)(1000 * mHz / 1000.f) | 268 | // as (int)(1000 * mHz / 1000.f) |
255 | // round() the result to avoid any error | 269 | // round() the result to avoid any error |
256 | 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; | ||
257 | 273 | ||
258 | if (wl_list_empty(&output->modes) || custom) { | 274 | if (wl_list_empty(&output->modes) || custom) { |
259 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); | 275 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); |
@@ -263,29 +279,35 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending | |||
263 | } | 279 | } |
264 | 280 | ||
265 | struct wlr_output_mode *mode, *best = NULL; | 281 | struct wlr_output_mode *mode, *best = NULL; |
282 | int best_diff_mhz = INT_MAX; | ||
266 | wl_list_for_each(mode, &output->modes, link) { | 283 | wl_list_for_each(mode, &output->modes, link) { |
267 | if (mode->width == width && mode->height == height) { | 284 | if (mode->width == width && mode->height == height) { |
268 | if (mode->refresh == mhz) { | 285 | int diff_mhz = abs(mode->refresh - mhz); |
269 | best = mode; | 286 | if (diff_mhz < best_diff_mhz) { |
270 | break; | 287 | best_diff_mhz = diff_mhz; |
271 | } | ||
272 | if (best == NULL || mode->refresh > best->refresh) { | ||
273 | best = mode; | 288 | best = mode; |
289 | if (best_diff_mhz == 0) { | ||
290 | break; | ||
291 | } | ||
274 | } | 292 | } |
275 | } | 293 | } |
276 | } | 294 | } |
277 | if (!best) { | 295 | if (best) { |
278 | 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", |
279 | sway_log(SWAY_INFO, "Picking preferred mode instead"); | 297 | best->width, best->height, best->refresh / 1000.f, output->name); |
280 | best = wlr_output_preferred_mode(output); | ||
281 | } else { | 298 | } else { |
282 | 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); | ||
283 | } | 304 | } |
284 | wlr_output_state_set_mode(pending, best); | 305 | wlr_output_state_set_mode(pending, best); |
285 | } | 306 | } |
286 | 307 | ||
287 | static void set_modeline(struct wlr_output *output, | 308 | static void set_modeline(struct wlr_output *output, |
288 | struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { | 309 | struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { |
310 | #if WLR_HAS_DRM_BACKEND | ||
289 | if (!wlr_output_is_drm(output)) { | 311 | if (!wlr_output_is_drm(output)) { |
290 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | 312 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); |
291 | return; | 313 | return; |
@@ -295,6 +317,9 @@ static void set_modeline(struct wlr_output *output, | |||
295 | if (mode) { | 317 | if (mode) { |
296 | wlr_output_state_set_mode(pending, mode); | 318 | wlr_output_state_set_mode(pending, mode); |
297 | } | 319 | } |
320 | #else | ||
321 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | ||
322 | #endif | ||
298 | } | 323 | } |
299 | 324 | ||
300 | /* 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 |
@@ -436,9 +461,11 @@ static void queue_output_config(struct output_config *oc, | |||
436 | enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; | 461 | enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; |
437 | if (oc && oc->transform >= 0) { | 462 | if (oc && oc->transform >= 0) { |
438 | tr = oc->transform; | 463 | tr = oc->transform; |
464 | #if WLR_HAS_DRM_BACKEND | ||
439 | } else if (wlr_output_is_drm(wlr_output)) { | 465 | } else if (wlr_output_is_drm(wlr_output)) { |
440 | tr = wlr_drm_connector_get_panel_orientation(wlr_output); | 466 | tr = wlr_drm_connector_get_panel_orientation(wlr_output); |
441 | sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); | 467 | sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); |
468 | #endif | ||
442 | } | 469 | } |
443 | if (wlr_output->transform != tr) { | 470 | if (wlr_output->transform != tr) { |
444 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); | 471 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); |
@@ -450,6 +477,16 @@ static void queue_output_config(struct output_config *oc, | |||
450 | float scale; | 477 | float scale; |
451 | if (oc && oc->scale > 0) { | 478 | if (oc && oc->scale > 0) { |
452 | 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 | } | ||
453 | } else { | 490 | } else { |
454 | scale = compute_default_scale(wlr_output, pending); | 491 | scale = compute_default_scale(wlr_output, pending); |
455 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); | 492 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); |
@@ -463,6 +500,10 @@ static void queue_output_config(struct output_config *oc, | |||
463 | 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, |
464 | oc->adaptive_sync); | 501 | oc->adaptive_sync); |
465 | wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); | 502 | wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); |
503 | if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) { | ||
504 | sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring"); | ||
505 | wlr_output_state_set_adaptive_sync_enabled(pending, false); | ||
506 | } | ||
466 | } | 507 | } |
467 | 508 | ||
468 | if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | 509 | if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { |
@@ -482,35 +523,12 @@ static void queue_output_config(struct output_config *oc, | |||
482 | } | 523 | } |
483 | } | 524 | } |
484 | 525 | ||
485 | bool apply_output_config(struct output_config *oc, struct sway_output *output) { | 526 | static bool finalize_output_config(struct output_config *oc, struct sway_output *output) { |
486 | if (output == root->fallback_output) { | 527 | if (output == root->fallback_output) { |
487 | return false; | 528 | return false; |
488 | } | 529 | } |
489 | 530 | ||
490 | struct wlr_output *wlr_output = output->wlr_output; | 531 | struct wlr_output *wlr_output = output->wlr_output; |
491 | |||
492 | // Flag to prevent the output mode event handler from calling us | ||
493 | output->enabling = (!oc || oc->enabled); | ||
494 | |||
495 | struct wlr_output_state pending = {0}; | ||
496 | queue_output_config(oc, output, &pending); | ||
497 | |||
498 | if (!oc || oc->power != 0) { | ||
499 | output->current_mode = pending.mode; | ||
500 | } | ||
501 | |||
502 | sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); | ||
503 | if (!wlr_output_commit_state(wlr_output, &pending)) { | ||
504 | // Failed to commit output changes, maybe the output is missing a CRTC. | ||
505 | // Leave the output disabled for now and try again when the output gets | ||
506 | // the mode we asked for. | ||
507 | sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name); | ||
508 | output->enabling = false; | ||
509 | return false; | ||
510 | } | ||
511 | |||
512 | output->enabling = false; | ||
513 | |||
514 | if (oc && !oc->enabled) { | 532 | if (oc && !oc->enabled) { |
515 | sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); | 533 | sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); |
516 | if (output->enabled) { | 534 | if (output->enabled) { |
@@ -520,10 +538,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
520 | return true; | 538 | return true; |
521 | } | 539 | } |
522 | 540 | ||
523 | if (config->reloading) { | ||
524 | output_damage_whole(output); | ||
525 | } | ||
526 | |||
527 | if (oc) { | 541 | if (oc) { |
528 | enum scale_filter_mode scale_filter_old = output->scale_filter; | 542 | enum scale_filter_mode scale_filter_old = output->scale_filter; |
529 | switch (oc->scale_filter) { | 543 | switch (oc->scale_filter) { |
@@ -540,6 +554,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
540 | if (scale_filter_old != output->scale_filter) { | 554 | if (scale_filter_old != output->scale_filter) { |
541 | 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, |
542 | 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); | ||
543 | } | 558 | } |
544 | } | 559 | } |
545 | 560 | ||
@@ -569,25 +584,9 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
569 | output->max_render_time = oc->max_render_time; | 584 | output->max_render_time = oc->max_render_time; |
570 | } | 585 | } |
571 | 586 | ||
572 | // Reconfigure all devices, since input config may have been applied before | ||
573 | // this output came online, and some config items (like map_to_output) are | ||
574 | // dependent on an output being present. | ||
575 | input_manager_configure_all_inputs(); | ||
576 | // Reconfigure the cursor images, since the scale may have changed. | ||
577 | input_manager_configure_xcursor(); | ||
578 | return true; | 587 | return true; |
579 | } | 588 | } |
580 | 589 | ||
581 | bool test_output_config(struct output_config *oc, struct sway_output *output) { | ||
582 | if (output == root->fallback_output) { | ||
583 | return false; | ||
584 | } | ||
585 | |||
586 | struct wlr_output_state pending = {0}; | ||
587 | queue_output_config(oc, output, &pending); | ||
588 | return wlr_output_test_state(output->wlr_output, &pending); | ||
589 | } | ||
590 | |||
591 | static void default_output_config(struct output_config *oc, | 590 | static void default_output_config(struct output_config *oc, |
592 | struct wlr_output *wlr_output) { | 591 | struct wlr_output *wlr_output) { |
593 | oc->enabled = 1; | 592 | oc->enabled = 1; |
@@ -607,143 +606,167 @@ static void default_output_config(struct output_config *oc, | |||
607 | oc->max_render_time = 0; | 606 | oc->max_render_time = 0; |
608 | } | 607 | } |
609 | 608 | ||
610 | static struct output_config *get_output_config(char *identifier, | 609 | // find_output_config returns a merged output_config containing all stored |
611 | 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) { | ||
612 | const char *name = sway_output->wlr_output->name; | 612 | const char *name = sway_output->wlr_output->name; |
613 | struct output_config *oc = NULL; | ||
613 | 614 | ||
614 | struct output_config *oc_id_on_name = NULL; | 615 | struct output_config *result = new_output_config(name); |
615 | struct output_config *oc_name = NULL; | 616 | if (config->reloading) { |
616 | struct output_config *oc_id = NULL; | 617 | default_output_config(result, sway_output->wlr_output); |
618 | } | ||
617 | 619 | ||
618 | size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; | 620 | char id[128]; |
619 | char *id_on_name = malloc(length); | 621 | output_get_identifier(id, sizeof(id), sway_output); |
620 | snprintf(id_on_name, length, "%s on %s", identifier, name); | ||
621 | int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); | ||
622 | if (i >= 0) { | ||
623 | oc_id_on_name = config->output_configs->items[i]; | ||
624 | } else { | ||
625 | i = list_seq_find(config->output_configs, output_name_cmp, name); | ||
626 | if (i >= 0) { | ||
627 | oc_name = config->output_configs->items[i]; | ||
628 | } | ||
629 | 622 | ||
630 | i = list_seq_find(config->output_configs, output_name_cmp, identifier); | 623 | int i; |
631 | if (i >= 0) { | 624 | bool match = false; |
632 | oc_id = config->output_configs->items[i]; | 625 | if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) { |
633 | } | 626 | match = true; |
627 | oc = config->output_configs->items[i]; | ||
628 | merge_output_config(result, oc); | ||
634 | } | 629 | } |
635 | 630 | if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) { | |
636 | struct output_config *result = new_output_config("temp"); | 631 | match = true; |
637 | if (config->reloading) { | 632 | oc = config->output_configs->items[i]; |
638 | default_output_config(result, sway_output->wlr_output); | 633 | merge_output_config(result, oc); |
639 | } | 634 | } |
640 | if (oc_id_on_name) { | 635 | if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) { |
641 | // Already have an identifier on name config, use that | 636 | match = true; |
642 | free(result->name); | 637 | oc = config->output_configs->items[i]; |
643 | result->name = strdup(id_on_name); | 638 | merge_output_config(result, oc); |
644 | merge_output_config(result, oc_id_on_name); | 639 | } |
645 | } else if (oc_name && oc_id) { | 640 | |
646 | // Generate a config named `<identifier> on <name>` which contains a | 641 | if (!match && !config->reloading) { |
647 | // merged copy of the identifier on name. This will make sure that both | 642 | // No name, identifier, or wildcard config. Since we are not |
648 | // identifier and name configs are respected, with identifier getting | 643 | // reloading with defaults, the output config will be empty, so |
649 | // priority | 644 | // just return NULL |
650 | struct output_config *temp = new_output_config(id_on_name); | 645 | free_output_config(result); |
651 | merge_output_config(temp, oc_name); | 646 | return NULL; |
652 | merge_output_config(temp, oc_id); | ||
653 | list_add(config->output_configs, temp); | ||
654 | |||
655 | free(result->name); | ||
656 | result->name = strdup(id_on_name); | ||
657 | merge_output_config(result, temp); | ||
658 | |||
659 | sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" | ||
660 | " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" | ||
661 | " (power %d) (max render time: %d)", result->name, result->enabled, | ||
662 | result->width, result->height, result->refresh_rate, | ||
663 | result->x, result->y, result->scale, result->transform, | ||
664 | result->background, result->background_option, result->power, | ||
665 | result->max_render_time); | ||
666 | } else if (oc_name) { | ||
667 | // No identifier config, just return a copy of the name config | ||
668 | free(result->name); | ||
669 | result->name = strdup(name); | ||
670 | merge_output_config(result, oc_name); | ||
671 | } else if (oc_id) { | ||
672 | // No name config, just return a copy of the identifier config | ||
673 | free(result->name); | ||
674 | result->name = strdup(identifier); | ||
675 | merge_output_config(result, oc_id); | ||
676 | } else { | ||
677 | i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
678 | if (i >= 0) { | ||
679 | // No name or identifier config, but there is a wildcard config | ||
680 | free(result->name); | ||
681 | result->name = strdup("*"); | ||
682 | merge_output_config(result, config->output_configs->items[i]); | ||
683 | } else if (!config->reloading) { | ||
684 | // No name, identifier, or wildcard config. Since we are not | ||
685 | // reloading with defaults, the output config will be empty, so | ||
686 | // just return NULL | ||
687 | free_output_config(result); | ||
688 | result = NULL; | ||
689 | } | ||
690 | } | 647 | } |
691 | 648 | ||
692 | free(id_on_name); | ||
693 | return result; | 649 | return result; |
694 | } | 650 | } |
695 | 651 | ||
696 | struct output_config *find_output_config(struct sway_output *output) { | 652 | bool apply_output_configs(struct matched_output_config *configs, |
697 | char id[128]; | 653 | size_t configs_len, bool test_only) { |
698 | output_get_identifier(id, sizeof(id), output); | 654 | struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); |
699 | return get_output_config(id, output); | 655 | if (!states) { |
700 | } | 656 | return false; |
657 | } | ||
701 | 658 | ||
702 | void apply_output_config_to_outputs(struct output_config *oc) { | 659 | sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len); |
703 | // Try to find the output container and apply configuration now. If | 660 | for (size_t idx = 0; idx < configs_len; idx++) { |
704 | // this is during startup then there will be no container and config | 661 | struct matched_output_config *cfg = &configs[idx]; |
705 | // will be applied during normal "new output" event from wlroots. | 662 | struct wlr_backend_output_state *backend_state = &states[idx]; |
706 | bool wildcard = strcmp(oc->name, "*") == 0; | ||
707 | char id[128]; | ||
708 | struct sway_output *sway_output, *tmp; | ||
709 | wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { | ||
710 | char *name = sway_output->wlr_output->name; | ||
711 | output_get_identifier(id, sizeof(id), sway_output); | ||
712 | if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { | ||
713 | struct output_config *current = get_output_config(id, sway_output); | ||
714 | if (!current) { | ||
715 | // No stored output config matched, apply oc directly | ||
716 | sway_log(SWAY_DEBUG, "Applying oc directly"); | ||
717 | current = new_output_config(oc->name); | ||
718 | merge_output_config(current, oc); | ||
719 | } | ||
720 | apply_output_config(current, sway_output); | ||
721 | free_output_config(current); | ||
722 | 663 | ||
723 | if (!wildcard) { | 664 | backend_state->output = cfg->output->wlr_output; |
724 | // Stop looking if the output config isn't applicable to all | 665 | wlr_output_state_init(&backend_state->base); |
725 | // outputs | 666 | |
726 | break; | 667 | sway_log(SWAY_DEBUG, "Preparing config for %s", |
727 | } | 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; | ||
728 | } | 700 | } |
729 | } | 701 | } |
730 | 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 | |||
731 | struct sway_seat *seat; | 735 | struct sway_seat *seat; |
732 | wl_list_for_each(seat, &server.input->seats, link) { | 736 | wl_list_for_each(seat, &server.input->seats, link) { |
733 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 737 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
734 | cursor_rebase(seat->cursor); | 738 | cursor_rebase(seat->cursor); |
735 | } | 739 | } |
740 | |||
741 | return ok; | ||
736 | } | 742 | } |
737 | 743 | ||
738 | void reset_outputs(void) { | 744 | void apply_all_output_configs(void) { |
739 | struct output_config *oc = NULL; | 745 | size_t configs_len = wl_list_length(&root->all_outputs); |
740 | int i = list_seq_find(config->output_configs, output_name_cmp, "*"); | 746 | struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); |
741 | if (i >= 0) { | 747 | if (!configs) { |
742 | oc = config->output_configs->items[i]; | 748 | return; |
743 | } else { | ||
744 | oc = store_output_config(new_output_config("*")); | ||
745 | } | 749 | } |
746 | 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); | ||
747 | } | 770 | } |
748 | 771 | ||
749 | void free_output_config(struct output_config *oc) { | 772 | void free_output_config(struct output_config *oc) { |
@@ -810,7 +833,9 @@ static bool _spawn_swaybg(char **command) { | |||
810 | setenv("WAYLAND_SOCKET", wayland_socket_str, true); | 833 | setenv("WAYLAND_SOCKET", wayland_socket_str, true); |
811 | 834 | ||
812 | execvp(command[0], command); | 835 | execvp(command[0], command); |
813 | 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]); | ||
814 | _exit(EXIT_FAILURE); | 839 | _exit(EXIT_FAILURE); |
815 | } | 840 | } |
816 | _exit(EXIT_SUCCESS); | 841 | _exit(EXIT_SUCCESS); |
@@ -820,12 +845,13 @@ static bool _spawn_swaybg(char **command) { | |||
820 | sway_log_errno(SWAY_ERROR, "close failed"); | 845 | sway_log_errno(SWAY_ERROR, "close failed"); |
821 | return false; | 846 | return false; |
822 | } | 847 | } |
823 | if (waitpid(pid, NULL, 0) < 0) { | 848 | int fork_status = 0; |
849 | if (waitpid(pid, &fork_status, 0) < 0) { | ||
824 | sway_log_errno(SWAY_ERROR, "waitpid failed"); | 850 | sway_log_errno(SWAY_ERROR, "waitpid failed"); |
825 | return false; | 851 | return false; |
826 | } | 852 | } |
827 | 853 | ||
828 | return true; | 854 | return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS; |
829 | } | 855 | } |
830 | 856 | ||
831 | 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 97cf667e..e16b4fa8 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -1,4 +1,3 @@ | |||
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> |
@@ -19,6 +18,7 @@ | |||
19 | bool criteria_is_empty(struct criteria *criteria) { | 18 | bool criteria_is_empty(struct criteria *criteria) { |
20 | return !criteria->title | 19 | return !criteria->title |
21 | && !criteria->shell | 20 | && !criteria->shell |
21 | && !criteria->all | ||
22 | && !criteria->app_id | 22 | && !criteria->app_id |
23 | && !criteria->con_mark | 23 | && !criteria->con_mark |
24 | && !criteria->con_id | 24 | && !criteria->con_id |
@@ -96,7 +96,8 @@ void criteria_destroy(struct criteria *criteria) { | |||
96 | pattern_destroy(criteria->window_role); | 96 | pattern_destroy(criteria->window_role); |
97 | #endif | 97 | #endif |
98 | pattern_destroy(criteria->con_mark); | 98 | pattern_destroy(criteria->con_mark); |
99 | free(criteria->workspace); | 99 | pattern_destroy(criteria->workspace); |
100 | free(criteria->target); | ||
100 | free(criteria->cmdlist); | 101 | free(criteria->cmdlist); |
101 | free(criteria->raw); | 102 | free(criteria->raw); |
102 | free(criteria); | 103 | free(criteria); |
@@ -189,7 +190,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
189 | if (criteria->title) { | 190 | if (criteria->title) { |
190 | const char *title = view_get_title(view); | 191 | const char *title = view_get_title(view); |
191 | if (!title) { | 192 | if (!title) { |
192 | return false; | 193 | title = ""; |
193 | } | 194 | } |
194 | 195 | ||
195 | switch (criteria->title->match_type) { | 196 | switch (criteria->title->match_type) { |
@@ -209,7 +210,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
209 | if (criteria->shell) { | 210 | if (criteria->shell) { |
210 | const char *shell = view_get_shell(view); | 211 | const char *shell = view_get_shell(view); |
211 | if (!shell) { | 212 | if (!shell) { |
212 | return false; | 213 | shell = ""; |
213 | } | 214 | } |
214 | 215 | ||
215 | switch (criteria->shell->match_type) { | 216 | switch (criteria->shell->match_type) { |
@@ -229,7 +230,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
229 | if (criteria->app_id) { | 230 | if (criteria->app_id) { |
230 | const char *app_id = view_get_app_id(view); | 231 | const char *app_id = view_get_app_id(view); |
231 | if (!app_id) { | 232 | if (!app_id) { |
232 | return false; | 233 | app_id = ""; |
233 | } | 234 | } |
234 | 235 | ||
235 | switch (criteria->app_id->match_type) { | 236 | switch (criteria->app_id->match_type) { |
@@ -261,7 +262,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
261 | if (criteria->class) { | 262 | if (criteria->class) { |
262 | const char *class = view_get_class(view); | 263 | const char *class = view_get_class(view); |
263 | if (!class) { | 264 | if (!class) { |
264 | return false; | 265 | class = ""; |
265 | } | 266 | } |
266 | 267 | ||
267 | switch (criteria->class->match_type) { | 268 | switch (criteria->class->match_type) { |
@@ -281,12 +282,12 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
281 | if (criteria->instance) { | 282 | if (criteria->instance) { |
282 | const char *instance = view_get_instance(view); | 283 | const char *instance = view_get_instance(view); |
283 | if (!instance) { | 284 | if (!instance) { |
284 | return false; | 285 | instance = ""; |
285 | } | 286 | } |
286 | 287 | ||
287 | switch (criteria->instance->match_type) { | 288 | switch (criteria->instance->match_type) { |
288 | case PATTERN_FOCUSED: | 289 | case PATTERN_FOCUSED: |
289 | if (focused && strcmp(instance, view_get_instance(focused))) { | 290 | if (focused && lenient_strcmp(instance, view_get_instance(focused))) { |
290 | return false; | 291 | return false; |
291 | } | 292 | } |
292 | break; | 293 | break; |
@@ -301,12 +302,12 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
301 | if (criteria->window_role) { | 302 | if (criteria->window_role) { |
302 | const char *window_role = view_get_window_role(view); | 303 | const char *window_role = view_get_window_role(view); |
303 | if (!window_role) { | 304 | if (!window_role) { |
304 | return false; | 305 | window_role = ""; |
305 | } | 306 | } |
306 | 307 | ||
307 | switch (criteria->window_role->match_type) { | 308 | switch (criteria->window_role->match_type) { |
308 | case PATTERN_FOCUSED: | 309 | case PATTERN_FOCUSED: |
309 | if (focused && strcmp(window_role, view_get_window_role(focused))) { | 310 | if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) { |
310 | return false; | 311 | return false; |
311 | } | 312 | } |
312 | break; | 313 | break; |
@@ -455,6 +456,7 @@ static enum atom_name parse_window_type(const char *type) { | |||
455 | #endif | 456 | #endif |
456 | 457 | ||
457 | enum criteria_token { | 458 | enum criteria_token { |
459 | T_ALL, | ||
458 | T_APP_ID, | 460 | T_APP_ID, |
459 | T_CON_ID, | 461 | T_CON_ID, |
460 | T_CON_MARK, | 462 | T_CON_MARK, |
@@ -477,7 +479,9 @@ enum criteria_token { | |||
477 | }; | 479 | }; |
478 | 480 | ||
479 | static enum criteria_token token_from_name(char *name) { | 481 | static enum criteria_token token_from_name(char *name) { |
480 | if (strcmp(name, "app_id") == 0) { | 482 | if (strcmp(name, "all") == 0) { |
483 | return T_ALL; | ||
484 | } else if (strcmp(name, "app_id") == 0) { | ||
481 | return T_APP_ID; | 485 | return T_APP_ID; |
482 | } else if (strcmp(name, "con_id") == 0) { | 486 | } else if (strcmp(name, "con_id") == 0) { |
483 | return T_CON_ID; | 487 | return T_CON_ID; |
@@ -523,8 +527,8 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
523 | return false; | 527 | return false; |
524 | } | 528 | } |
525 | 529 | ||
526 | // Require value, unless token is floating or tiled | 530 | // Require value, unless token is all, floating or tiled |
527 | if (!value && token != T_FLOATING && token != T_TILING) { | 531 | if (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) { |
528 | const char *fmt = "Token '%s' requires a value"; | 532 | const char *fmt = "Token '%s' requires a value"; |
529 | int len = strlen(fmt) + strlen(name) - 1; | 533 | int len = strlen(fmt) + strlen(name) - 1; |
530 | error = malloc(len); | 534 | error = malloc(len); |
@@ -534,6 +538,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
534 | 538 | ||
535 | char *endptr = NULL; | 539 | char *endptr = NULL; |
536 | switch (token) { | 540 | switch (token) { |
541 | case T_ALL: | ||
542 | criteria->all = true; | ||
543 | break; | ||
537 | case T_TITLE: | 544 | case T_TITLE: |
538 | pattern_create(&criteria->title, value); | 545 | pattern_create(&criteria->title, value); |
539 | break; | 546 | break; |
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c deleted file mode 100644 index c8d4502c..00000000 --- a/sway/desktop/desktop.c +++ /dev/null | |||
@@ -1,40 +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; | ||
10 | wlr_output_layout_get_box(root->output_layout, | ||
11 | output->wlr_output, &output_box); | ||
12 | output_damage_surface(output, lx - output_box.x, | ||
13 | ly - output_box.y, surface, whole); | ||
14 | } | ||
15 | } | ||
16 | |||
17 | void desktop_damage_whole_container(struct sway_container *con) { | ||
18 | for (int i = 0; i < root->outputs->length; ++i) { | ||
19 | struct sway_output *output = root->outputs->items[i]; | ||
20 | output_damage_whole_container(output, con); | ||
21 | } | ||
22 | } | ||
23 | |||
24 | void desktop_damage_box(struct wlr_box *box) { | ||
25 | for (int i = 0; i < root->outputs->length; ++i) { | ||
26 | struct sway_output *output = root->outputs->items[i]; | ||
27 | output_damage_box(output, box); | ||
28 | } | ||
29 | } | ||
30 | |||
31 | void desktop_damage_view(struct sway_view *view) { | ||
32 | desktop_damage_whole_container(view->container); | ||
33 | struct wlr_box box = { | ||
34 | .x = view->container->current.content_x - view->geometry.x, | ||
35 | .y = view->container->current.content_y - view->geometry.y, | ||
36 | .width = view->surface->current.width, | ||
37 | .height = view->surface->current.height, | ||
38 | }; | ||
39 | desktop_damage_box(&box); | ||
40 | } | ||
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 82353038..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,33 +41,34 @@ 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->mode != INHIBIT_IDLE_APPLICATION && |
73 | inhibitor->view == view) { | 73 | inhibitor->view == view) { |
74 | return inhibitor; | 74 | return inhibitor; |
@@ -79,9 +79,9 @@ 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->mode == INHIBIT_IDLE_APPLICATION && |
86 | view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { | 86 | view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { |
87 | return inhibitor; | 87 | return inhibitor; |
@@ -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 d44d6338..4b2584b6 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c | |||
@@ -3,10 +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> | 8 | #include <wlr/types/wlr_subcompositor.h> |
9 | #include <wlr/types/wlr_xdg_shell.h> | ||
9 | #include "log.h" | 10 | #include "log.h" |
11 | #include "sway/scene_descriptor.h" | ||
10 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
11 | #include "sway/input/cursor.h" | 13 | #include "sway/input/cursor.h" |
12 | #include "sway/input/input-manager.h" | 14 | #include "sway/input/input-manager.h" |
@@ -17,162 +19,55 @@ | |||
17 | #include "sway/tree/arrange.h" | 19 | #include "sway/tree/arrange.h" |
18 | #include "sway/tree/workspace.h" | 20 | #include "sway/tree/workspace.h" |
19 | 21 | ||
20 | static void apply_exclusive(struct wlr_box *usable_area, | 22 | struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( |
21 | uint32_t anchor, int32_t exclusive, | 23 | struct wlr_surface *surface) { |
22 | int32_t margin_top, int32_t margin_right, | 24 | struct wlr_layer_surface_v1 *layer; |
23 | int32_t margin_bottom, int32_t margin_left) { | 25 | do { |
24 | if (exclusive <= 0) { | 26 | if (!surface) { |
25 | return; | 27 | return NULL; |
26 | } | 28 | } |
27 | struct { | 29 | // Topmost layer surface |
28 | uint32_t singular_anchor; | 30 | if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) { |
29 | uint32_t anchor_triplet; | 31 | return layer; |
30 | int *positive_axis; | 32 | } |
31 | int *negative_axis; | 33 | // Layer subsurface |
32 | int margin; | 34 | if (wlr_subsurface_try_from_wlr_surface(surface)) { |
33 | } edges[] = { | 35 | surface = wlr_surface_get_root_surface(surface); |
34 | // Top | 36 | continue; |
35 | { | 37 | } |
36 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, | 38 | |
37 | .anchor_triplet = | 39 | // Layer surface popup |
38 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | 40 | struct wlr_xdg_surface *xdg_surface = NULL; |
39 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | 41 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) && |
40 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, | 42 | xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) { |
41 | .positive_axis = &usable_area->y, | 43 | if (!xdg_surface->popup->parent) { |
42 | .negative_axis = &usable_area->height, | 44 | return NULL; |
43 | .margin = margin_top, | ||
44 | }, | ||
45 | // Bottom | ||
46 | { | ||
47 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
48 | .anchor_triplet = | ||
49 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | ||
50 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
51 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
52 | .positive_axis = NULL, | ||
53 | .negative_axis = &usable_area->height, | ||
54 | .margin = margin_bottom, | ||
55 | }, | ||
56 | // Left | ||
57 | { | ||
58 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, | ||
59 | .anchor_triplet = | ||
60 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | ||
61 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
62 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
63 | .positive_axis = &usable_area->x, | ||
64 | .negative_axis = &usable_area->width, | ||
65 | .margin = margin_left, | ||
66 | }, | ||
67 | // Right | ||
68 | { | ||
69 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, | ||
70 | .anchor_triplet = | ||
71 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
72 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
73 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
74 | .positive_axis = NULL, | ||
75 | .negative_axis = &usable_area->width, | ||
76 | .margin = margin_right, | ||
77 | }, | ||
78 | }; | ||
79 | for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { | ||
80 | if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) | ||
81 | && exclusive + edges[i].margin > 0) { | ||
82 | if (edges[i].positive_axis) { | ||
83 | *edges[i].positive_axis += exclusive + edges[i].margin; | ||
84 | } | ||
85 | if (edges[i].negative_axis) { | ||
86 | *edges[i].negative_axis -= exclusive + edges[i].margin; | ||
87 | } | 45 | } |
88 | break; | 46 | surface = wlr_surface_get_root_surface(xdg_surface->popup->parent); |
47 | continue; | ||
89 | } | 48 | } |
90 | } | 49 | |
50 | // Return early if the surface is not a layer/xdg_popup/sub surface | ||
51 | return NULL; | ||
52 | } while (true); | ||
91 | } | 53 | } |
92 | 54 | ||
93 | 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, |
94 | struct wlr_box *usable_area, bool exclusive) { | 56 | struct wlr_box *usable_area, struct wlr_scene_tree *tree) { |
95 | struct sway_layer_surface *sway_layer; | 57 | struct wlr_scene_node *node; |
96 | struct wlr_box full_area = { 0 }; | 58 | wl_list_for_each(node, &tree->children, link) { |
97 | wlr_output_effective_resolution(output->wlr_output, | 59 | struct sway_layer_surface *surface = scene_descriptor_try_get(node, |
98 | &full_area.width, &full_area.height); | 60 | SWAY_SCENE_DESC_LAYER_SHELL); |
99 | wl_list_for_each(sway_layer, list, link) { | 61 | // surface could be null during destruction |
100 | struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; | 62 | if (!surface) { |
101 | struct wlr_layer_surface_v1_state *state = &layer->current; | ||
102 | if (exclusive != (state->exclusive_zone > 0)) { | ||
103 | continue; | 63 | continue; |
104 | } | 64 | } |
105 | struct wlr_box bounds; | 65 | |
106 | if (state->exclusive_zone == -1) { | 66 | if (!surface->scene->layer_surface->initialized) { |
107 | bounds = full_area; | ||
108 | } else { | ||
109 | bounds = *usable_area; | ||
110 | } | ||
111 | struct wlr_box box = { | ||
112 | .width = state->desired_width, | ||
113 | .height = state->desired_height | ||
114 | }; | ||
115 | // Horizontal axis | ||
116 | const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ||
117 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; | ||
118 | if (box.width == 0) { | ||
119 | box.x = bounds.x; | ||
120 | } else if ((state->anchor & both_horiz) == both_horiz) { | ||
121 | box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); | ||
122 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
123 | box.x = bounds.x; | ||
124 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
125 | box.x = bounds.x + (bounds.width - box.width); | ||
126 | } else { | ||
127 | box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); | ||
128 | } | ||
129 | // Vertical axis | ||
130 | const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ||
131 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; | ||
132 | if (box.height == 0) { | ||
133 | box.y = bounds.y; | ||
134 | } else if ((state->anchor & both_vert) == both_vert) { | ||
135 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | ||
136 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
137 | box.y = bounds.y; | ||
138 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
139 | box.y = bounds.y + (bounds.height - box.height); | ||
140 | } else { | ||
141 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | ||
142 | } | ||
143 | // Margin | ||
144 | if (box.width == 0) { | ||
145 | box.x += state->margin.left; | ||
146 | box.width = bounds.width - | ||
147 | (state->margin.left + state->margin.right); | ||
148 | } else if ((state->anchor & both_horiz) == both_horiz) { | ||
149 | // don't apply margins | ||
150 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
151 | box.x += state->margin.left; | ||
152 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
153 | box.x -= state->margin.right; | ||
154 | } | ||
155 | if (box.height == 0) { | ||
156 | box.y += state->margin.top; | ||
157 | box.height = bounds.height - | ||
158 | (state->margin.top + state->margin.bottom); | ||
159 | } else if ((state->anchor & both_vert) == both_vert) { | ||
160 | // don't apply margins | ||
161 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
162 | box.y += state->margin.top; | ||
163 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
164 | box.y -= state->margin.bottom; | ||
165 | } | ||
166 | if (!sway_assert(box.width >= 0 && box.height >= 0, | ||
167 | "Expected layer surface to have positive size")) { | ||
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,293 +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 | if (set_focus) { | ||
275 | struct sway_layer_surface *layer = | ||
276 | find_mapped_layer_by_client(client, sway_layer->layer_surface->output); | ||
277 | if (layer) { | ||
278 | seat_set_focus_layer(seat, layer->layer_surface); | ||
279 | } | ||
280 | } | ||
281 | 175 | ||
282 | wlr_layer_surface_v1_destroy(sway_layer->layer_surface); | 176 | layer->output = NULL; |
177 | wlr_scene_node_destroy(&layer->scene->tree->node); | ||
283 | } | 178 | } |
284 | 179 | ||
285 | static void handle_surface_commit(struct wl_listener *listener, void *data) { | 180 | static void handle_node_destroy(struct wl_listener *listener, void *data) { |
286 | struct sway_layer_surface *layer = | 181 | struct sway_layer_surface *layer = |
287 | wl_container_of(listener, layer, surface_commit); | 182 | wl_container_of(listener, layer, node_destroy); |
288 | struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; | ||
289 | struct wlr_output *wlr_output = layer_surface->output; | ||
290 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
291 | struct sway_output *output = wlr_output->data; | ||
292 | struct wlr_box old_extent = layer->extent; | ||
293 | |||
294 | bool layer_changed = false; | ||
295 | if (layer_surface->current.committed != 0 | ||
296 | || layer->mapped != layer_surface->mapped) { | ||
297 | layer->mapped = layer_surface->mapped; | ||
298 | layer_changed = layer->layer != layer_surface->current.layer; | ||
299 | if (layer_changed) { | ||
300 | wl_list_remove(&layer->link); | ||
301 | wl_list_insert(&output->layers[layer_surface->current.layer], | ||
302 | &layer->link); | ||
303 | layer->layer = layer_surface->current.layer; | ||
304 | } | ||
305 | arrange_layers(output); | ||
306 | } | ||
307 | |||
308 | wlr_surface_get_extends(layer_surface->surface, &layer->extent); | ||
309 | layer->extent.x += layer->geo.x; | ||
310 | layer->extent.y += layer->geo.y; | ||
311 | 183 | ||
312 | bool extent_changed = | 184 | // destroy the scene descriptor straight away if it exists, otherwise |
313 | memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0; | 185 | // we will try to reflow still considering the destroyed node. |
314 | if (extent_changed || layer_changed) { | 186 | scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL); |
315 | output_damage_box(output, &old_extent); | ||
316 | output_damage_surface(output, layer->geo.x, layer->geo.y, | ||
317 | layer_surface->surface, true); | ||
318 | } else { | ||
319 | output_damage_surface(output, layer->geo.x, layer->geo.y, | ||
320 | layer_surface->surface, false); | ||
321 | } | ||
322 | 187 | ||
323 | transaction_commit_dirty(); | 188 | // Determine if this layer is being used by an exclusive client. If it is, |
324 | } | 189 | // try and find another layer owned by this client to pass focus to. |
325 | 190 | struct sway_seat *seat = input_manager_get_default_seat(); | |
326 | static void unmap(struct sway_layer_surface *sway_layer) { | 191 | struct wl_client *client = |
327 | struct sway_seat *seat; | 192 | wl_resource_get_client(layer->layer_surface->resource); |
328 | wl_list_for_each(seat, &server.input->seats, link) { | 193 | if (!server.session_lock.lock) { |
329 | if (seat->focused_layer == sway_layer->layer_surface) { | 194 | struct sway_layer_surface *consider_layer = |
330 | seat_set_focus_layer(seat, NULL); | 195 | find_mapped_layer_by_client(client, layer->output); |
196 | if (consider_layer) { | ||
197 | seat_set_focus_layer(seat, consider_layer->layer_surface); | ||
331 | } | 198 | } |
332 | } | 199 | } |
333 | 200 | ||
334 | cursor_rebase_all(); | 201 | if (layer->output) { |
335 | 202 | arrange_layers(layer->output); | |
336 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | 203 | transaction_commit_dirty(); |
337 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
338 | struct sway_output *output = wlr_output->data; | ||
339 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | ||
340 | sway_layer->layer_surface->surface, true); | ||
341 | } | ||
342 | |||
343 | static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface); | ||
344 | |||
345 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
346 | struct sway_layer_surface *sway_layer = | ||
347 | wl_container_of(listener, sway_layer, destroy); | ||
348 | sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", | ||
349 | sway_layer->layer_surface->namespace); | ||
350 | if (sway_layer->layer_surface->mapped) { | ||
351 | unmap(sway_layer); | ||
352 | } | ||
353 | |||
354 | struct sway_layer_subsurface *subsurface, *subsurface_tmp; | ||
355 | wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) { | ||
356 | layer_subsurface_destroy(subsurface); | ||
357 | } | 204 | } |
358 | 205 | ||
359 | wl_list_remove(&sway_layer->link); | 206 | wlr_scene_node_destroy(&layer->popups->node); |
360 | wl_list_remove(&sway_layer->destroy.link); | ||
361 | wl_list_remove(&sway_layer->map.link); | ||
362 | wl_list_remove(&sway_layer->unmap.link); | ||
363 | wl_list_remove(&sway_layer->surface_commit.link); | ||
364 | wl_list_remove(&sway_layer->new_popup.link); | ||
365 | wl_list_remove(&sway_layer->new_subsurface.link); | ||
366 | |||
367 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | ||
368 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
369 | struct sway_output *output = wlr_output->data; | ||
370 | arrange_layers(output); | ||
371 | transaction_commit_dirty(); | ||
372 | wl_list_remove(&sway_layer->output_destroy.link); | ||
373 | sway_layer->layer_surface->output = NULL; | ||
374 | |||
375 | free(sway_layer); | ||
376 | } | ||
377 | 207 | ||
378 | static void handle_map(struct wl_listener *listener, void *data) { | 208 | wl_list_remove(&layer->map.link); |
379 | struct sway_layer_surface *sway_layer = wl_container_of(listener, | 209 | wl_list_remove(&layer->unmap.link); |
380 | sway_layer, map); | 210 | wl_list_remove(&layer->surface_commit.link); |
381 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | 211 | wl_list_remove(&layer->node_destroy.link); |
382 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | 212 | wl_list_remove(&layer->output_destroy.link); |
383 | struct sway_output *output = wlr_output->data; | ||
384 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | ||
385 | sway_layer->layer_surface->surface, true); | ||
386 | wlr_surface_send_enter(sway_layer->layer_surface->surface, | ||
387 | sway_layer->layer_surface->output); | ||
388 | cursor_rebase_all(); | ||
389 | } | ||
390 | 213 | ||
391 | static void handle_unmap(struct wl_listener *listener, void *data) { | 214 | layer->layer_surface->data = NULL; |
392 | struct sway_layer_surface *sway_layer = wl_container_of( | ||
393 | listener, sway_layer, unmap); | ||
394 | unmap(sway_layer); | ||
395 | } | ||
396 | 215 | ||
397 | static void subsurface_damage(struct sway_layer_subsurface *subsurface, | 216 | free(layer); |
398 | bool whole) { | ||
399 | struct sway_layer_surface *layer = subsurface->layer_surface; | ||
400 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
401 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
402 | struct sway_output *output = wlr_output->data; | ||
403 | int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; | ||
404 | int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; | ||
405 | output_damage_surface( | ||
406 | output, ox, oy, subsurface->wlr_subsurface->surface, whole); | ||
407 | } | 217 | } |
408 | 218 | ||
409 | static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { | 219 | static void handle_surface_commit(struct wl_listener *listener, void *data) { |
410 | struct sway_layer_subsurface *subsurface = | 220 | struct sway_layer_surface *surface = |
411 | wl_container_of(listener, subsurface, unmap); | 221 | wl_container_of(listener, surface, surface_commit); |
412 | subsurface_damage(subsurface, true); | ||
413 | } | ||
414 | |||
415 | static void subsurface_handle_map(struct wl_listener *listener, void *data) { | ||
416 | struct sway_layer_subsurface *subsurface = | ||
417 | wl_container_of(listener, subsurface, map); | ||
418 | subsurface_damage(subsurface, true); | ||
419 | } | ||
420 | |||
421 | static void subsurface_handle_commit(struct wl_listener *listener, void *data) { | ||
422 | struct sway_layer_subsurface *subsurface = | ||
423 | wl_container_of(listener, subsurface, commit); | ||
424 | subsurface_damage(subsurface, false); | ||
425 | } | ||
426 | |||
427 | static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) { | ||
428 | wl_list_remove(&subsurface->link); | ||
429 | wl_list_remove(&subsurface->map.link); | ||
430 | wl_list_remove(&subsurface->unmap.link); | ||
431 | wl_list_remove(&subsurface->destroy.link); | ||
432 | wl_list_remove(&subsurface->commit.link); | ||
433 | free(subsurface); | ||
434 | } | ||
435 | |||
436 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
437 | void *data) { | ||
438 | struct sway_layer_subsurface *subsurface = | ||
439 | wl_container_of(listener, subsurface, destroy); | ||
440 | layer_subsurface_destroy(subsurface); | ||
441 | } | ||
442 | 222 | ||
443 | static struct sway_layer_subsurface *create_subsurface( | 223 | struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; |
444 | struct wlr_subsurface *wlr_subsurface, | 224 | if (!layer_surface->initialized) { |
445 | struct sway_layer_surface *layer_surface) { | 225 | return; |
446 | struct sway_layer_subsurface *subsurface = | ||
447 | calloc(1, sizeof(struct sway_layer_subsurface)); | ||
448 | if (subsurface == NULL) { | ||
449 | return NULL; | ||
450 | } | 226 | } |
451 | 227 | ||
452 | subsurface->wlr_subsurface = wlr_subsurface; | 228 | uint32_t committed = layer_surface->current.committed; |
453 | subsurface->layer_surface = layer_surface; | 229 | if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { |
454 | wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); | 230 | enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; |
455 | 231 | struct wlr_scene_tree *output_layer = sway_layer_get_scene( | |
456 | subsurface->map.notify = subsurface_handle_map; | 232 | surface->output, layer_type); |
457 | wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); | 233 | wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); |
458 | subsurface->unmap.notify = subsurface_handle_unmap; | 234 | } |
459 | wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); | ||
460 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
461 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
462 | subsurface->commit.notify = subsurface_handle_commit; | ||
463 | wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit); | ||
464 | |||
465 | return subsurface; | ||
466 | } | ||
467 | |||
468 | static void handle_new_subsurface(struct wl_listener *listener, void *data) { | ||
469 | struct sway_layer_surface *sway_layer_surface = | ||
470 | wl_container_of(listener, sway_layer_surface, new_subsurface); | ||
471 | struct wlr_subsurface *wlr_subsurface = data; | ||
472 | create_subsurface(wlr_subsurface, sway_layer_surface); | ||
473 | } | ||
474 | |||
475 | 235 | ||
476 | static struct sway_layer_surface *popup_get_layer( | 236 | if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) { |
477 | struct sway_layer_popup *popup) { | 237 | surface->mapped = layer_surface->surface->mapped; |
478 | while (popup->parent_type == LAYER_PARENT_POPUP) { | 238 | arrange_layers(surface->output); |
479 | popup = popup->parent_popup; | 239 | transaction_commit_dirty(); |
480 | } | 240 | } |
481 | return popup->parent_layer; | ||
482 | } | 241 | } |
483 | 242 | ||
484 | static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { | 243 | static void handle_map(struct wl_listener *listener, void *data) { |
485 | struct wlr_xdg_popup *popup = layer_popup->wlr_popup; | 244 | struct sway_layer_surface *surface = wl_container_of(listener, |
486 | struct wlr_surface *surface = popup->base->surface; | 245 | surface, map); |
487 | int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; | 246 | |
488 | int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; | 247 | struct wlr_layer_surface_v1 *layer_surface = |
489 | int ox = popup_sx, oy = popup_sy; | 248 | surface->scene->layer_surface; |
490 | struct sway_layer_surface *layer; | 249 | |
491 | while (true) { | 250 | // focus on new surface |
492 | if (layer_popup->parent_type == LAYER_PARENT_POPUP) { | 251 | if (layer_surface->current.keyboard_interactive && |
493 | layer_popup = layer_popup->parent_popup; | 252 | (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || |
494 | ox += layer_popup->wlr_popup->current.geometry.x; | 253 | layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { |
495 | oy += layer_popup->wlr_popup->current.geometry.y; | 254 | struct sway_seat *seat; |
496 | } else { | 255 | wl_list_for_each(seat, &server.input->seats, link) { |
497 | layer = layer_popup->parent_layer; | 256 | // but only if the currently focused layer has a lower precedence |
498 | ox += layer->geo.x; | 257 | if (!seat->focused_layer || |
499 | oy += layer->geo.y; | 258 | seat->focused_layer->current.layer >= layer_surface->current.layer) { |
500 | break; | 259 | seat_set_focus_layer(seat, layer_surface); |
260 | } | ||
501 | } | 261 | } |
262 | arrange_layers(surface->output); | ||
502 | } | 263 | } |
503 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
504 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
505 | struct sway_output *output = wlr_output->data; | ||
506 | output_damage_surface(output, ox, oy, surface, whole); | ||
507 | } | ||
508 | 264 | ||
509 | static void popup_handle_map(struct wl_listener *listener, void *data) { | 265 | cursor_rebase_all(); |
510 | struct sway_layer_popup *popup = wl_container_of(listener, popup, map); | ||
511 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
512 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
513 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
514 | wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); | ||
515 | popup_damage(popup, true); | ||
516 | } | 266 | } |
517 | 267 | ||
518 | static void popup_handle_unmap(struct wl_listener *listener, void *data) { | 268 | static void handle_unmap(struct wl_listener *listener, void *data) { |
519 | struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); | 269 | struct sway_layer_surface *surface = wl_container_of( |
520 | popup_damage(popup, true); | 270 | listener, surface, unmap); |
521 | } | 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 | } | ||
522 | 277 | ||
523 | static void popup_handle_commit(struct wl_listener *listener, void *data) { | 278 | cursor_rebase_all(); |
524 | struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); | ||
525 | popup_damage(popup, false); | ||
526 | } | 279 | } |
527 | 280 | ||
528 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 281 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
529 | struct sway_layer_popup *popup = | 282 | struct sway_layer_popup *popup = |
530 | wl_container_of(listener, popup, destroy); | 283 | wl_container_of(listener, popup, destroy); |
531 | 284 | ||
532 | wl_list_remove(&popup->map.link); | ||
533 | wl_list_remove(&popup->unmap.link); | ||
534 | wl_list_remove(&popup->destroy.link); | 285 | wl_list_remove(&popup->destroy.link); |
286 | wl_list_remove(&popup->new_popup.link); | ||
535 | wl_list_remove(&popup->commit.link); | 287 | wl_list_remove(&popup->commit.link); |
536 | free(popup); | 288 | free(popup); |
537 | } | 289 | } |
538 | 290 | ||
539 | static void popup_unconstrain(struct sway_layer_popup *popup) { | 291 | static void popup_unconstrain(struct sway_layer_popup *popup) { |
540 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
541 | 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; | ||
294 | |||
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 | } | ||
542 | 300 | ||
543 | struct wlr_output *wlr_output = layer->layer_surface->output; | 301 | int lx, ly; |
544 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | 302 | wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly); |
545 | struct sway_output *output = wlr_output->data; | ||
546 | 303 | ||
547 | // 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 |
548 | // of the popup | 305 | // of the popup |
549 | struct wlr_box output_toplevel_sx_box = { | 306 | struct wlr_box output_toplevel_sx_box = { |
550 | .x = -layer->geo.x, | 307 | .x = output->lx - lx, |
551 | .y = -layer->geo.y, | 308 | .y = output->ly - ly, |
552 | .width = output->width, | 309 | .width = output->width, |
553 | .height = output->height, | 310 | .height = output->height, |
554 | }; | 311 | }; |
@@ -556,32 +313,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { | |||
556 | 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); |
557 | } | 314 | } |
558 | 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 | |||
559 | 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); |
560 | 324 | ||
561 | 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, |
562 | enum layer_parent parent_type, void *parent) { | 326 | struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) { |
563 | struct sway_layer_popup *popup = | 327 | struct sway_layer_popup *popup = calloc(1, sizeof(*popup)); |
564 | calloc(1, sizeof(struct sway_layer_popup)); | ||
565 | if (popup == NULL) { | 328 | if (popup == NULL) { |
566 | return NULL; | 329 | return NULL; |
567 | } | 330 | } |
568 | 331 | ||
332 | popup->toplevel = toplevel; | ||
569 | popup->wlr_popup = wlr_popup; | 333 | popup->wlr_popup = wlr_popup; |
570 | popup->parent_type = parent_type; | 334 | popup->scene = wlr_scene_xdg_surface_create(parent, |
571 | popup->parent_layer = parent; | 335 | wlr_popup->base); |
336 | |||
337 | if (!popup->scene) { | ||
338 | free(popup); | ||
339 | return NULL; | ||
340 | } | ||
572 | 341 | ||
573 | popup->map.notify = popup_handle_map; | ||
574 | wl_signal_add(&wlr_popup->base->events.map, &popup->map); | ||
575 | popup->unmap.notify = popup_handle_unmap; | ||
576 | wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); | ||
577 | popup->destroy.notify = popup_handle_destroy; | 342 | popup->destroy.notify = popup_handle_destroy; |
578 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); | 343 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); |
579 | popup->commit.notify = popup_handle_commit; | ||
580 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); | ||
581 | popup->new_popup.notify = popup_handle_new_popup; | 344 | popup->new_popup.notify = popup_handle_new_popup; |
582 | 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); |
583 | 346 | popup->commit.notify = popup_handle_commit; | |
584 | popup_unconstrain(popup); | 347 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); |
585 | 348 | ||
586 | return popup; | 349 | return popup; |
587 | } | 350 | } |
@@ -590,19 +353,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | |||
590 | struct sway_layer_popup *sway_layer_popup = | 353 | struct sway_layer_popup *sway_layer_popup = |
591 | wl_container_of(listener, sway_layer_popup, new_popup); | 354 | wl_container_of(listener, sway_layer_popup, new_popup); |
592 | struct wlr_xdg_popup *wlr_popup = data; | 355 | struct wlr_xdg_popup *wlr_popup = data; |
593 | create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); | 356 | create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene); |
594 | } | 357 | } |
595 | 358 | ||
596 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 359 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
597 | struct sway_layer_surface *sway_layer_surface = | 360 | struct sway_layer_surface *sway_layer_surface = |
598 | wl_container_of(listener, sway_layer_surface, new_popup); | 361 | wl_container_of(listener, sway_layer_surface, new_popup); |
599 | struct wlr_xdg_popup *wlr_popup = data; | 362 | struct wlr_xdg_popup *wlr_popup = data; |
600 | create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); | 363 | create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups); |
601 | } | ||
602 | |||
603 | struct sway_layer_surface *layer_from_wlr_layer_surface_v1( | ||
604 | struct wlr_layer_surface_v1 *layer_surface) { | ||
605 | return layer_surface->data; | ||
606 | } | 364 | } |
607 | 365 | ||
608 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | 366 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { |
@@ -634,10 +392,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
634 | sway_log(SWAY_ERROR, | 392 | sway_log(SWAY_ERROR, |
635 | "no output to auto-assign layer surface '%s' to", | 393 | "no output to auto-assign layer surface '%s' to", |
636 | layer_surface->namespace); | 394 | layer_surface->namespace); |
637 | // Note that layer_surface->output can be NULL | ||
638 | // here, but none of our destroy callbacks are | ||
639 | // registered yet so we don't have to make them | ||
640 | // handle that case. | ||
641 | wlr_layer_surface_v1_destroy(layer_surface); | 395 | wlr_layer_surface_v1_destroy(layer_surface); |
642 | return; | 396 | return; |
643 | } | 397 | } |
@@ -646,44 +400,51 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
646 | layer_surface->output = output->wlr_output; | 400 | layer_surface->output = output->wlr_output; |
647 | } | 401 | } |
648 | 402 | ||
649 | struct sway_layer_surface *sway_layer = | 403 | struct sway_output *output = layer_surface->output->data; |
650 | calloc(1, sizeof(struct sway_layer_surface)); | 404 | |
651 | 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"); | ||
412 | return; | ||
413 | } | ||
414 | |||
415 | struct sway_layer_surface *surface = | ||
416 | sway_layer_surface_create(scene_surface); | ||
417 | if (!surface) { | ||
418 | wlr_layer_surface_v1_destroy(layer_surface); | ||
419 | |||
420 | sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface"); | ||
652 | return; | 421 | return; |
653 | } | 422 | } |
654 | 423 | ||
655 | wl_list_init(&sway_layer->subsurfaces); | 424 | if (!scene_descriptor_assign(&scene_surface->tree->node, |
425 | SWAY_SCENE_DESC_LAYER_SHELL, surface)) { | ||
426 | sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor"); | ||
427 | // destroying the layer_surface will also destroy its corresponding | ||
428 | // scene node | ||
429 | wlr_layer_surface_v1_destroy(layer_surface); | ||
430 | return; | ||
431 | } | ||
656 | 432 | ||
657 | sway_layer->surface_commit.notify = handle_surface_commit; | 433 | surface->output = output; |
658 | wl_signal_add(&layer_surface->surface->events.commit, | ||
659 | &sway_layer->surface_commit); | ||
660 | |||
661 | sway_layer->destroy.notify = handle_destroy; | ||
662 | wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); | ||
663 | sway_layer->map.notify = handle_map; | ||
664 | wl_signal_add(&layer_surface->events.map, &sway_layer->map); | ||
665 | sway_layer->unmap.notify = handle_unmap; | ||
666 | wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap); | ||
667 | sway_layer->new_popup.notify = handle_new_popup; | ||
668 | wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup); | ||
669 | sway_layer->new_subsurface.notify = handle_new_subsurface; | ||
670 | wl_signal_add(&layer_surface->surface->events.new_subsurface, | ||
671 | &sway_layer->new_subsurface); | ||
672 | |||
673 | sway_layer->layer_surface = layer_surface; | ||
674 | layer_surface->data = sway_layer; | ||
675 | 434 | ||
676 | struct sway_output *output = layer_surface->output->data; | 435 | surface->surface_commit.notify = handle_surface_commit; |
677 | sway_layer->output_destroy.notify = handle_output_destroy; | 436 | wl_signal_add(&layer_surface->surface->events.commit, |
678 | wl_signal_add(&output->events.disable, &sway_layer->output_destroy); | 437 | &surface->surface_commit); |
679 | 438 | surface->map.notify = handle_map; | |
680 | wl_list_insert(&output->layers[layer_surface->pending.layer], | 439 | wl_signal_add(&layer_surface->surface->events.map, &surface->map); |
681 | &sway_layer->link); | 440 | surface->unmap.notify = handle_unmap; |
682 | 441 | wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap); | |
683 | // Temporarily set the layer's current state to pending | 442 | surface->new_popup.notify = handle_new_popup; |
684 | // So that we can easily arrange it | 443 | wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); |
685 | struct wlr_layer_surface_v1_state old_state = layer_surface->current; | 444 | |
686 | layer_surface->current = layer_surface->pending; | 445 | surface->output_destroy.notify = handle_output_destroy; |
687 | arrange_layers(output); | 446 | wl_signal_add(&output->events.disable, &surface->output_destroy); |
688 | layer_surface->current = old_state; | 447 | |
448 | surface->node_destroy.notify = handle_node_destroy; | ||
449 | wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy); | ||
689 | } | 450 | } |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 7bb9dab2..bd3de3fe 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -1,44 +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> |
7 | #include <wlr/backend/drm.h> | 6 | #include <wlr/config.h> |
8 | #include <wlr/backend/headless.h> | 7 | #include <wlr/backend/headless.h> |
8 | #include <wlr/render/swapchain.h> | ||
9 | #include <wlr/render/wlr_renderer.h> | 9 | #include <wlr/render/wlr_renderer.h> |
10 | #include <wlr/types/wlr_buffer.h> | 10 | #include <wlr/types/wlr_buffer.h> |
11 | #include <wlr/types/wlr_drm_lease_v1.h> | 11 | #include <wlr/types/wlr_gamma_control_v1.h> |
12 | #include <wlr/types/wlr_matrix.h> | 12 | #include <wlr/types/wlr_matrix.h> |
13 | #include <wlr/types/wlr_output_damage.h> | ||
14 | #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> | ||
15 | #include <wlr/types/wlr_output.h> | 16 | #include <wlr/types/wlr_output.h> |
16 | #include <wlr/types/wlr_presentation_time.h> | 17 | #include <wlr/types/wlr_presentation_time.h> |
17 | #include <wlr/types/wlr_compositor.h> | 18 | #include <wlr/types/wlr_compositor.h> |
18 | #include <wlr/util/region.h> | 19 | #include <wlr/util/region.h> |
20 | #include <wlr/util/transform.h> | ||
19 | #include "config.h" | 21 | #include "config.h" |
20 | #include "log.h" | 22 | #include "log.h" |
21 | #include "sway/config.h" | 23 | #include "sway/config.h" |
22 | #include "sway/desktop/transaction.h" | 24 | #include "sway/desktop/transaction.h" |
23 | #include "sway/input/input-manager.h" | 25 | #include "sway/input/input-manager.h" |
24 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/ipc-server.h" | ||
25 | #include "sway/layers.h" | 28 | #include "sway/layers.h" |
26 | #include "sway/output.h" | 29 | #include "sway/output.h" |
30 | #include "sway/scene_descriptor.h" | ||
27 | #include "sway/server.h" | 31 | #include "sway/server.h" |
28 | #include "sway/surface.h" | ||
29 | #include "sway/tree/arrange.h" | 32 | #include "sway/tree/arrange.h" |
30 | #include "sway/tree/container.h" | 33 | #include "sway/tree/container.h" |
31 | #include "sway/tree/root.h" | 34 | #include "sway/tree/root.h" |
32 | #include "sway/tree/view.h" | 35 | #include "sway/tree/view.h" |
33 | #include "sway/tree/workspace.h" | 36 | #include "sway/tree/workspace.h" |
34 | 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 | |||
35 | 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) { |
36 | for (int i = 0; i < root->outputs->length; ++i) { | 56 | for (int i = 0; i < root->outputs->length; ++i) { |
37 | struct sway_output *output = root->outputs->items[i]; | 57 | struct sway_output *output = root->outputs->items[i]; |
38 | char identifier[128]; | 58 | if (output_match_name_or_id(output, name_or_id)) { |
39 | output_get_identifier(identifier, sizeof(identifier), output); | ||
40 | if (strcasecmp(identifier, name_or_id) == 0 | ||
41 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
42 | return output; | 59 | return output; |
43 | } | 60 | } |
44 | } | 61 | } |
@@ -48,536 +65,217 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) { | |||
48 | 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) { |
49 | struct sway_output *output; | 66 | struct sway_output *output; |
50 | wl_list_for_each(output, &root->all_outputs, link) { | 67 | wl_list_for_each(output, &root->all_outputs, link) { |
51 | char identifier[128]; | 68 | if (output_match_name_or_id(output, name_or_id)) { |
52 | output_get_identifier(identifier, sizeof(identifier), output); | ||
53 | if (strcasecmp(identifier, name_or_id) == 0 | ||
54 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
55 | return output; | 69 | return output; |
56 | } | 70 | } |
57 | } | 71 | } |
58 | return NULL; | 72 | return NULL; |
59 | } | 73 | } |
60 | 74 | ||
61 | struct surface_iterator_data { | ||
62 | sway_surface_iterator_func_t user_iterator; | ||
63 | void *user_data; | ||
64 | |||
65 | struct sway_output *output; | ||
66 | struct sway_view *view; | ||
67 | double ox, oy; | ||
68 | int width, height; | ||
69 | }; | ||
70 | |||
71 | static bool get_surface_box(struct surface_iterator_data *data, | ||
72 | struct wlr_surface *surface, int sx, int sy, | ||
73 | struct wlr_box *surface_box) { | ||
74 | struct sway_output *output = data->output; | ||
75 | |||
76 | if (!wlr_surface_has_buffer(surface)) { | ||
77 | return false; | ||
78 | } | ||
79 | 75 | ||
80 | int sw = surface->current.width; | 76 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { |
81 | int sh = surface->current.height; | 77 | struct sway_seat *seat = input_manager_current_seat(); |
82 | 78 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); | |
83 | struct wlr_box box = { | 79 | if (!focus) { |
84 | .x = floor(data->ox + sx), | 80 | if (!output->workspaces->length) { |
85 | .y = floor(data->oy + sy), | 81 | return NULL; |
86 | .width = sw, | 82 | } |
87 | .height = sh, | 83 | return output->workspaces->items[0]; |
88 | }; | ||
89 | if (surface_box != NULL) { | ||
90 | memcpy(surface_box, &box, sizeof(struct wlr_box)); | ||
91 | } | 84 | } |
92 | 85 | return focus->sway_workspace; | |
93 | struct wlr_box output_box = { | ||
94 | .width = output->width, | ||
95 | .height = output->height, | ||
96 | }; | ||
97 | |||
98 | struct wlr_box intersection; | ||
99 | return wlr_box_intersection(&intersection, &output_box, &box); | ||
100 | } | 86 | } |
101 | 87 | ||
102 | static void output_for_each_surface_iterator(struct wlr_surface *surface, | 88 | struct send_frame_done_data { |
103 | int sx, int sy, void *_data) { | 89 | struct timespec when; |
104 | struct surface_iterator_data *data = _data; | 90 | int msec_until_refresh; |
105 | 91 | struct sway_output *output; | |
106 | struct wlr_box box; | 92 | }; |
107 | bool intersects = get_surface_box(data, surface, sx, sy, &box); | ||
108 | if (!intersects) { | ||
109 | return; | ||
110 | } | ||
111 | 93 | ||
112 | data->user_iterator(data->output, data->view, surface, &box, | 94 | struct buffer_timer { |
113 | data->user_data); | 95 | struct wl_listener destroy; |
114 | } | 96 | struct wl_event_source *frame_done_timer; |
97 | }; | ||
115 | 98 | ||
116 | void output_surface_for_each_surface(struct sway_output *output, | 99 | static int handle_buffer_timer(void *data) { |
117 | struct wlr_surface *surface, double ox, double oy, | 100 | struct wlr_scene_buffer *buffer = data; |
118 | sway_surface_iterator_func_t iterator, void *user_data) { | ||
119 | struct surface_iterator_data data = { | ||
120 | .user_iterator = iterator, | ||
121 | .user_data = user_data, | ||
122 | .output = output, | ||
123 | .view = NULL, | ||
124 | .ox = ox, | ||
125 | .oy = oy, | ||
126 | .width = surface->current.width, | ||
127 | .height = surface->current.height, | ||
128 | }; | ||
129 | |||
130 | wlr_surface_for_each_surface(surface, | ||
131 | output_for_each_surface_iterator, &data); | ||
132 | } | ||
133 | 101 | ||
134 | void output_view_for_each_surface(struct sway_output *output, | 102 | struct timespec now; |
135 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 103 | clock_gettime(CLOCK_MONOTONIC, &now); |
136 | void *user_data) { | 104 | wlr_scene_buffer_send_frame_done(buffer, &now); |
137 | struct surface_iterator_data data = { | 105 | return 0; |
138 | .user_iterator = iterator, | ||
139 | .user_data = user_data, | ||
140 | .output = output, | ||
141 | .view = view, | ||
142 | .ox = view->container->surface_x - output->lx | ||
143 | - view->geometry.x, | ||
144 | .oy = view->container->surface_y - output->ly | ||
145 | - view->geometry.y, | ||
146 | .width = view->container->current.content_width, | ||
147 | .height = view->container->current.content_height, | ||
148 | }; | ||
149 | |||
150 | view_for_each_surface(view, output_for_each_surface_iterator, &data); | ||
151 | } | 106 | } |
152 | 107 | ||
153 | void output_view_for_each_popup_surface(struct sway_output *output, | 108 | static void handle_buffer_timer_destroy(struct wl_listener *listener, |
154 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 109 | void *data) { |
155 | void *user_data) { | 110 | struct buffer_timer *timer = wl_container_of(listener, timer, destroy); |
156 | struct surface_iterator_data data = { | ||
157 | .user_iterator = iterator, | ||
158 | .user_data = user_data, | ||
159 | .output = output, | ||
160 | .view = view, | ||
161 | .ox = view->container->surface_x - output->lx | ||
162 | - view->geometry.x, | ||
163 | .oy = view->container->surface_y - output->ly | ||
164 | - view->geometry.y, | ||
165 | .width = view->container->current.content_width, | ||
166 | .height = view->container->current.content_height, | ||
167 | }; | ||
168 | |||
169 | view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); | ||
170 | } | ||
171 | 111 | ||
172 | void output_layer_for_each_surface(struct sway_output *output, | 112 | wl_list_remove(&timer->destroy.link); |
173 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 113 | wl_event_source_remove(timer->frame_done_timer); |
174 | void *user_data) { | 114 | free(timer); |
175 | struct sway_layer_surface *layer_surface; | ||
176 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
177 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
178 | layer_surface->layer_surface; | ||
179 | struct wlr_surface *surface = wlr_layer_surface_v1->surface; | ||
180 | struct surface_iterator_data data = { | ||
181 | .user_iterator = iterator, | ||
182 | .user_data = user_data, | ||
183 | .output = output, | ||
184 | .view = NULL, | ||
185 | .ox = layer_surface->geo.x, | ||
186 | .oy = layer_surface->geo.y, | ||
187 | .width = surface->current.width, | ||
188 | .height = surface->current.height, | ||
189 | }; | ||
190 | wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1, | ||
191 | output_for_each_surface_iterator, &data); | ||
192 | } | ||
193 | } | 115 | } |
194 | 116 | ||
195 | 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) { |
196 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 118 | struct buffer_timer *timer = |
197 | void *user_data) { | 119 | scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); |
198 | struct sway_layer_surface *layer_surface; | 120 | if (timer) { |
199 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 121 | return timer; |
200 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
201 | layer_surface->layer_surface; | ||
202 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | ||
203 | layer_surface->geo.x, layer_surface->geo.y, iterator, | ||
204 | user_data); | ||
205 | } | 122 | } |
206 | } | ||
207 | |||
208 | 123 | ||
209 | void output_layer_for_each_popup_surface(struct sway_output *output, | 124 | timer = calloc(1, sizeof(struct buffer_timer)); |
210 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 125 | if (!timer) { |
211 | void *user_data) { | 126 | return NULL; |
212 | struct sway_layer_surface *layer_surface; | ||
213 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
214 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
215 | layer_surface->layer_surface; | ||
216 | struct wlr_surface *surface = wlr_layer_surface_v1->surface; | ||
217 | struct surface_iterator_data data = { | ||
218 | .user_iterator = iterator, | ||
219 | .user_data = user_data, | ||
220 | .output = output, | ||
221 | .view = NULL, | ||
222 | .ox = layer_surface->geo.x, | ||
223 | .oy = layer_surface->geo.y, | ||
224 | .width = surface->current.width, | ||
225 | .height = surface->current.height, | ||
226 | }; | ||
227 | wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1, | ||
228 | output_for_each_surface_iterator, &data); | ||
229 | } | 127 | } |
230 | } | ||
231 | 128 | ||
232 | #if HAVE_XWAYLAND | 129 | timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, |
233 | void output_unmanaged_for_each_surface(struct sway_output *output, | 130 | handle_buffer_timer, buffer); |
234 | struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, | 131 | if (!timer->frame_done_timer) { |
235 | void *user_data) { | 132 | free(timer); |
236 | struct sway_xwayland_unmanaged *unmanaged_surface; | 133 | return NULL; |
237 | wl_list_for_each(unmanaged_surface, unmanaged, link) { | ||
238 | struct wlr_xwayland_surface *xsurface = | ||
239 | unmanaged_surface->wlr_xwayland_surface; | ||
240 | double ox = unmanaged_surface->lx - output->lx; | ||
241 | double oy = unmanaged_surface->ly - output->ly; | ||
242 | |||
243 | output_surface_for_each_surface(output, xsurface->surface, ox, oy, | ||
244 | iterator, user_data); | ||
245 | } | 134 | } |
246 | } | ||
247 | #endif | ||
248 | 135 | ||
249 | void output_drag_icons_for_each_surface(struct sway_output *output, | 136 | scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer); |
250 | struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, | ||
251 | void *user_data) { | ||
252 | struct sway_drag_icon *drag_icon; | ||
253 | wl_list_for_each(drag_icon, drag_icons, link) { | ||
254 | double ox = drag_icon->x - output->lx; | ||
255 | double oy = drag_icon->y - output->ly; | ||
256 | |||
257 | if (drag_icon->wlr_drag_icon->mapped) { | ||
258 | output_surface_for_each_surface(output, | ||
259 | drag_icon->wlr_drag_icon->surface, ox, oy, | ||
260 | iterator, user_data); | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | 137 | ||
265 | static void for_each_surface_container_iterator(struct sway_container *con, | 138 | timer->destroy.notify = handle_buffer_timer_destroy; |
266 | void *_data) { | 139 | wl_signal_add(&buffer->node.events.destroy, &timer->destroy); |
267 | if (!con->view || !view_is_visible(con->view)) { | ||
268 | return; | ||
269 | } | ||
270 | 140 | ||
271 | struct surface_iterator_data *data = _data; | 141 | return timer; |
272 | output_view_for_each_surface(data->output, con->view, | ||
273 | data->user_iterator, data->user_data); | ||
274 | } | 142 | } |
275 | 143 | ||
276 | static void output_for_each_surface(struct sway_output *output, | 144 | static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, |
277 | sway_surface_iterator_func_t iterator, void *user_data) { | 145 | int x, int y, void *user_data) { |
278 | if (server.session_lock.locked) { | 146 | struct send_frame_done_data *data = user_data; |
279 | if (server.session_lock.lock == NULL) { | 147 | struct sway_output *output = data->output; |
280 | return; | 148 | int view_max_render_time = 0; |
281 | } | ||
282 | struct wlr_session_lock_surface_v1 *lock_surface; | ||
283 | wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { | ||
284 | if (lock_surface->output != output->wlr_output) { | ||
285 | continue; | ||
286 | } | ||
287 | if (!lock_surface->mapped) { | ||
288 | continue; | ||
289 | } | ||
290 | 149 | ||
291 | output_surface_for_each_surface(output, lock_surface->surface, | 150 | if (buffer->primary_output != data->output->scene_output) { |
292 | 0.0, 0.0, iterator, user_data); | ||
293 | } | ||
294 | return; | 151 | return; |
295 | } | 152 | } |
296 | 153 | ||
297 | if (output_has_opaque_overlay_layer_surface(output)) { | 154 | struct wlr_scene_node *current = &buffer->node; |
298 | goto overlay; | 155 | while (true) { |
299 | } | 156 | struct sway_view *view = scene_descriptor_try_get(current, |
300 | 157 | SWAY_SCENE_DESC_VIEW); | |
301 | struct surface_iterator_data data = { | 158 | if (view) { |
302 | .user_iterator = iterator, | 159 | view_max_render_time = view->max_render_time; |
303 | .user_data = user_data, | 160 | break; |
304 | .output = output, | ||
305 | .view = NULL, | ||
306 | }; | ||
307 | |||
308 | struct sway_workspace *workspace = output_get_active_workspace(output); | ||
309 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
310 | if (!fullscreen_con) { | ||
311 | if (!workspace) { | ||
312 | return; | ||
313 | } | ||
314 | fullscreen_con = workspace->current.fullscreen; | ||
315 | } | ||
316 | if (fullscreen_con) { | ||
317 | for_each_surface_container_iterator(fullscreen_con, &data); | ||
318 | container_for_each_child(fullscreen_con, | ||
319 | for_each_surface_container_iterator, &data); | ||
320 | |||
321 | // TODO: Show transient containers for fullscreen global | ||
322 | if (fullscreen_con == workspace->current.fullscreen) { | ||
323 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
324 | struct sway_container *floater = | ||
325 | workspace->current.floating->items[i]; | ||
326 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
327 | for_each_surface_container_iterator(floater, &data); | ||
328 | } | ||
329 | } | ||
330 | } | 161 | } |
331 | #if HAVE_XWAYLAND | ||
332 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
333 | iterator, user_data); | ||
334 | #endif | ||
335 | } else { | ||
336 | output_layer_for_each_surface(output, | ||
337 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
338 | iterator, user_data); | ||
339 | output_layer_for_each_surface(output, | ||
340 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
341 | iterator, user_data); | ||
342 | |||
343 | workspace_for_each_container(workspace, | ||
344 | for_each_surface_container_iterator, &data); | ||
345 | |||
346 | #if HAVE_XWAYLAND | ||
347 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
348 | iterator, user_data); | ||
349 | #endif | ||
350 | output_layer_for_each_surface(output, | ||
351 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
352 | iterator, user_data); | ||
353 | } | ||
354 | |||
355 | overlay: | ||
356 | output_layer_for_each_surface(output, | ||
357 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
358 | iterator, user_data); | ||
359 | output_drag_icons_for_each_surface(output, &root->drag_icons, | ||
360 | iterator, user_data); | ||
361 | } | ||
362 | |||
363 | static int scale_length(int length, int offset, float scale) { | ||
364 | return round((offset + length) * scale) - round(offset * scale); | ||
365 | } | ||
366 | |||
367 | void scale_box(struct wlr_box *box, float scale) { | ||
368 | box->width = scale_length(box->width, box->x, scale); | ||
369 | box->height = scale_length(box->height, box->y, scale); | ||
370 | box->x = round(box->x * scale); | ||
371 | box->y = round(box->y * scale); | ||
372 | } | ||
373 | 162 | ||
374 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { | 163 | if (!current->parent) { |
375 | struct sway_seat *seat = input_manager_current_seat(); | 164 | break; |
376 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); | ||
377 | if (!focus) { | ||
378 | if (!output->workspaces->length) { | ||
379 | return NULL; | ||
380 | } | 165 | } |
381 | return output->workspaces->items[0]; | ||
382 | } | ||
383 | return focus->sway_workspace; | ||
384 | } | ||
385 | 166 | ||
386 | bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { | 167 | current = ¤t->parent->node; |
387 | struct sway_layer_surface *sway_layer_surface; | ||
388 | wl_list_for_each(sway_layer_surface, | ||
389 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | ||
390 | struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface; | ||
391 | pixman_box32_t output_box = { | ||
392 | .x2 = output->width, | ||
393 | .y2 = output->height, | ||
394 | }; | ||
395 | pixman_region32_t surface_opaque_box; | ||
396 | pixman_region32_init(&surface_opaque_box); | ||
397 | pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); | ||
398 | pixman_region32_translate(&surface_opaque_box, | ||
399 | sway_layer_surface->geo.x, sway_layer_surface->geo.y); | ||
400 | pixman_region_overlap_t contains = | ||
401 | pixman_region32_contains_rectangle(&surface_opaque_box, &output_box); | ||
402 | pixman_region32_fini(&surface_opaque_box); | ||
403 | |||
404 | if (contains == PIXMAN_REGION_IN) { | ||
405 | return true; | ||
406 | } | ||
407 | } | 168 | } |
408 | return false; | ||
409 | } | ||
410 | |||
411 | struct send_frame_done_data { | ||
412 | struct timespec when; | ||
413 | int msec_until_refresh; | ||
414 | }; | ||
415 | |||
416 | static void send_frame_done_iterator(struct sway_output *output, | ||
417 | struct sway_view *view, struct wlr_surface *surface, | ||
418 | struct wlr_box *box, void *user_data) { | ||
419 | int view_max_render_time = 0; | ||
420 | if (view != NULL) { | ||
421 | view_max_render_time = view->max_render_time; | ||
422 | } | ||
423 | |||
424 | struct send_frame_done_data *data = user_data; | ||
425 | 169 | ||
426 | int delay = data->msec_until_refresh - output->max_render_time | 170 | int delay = data->msec_until_refresh - output->max_render_time |
427 | - view_max_render_time; | 171 | - view_max_render_time; |
428 | 172 | ||
429 | if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { | 173 | struct buffer_timer *timer = NULL; |
430 | wlr_surface_send_frame_done(surface, &data->when); | ||
431 | } else { | ||
432 | struct sway_surface *sway_surface = surface->data; | ||
433 | wl_event_source_timer_update(sway_surface->frame_done_timer, delay); | ||
434 | } | ||
435 | } | ||
436 | |||
437 | static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { | ||
438 | output_for_each_surface(output, send_frame_done_iterator, data); | ||
439 | } | ||
440 | |||
441 | static void count_surface_iterator(struct sway_output *output, | ||
442 | struct sway_view *view, struct wlr_surface *surface, | ||
443 | struct wlr_box *box, void *data) { | ||
444 | size_t *n = data; | ||
445 | (*n)++; | ||
446 | } | ||
447 | 174 | ||
448 | static bool scan_out_fullscreen_view(struct sway_output *output, | 175 | if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { |
449 | struct sway_view *view) { | 176 | timer = buffer_timer_get_or_create(buffer); |
450 | struct wlr_output *wlr_output = output->wlr_output; | ||
451 | struct sway_workspace *workspace = output->current.active_workspace; | ||
452 | if (!sway_assert(workspace, "Expected an active workspace")) { | ||
453 | return false; | ||
454 | } | 177 | } |
455 | 178 | ||
456 | if (server.session_lock.locked) { | 179 | if (timer) { |
457 | 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); | ||
458 | } | 183 | } |
184 | } | ||
459 | 185 | ||
460 | if (!wl_list_empty(&view->saved_buffers)) { | 186 | static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, |
461 | return false; | 187 | struct wlr_scene_buffer *buffer) { |
188 | // if we are scaling down, we should always choose linear | ||
189 | if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( | ||
190 | buffer->dst_width < buffer->buffer_width || | ||
191 | buffer->dst_height < buffer->buffer_height)) { | ||
192 | return WLR_SCALE_FILTER_BILINEAR; | ||
462 | } | 193 | } |
463 | 194 | ||
464 | for (int i = 0; i < workspace->current.floating->length; ++i) { | 195 | switch (output->scale_filter) { |
465 | struct sway_container *floater = | 196 | case SCALE_FILTER_LINEAR: |
466 | workspace->current.floating->items[i]; | 197 | return WLR_SCALE_FILTER_BILINEAR; |
467 | if (container_is_transient_for(floater, view->container)) { | 198 | case SCALE_FILTER_NEAREST: |
468 | return false; | 199 | return WLR_SCALE_FILTER_NEAREST; |
469 | } | 200 | default: |
201 | abort(); // unreachable | ||
470 | } | 202 | } |
203 | } | ||
471 | 204 | ||
472 | #if HAVE_XWAYLAND | 205 | static void output_configure_scene(struct sway_output *output, |
473 | if (!wl_list_empty(&root->xwayland_unmanaged)) { | 206 | struct wlr_scene_node *node, float opacity) { |
474 | return false; | 207 | if (!node->enabled) { |
475 | } | 208 | return; |
476 | #endif | ||
477 | |||
478 | if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { | ||
479 | return false; | ||
480 | } | ||
481 | if (!wl_list_empty(&root->drag_icons)) { | ||
482 | return false; | ||
483 | } | 209 | } |
484 | 210 | ||
485 | struct wlr_surface *surface = view->surface; | 211 | struct sway_container *con = |
486 | if (surface == NULL) { | 212 | scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER); |
487 | return false; | 213 | if (con) { |
488 | } | 214 | opacity = con->alpha; |
489 | size_t n_surfaces = 0; | ||
490 | output_view_for_each_surface(output, view, | ||
491 | count_surface_iterator, &n_surfaces); | ||
492 | if (n_surfaces != 1) { | ||
493 | return false; | ||
494 | } | 215 | } |
495 | 216 | ||
496 | if (surface->buffer == NULL) { | 217 | if (node->type == WLR_SCENE_NODE_BUFFER) { |
497 | return false; | 218 | struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); |
498 | } | ||
499 | 219 | ||
500 | if ((float)surface->current.scale != wlr_output->scale || | 220 | // hack: don't call the scene setter because that will damage all outputs |
501 | surface->current.transform != wlr_output->transform) { | 221 | // We don't want to damage outputs that aren't our current output that |
502 | return false; | 222 | // we're configuring |
503 | } | 223 | buffer->filter_mode = get_scale_filter(output, buffer); |
504 | 224 | ||
505 | wlr_output_attach_buffer(wlr_output, &surface->buffer->base); | 225 | wlr_scene_buffer_set_opacity(buffer, opacity); |
506 | if (!wlr_output_test(wlr_output)) { | 226 | } else if (node->type == WLR_SCENE_NODE_TREE) { |
507 | 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 | } | ||
508 | } | 232 | } |
509 | |||
510 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
511 | wlr_output); | ||
512 | |||
513 | return wlr_output_commit(wlr_output); | ||
514 | } | 233 | } |
515 | 234 | ||
516 | static int output_repaint_timer_handler(void *data) { | 235 | static int output_repaint_timer_handler(void *data) { |
517 | struct sway_output *output = data; | 236 | struct sway_output *output = data; |
518 | if (output->wlr_output == NULL) { | ||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | output->wlr_output->frame_pending = false; | ||
523 | 237 | ||
524 | struct sway_workspace *workspace = output->current.active_workspace; | 238 | if (!output->enabled) { |
525 | if (workspace == NULL) { | ||
526 | return 0; | 239 | return 0; |
527 | } | 240 | } |
528 | 241 | ||
529 | struct sway_container *fullscreen_con = root->fullscreen_global; | 242 | output->wlr_output->frame_pending = false; |
530 | if (!fullscreen_con) { | ||
531 | fullscreen_con = workspace->current.fullscreen; | ||
532 | } | ||
533 | 243 | ||
534 | if (fullscreen_con && fullscreen_con->view && !debug.noscanout) { | 244 | output_configure_scene(output, &root->root_scene->tree.node, 1.0f); |
535 | // Try to scan-out the fullscreen view | ||
536 | static bool last_scanned_out = false; | ||
537 | bool scanned_out = | ||
538 | scan_out_fullscreen_view(output, fullscreen_con->view); | ||
539 | 245 | ||
540 | if (scanned_out && !last_scanned_out) { | 246 | if (output->gamma_lut_changed) { |
541 | sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", | 247 | struct wlr_output_state pending; |
542 | 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; | ||
543 | } | 251 | } |
544 | if (last_scanned_out && !scanned_out) { | 252 | |
545 | sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", | 253 | output->gamma_lut_changed = false; |
546 | output->wlr_output->name); | 254 | struct wlr_gamma_control_v1 *gamma_control = |
547 | 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; | ||
548 | } | 260 | } |
549 | last_scanned_out = scanned_out; | ||
550 | 261 | ||
551 | 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); | ||
552 | return 0; | 265 | return 0; |
553 | } | 266 | } |
554 | } | ||
555 | 267 | ||
556 | bool needs_frame; | 268 | wlr_output_state_finish(&pending); |
557 | pixman_region32_t damage; | ||
558 | pixman_region32_init(&damage); | ||
559 | if (!wlr_output_damage_attach_render(output->damage, | ||
560 | &needs_frame, &damage)) { | ||
561 | return 0; | 269 | return 0; |
562 | } | 270 | } |
563 | 271 | ||
564 | if (needs_frame) { | 272 | wlr_scene_output_commit(output->scene_output, NULL); |
565 | struct timespec now; | ||
566 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
567 | |||
568 | output_render(output, &now, &damage); | ||
569 | } else { | ||
570 | wlr_output_rollback(output->wlr_output); | ||
571 | } | ||
572 | |||
573 | pixman_region32_fini(&damage); | ||
574 | |||
575 | return 0; | 273 | return 0; |
576 | } | 274 | } |
577 | 275 | ||
578 | static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | 276 | static void handle_frame(struct wl_listener *listener, void *user_data) { |
579 | struct sway_output *output = | 277 | struct sway_output *output = |
580 | wl_container_of(listener, output, damage_frame); | 278 | wl_container_of(listener, output, frame); |
581 | if (!output->enabled || !output->wlr_output->enabled) { | 279 | if (!output->enabled || !output->wlr_output->enabled) { |
582 | return; | 280 | return; |
583 | } | 281 | } |
@@ -588,9 +286,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
588 | 286 | ||
589 | if (output->max_render_time != 0) { | 287 | if (output->max_render_time != 0) { |
590 | struct timespec now; | 288 | struct timespec now; |
591 | clockid_t presentation_clock | 289 | clock_gettime(CLOCK_MONOTONIC, &now); |
592 | = wlr_backend_get_presentation_clock(server.backend); | ||
593 | clock_gettime(presentation_clock, &now); | ||
594 | 290 | ||
595 | const long NSEC_IN_SECONDS = 1000000000; | 291 | const long NSEC_IN_SECONDS = 1000000000; |
596 | struct timespec predicted_refresh = output->last_presentation; | 292 | struct timespec predicted_refresh = output->last_presentation; |
@@ -637,116 +333,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
637 | struct send_frame_done_data data = {0}; | 333 | struct send_frame_done_data data = {0}; |
638 | clock_gettime(CLOCK_MONOTONIC, &data.when); | 334 | clock_gettime(CLOCK_MONOTONIC, &data.when); |
639 | data.msec_until_refresh = msec_until_refresh; | 335 | data.msec_until_refresh = msec_until_refresh; |
640 | send_frame_done(output, &data); | 336 | data.output = output; |
641 | } | 337 | wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); |
642 | |||
643 | void output_damage_whole(struct sway_output *output) { | ||
644 | // The output can exist with no wlr_output if it's just been disconnected | ||
645 | // and the transaction to evacuate it has't completed yet. | ||
646 | if (output && output->wlr_output && output->damage) { | ||
647 | wlr_output_damage_add_whole(output->damage); | ||
648 | } | ||
649 | } | ||
650 | |||
651 | static void damage_surface_iterator(struct sway_output *output, | ||
652 | struct sway_view *view, struct wlr_surface *surface, | ||
653 | struct wlr_box *_box, void *_data) { | ||
654 | bool *data = _data; | ||
655 | bool whole = *data; | ||
656 | |||
657 | struct wlr_box box = *_box; | ||
658 | scale_box(&box, output->wlr_output->scale); | ||
659 | |||
660 | pixman_region32_t damage; | ||
661 | pixman_region32_init(&damage); | ||
662 | wlr_surface_get_effective_damage(surface, &damage); | ||
663 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); | ||
664 | if (ceil(output->wlr_output->scale) > surface->current.scale) { | ||
665 | // When scaling up a surface, it'll become blurry so we need to | ||
666 | // expand the damage region | ||
667 | wlr_region_expand(&damage, &damage, | ||
668 | ceil(output->wlr_output->scale) - surface->current.scale); | ||
669 | } | ||
670 | pixman_region32_translate(&damage, box.x, box.y); | ||
671 | wlr_output_damage_add(output->damage, &damage); | ||
672 | pixman_region32_fini(&damage); | ||
673 | |||
674 | if (whole) { | ||
675 | wlr_output_damage_add_box(output->damage, &box); | ||
676 | } | ||
677 | |||
678 | if (!wl_list_empty(&surface->current.frame_callback_list)) { | ||
679 | wlr_output_schedule_frame(output->wlr_output); | ||
680 | } | ||
681 | } | ||
682 | |||
683 | void output_damage_surface(struct sway_output *output, double ox, double oy, | ||
684 | struct wlr_surface *surface, bool whole) { | ||
685 | output_surface_for_each_surface(output, surface, ox, oy, | ||
686 | damage_surface_iterator, &whole); | ||
687 | } | ||
688 | |||
689 | void output_damage_from_view(struct sway_output *output, | ||
690 | struct sway_view *view) { | ||
691 | if (!view_is_visible(view)) { | ||
692 | return; | ||
693 | } | ||
694 | bool whole = false; | ||
695 | output_view_for_each_surface(output, view, damage_surface_iterator, &whole); | ||
696 | } | ||
697 | |||
698 | // Expecting an unscaled box in layout coordinates | ||
699 | void output_damage_box(struct sway_output *output, struct wlr_box *_box) { | ||
700 | struct wlr_box box; | ||
701 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
702 | box.x -= output->lx; | ||
703 | box.y -= output->ly; | ||
704 | scale_box(&box, output->wlr_output->scale); | ||
705 | wlr_output_damage_add_box(output->damage, &box); | ||
706 | } | ||
707 | |||
708 | static void damage_child_views_iterator(struct sway_container *con, | ||
709 | void *data) { | ||
710 | if (!con->view || !view_is_visible(con->view)) { | ||
711 | return; | ||
712 | } | ||
713 | struct sway_output *output = data; | ||
714 | bool whole = true; | ||
715 | output_view_for_each_surface(output, con->view, damage_surface_iterator, | ||
716 | &whole); | ||
717 | } | ||
718 | |||
719 | void output_damage_whole_container(struct sway_output *output, | ||
720 | struct sway_container *con) { | ||
721 | // Pad the box by 1px, because the width is a double and might be a fraction | ||
722 | struct wlr_box box = { | ||
723 | .x = con->current.x - output->lx - 1, | ||
724 | .y = con->current.y - output->ly - 1, | ||
725 | .width = con->current.width + 2, | ||
726 | .height = con->current.height + 2, | ||
727 | }; | ||
728 | scale_box(&box, output->wlr_output->scale); | ||
729 | wlr_output_damage_add_box(output->damage, &box); | ||
730 | // Damage subsurfaces as well, which may extend outside the box | ||
731 | if (con->view) { | ||
732 | damage_child_views_iterator(con, output); | ||
733 | } else { | ||
734 | container_for_each_child(con, damage_child_views_iterator, output); | ||
735 | } | ||
736 | } | ||
737 | |||
738 | static void damage_handle_destroy(struct wl_listener *listener, void *data) { | ||
739 | struct sway_output *output = | ||
740 | wl_container_of(listener, output, damage_destroy); | ||
741 | if (!output->enabled) { | ||
742 | return; | ||
743 | } | ||
744 | output_disable(output); | ||
745 | |||
746 | wl_list_remove(&output->damage_destroy.link); | ||
747 | wl_list_remove(&output->damage_frame.link); | ||
748 | |||
749 | transaction_commit_dirty(); | ||
750 | } | 338 | } |
751 | 339 | ||
752 | static void update_output_manager_config(struct sway_server *server) { | 340 | static void update_output_manager_config(struct sway_server *server) { |
@@ -764,33 +352,36 @@ static void update_output_manager_config(struct sway_server *server) { | |||
764 | wlr_output_layout_get_box(root->output_layout, | 352 | wlr_output_layout_get_box(root->output_layout, |
765 | output->wlr_output, &output_box); | 353 | output->wlr_output, &output_box); |
766 | // We mark the output enabled when it's switched off but not disabled | 354 | // We mark the output enabled when it's switched off but not disabled |
767 | config_head->state.enabled = output->current_mode != NULL && output->enabled; | 355 | config_head->state.enabled = !wlr_box_empty(&output_box); |
768 | config_head->state.mode = output->current_mode; | 356 | config_head->state.x = output_box.x; |
769 | if (!wlr_box_empty(&output_box)) { | 357 | config_head->state.y = output_box.y; |
770 | config_head->state.x = output_box.x; | ||
771 | config_head->state.y = output_box.y; | ||
772 | } | ||
773 | } | 358 | } |
774 | 359 | ||
775 | 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(); | ||
776 | } | 363 | } |
777 | 364 | ||
778 | static void handle_destroy(struct wl_listener *listener, void *data) { | 365 | static void begin_destroy(struct sway_output *output) { |
779 | struct sway_output *output = wl_container_of(listener, output, destroy); | ||
780 | struct sway_server *server = output->server; | 366 | struct sway_server *server = output->server; |
781 | output_begin_destroy(output); | ||
782 | 367 | ||
783 | if (output->enabled) { | 368 | if (output->enabled) { |
784 | output_disable(output); | 369 | output_disable(output); |
785 | } | 370 | } |
786 | 371 | ||
372 | output_begin_destroy(output); | ||
373 | |||
787 | wl_list_remove(&output->link); | 374 | wl_list_remove(&output->link); |
788 | 375 | ||
376 | wl_list_remove(&output->layout_destroy.link); | ||
789 | wl_list_remove(&output->destroy.link); | 377 | wl_list_remove(&output->destroy.link); |
790 | wl_list_remove(&output->commit.link); | 378 | wl_list_remove(&output->commit.link); |
791 | wl_list_remove(&output->mode.link); | ||
792 | 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); | ||
793 | 382 | ||
383 | wlr_scene_output_destroy(output->scene_output); | ||
384 | output->scene_output = NULL; | ||
794 | output->wlr_output->data = NULL; | 385 | output->wlr_output->data = NULL; |
795 | output->wlr_output = NULL; | 386 | output->wlr_output = NULL; |
796 | 387 | ||
@@ -799,34 +390,14 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
799 | update_output_manager_config(server); | 390 | update_output_manager_config(server); |
800 | } | 391 | } |
801 | 392 | ||
802 | static void handle_mode(struct wl_listener *listener, void *data) { | 393 | static void handle_destroy(struct wl_listener *listener, void *data) { |
803 | struct sway_output *output = wl_container_of(listener, output, mode); | 394 | struct sway_output *output = wl_container_of(listener, output, destroy); |
804 | if (!output->enabled && !output->enabling) { | 395 | begin_destroy(output); |
805 | struct output_config *oc = find_output_config(output); | ||
806 | if (output->wlr_output->current_mode != NULL && | ||
807 | (!oc || oc->enabled)) { | ||
808 | // We want to enable this output, but it didn't work last time, | ||
809 | // possibly because we hadn't enough CRTCs. Try again now that the | ||
810 | // output has a mode. | ||
811 | sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, " | ||
812 | "trying to enable it", output->wlr_output->name); | ||
813 | apply_output_config(oc, output); | ||
814 | } | ||
815 | return; | ||
816 | } | ||
817 | if (!output->enabled) { | ||
818 | return; | ||
819 | } | ||
820 | arrange_layers(output); | ||
821 | arrange_output(output); | ||
822 | transaction_commit_dirty(); | ||
823 | |||
824 | update_output_manager_config(output->server); | ||
825 | } | 396 | } |
826 | 397 | ||
827 | static void update_textures(struct sway_container *con, void *data) { | 398 | static void handle_layout_destroy(struct wl_listener *listener, void *data) { |
828 | container_update_title_textures(con); | 399 | struct sway_output *output = wl_container_of(listener, output, layout_destroy); |
829 | container_update_marks_textures(con); | 400 | begin_destroy(output); |
830 | } | 401 | } |
831 | 402 | ||
832 | static void handle_commit(struct wl_listener *listener, void *data) { | 403 | static void handle_commit(struct wl_listener *listener, void *data) { |
@@ -837,17 +408,21 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
837 | return; | 408 | return; |
838 | } | 409 | } |
839 | 410 | ||
840 | if (event->committed & WLR_OUTPUT_STATE_SCALE) { | 411 | if (event->state->committed & ( |
841 | output_for_each_container(output, update_textures, NULL); | 412 | WLR_OUTPUT_STATE_MODE | |
842 | } | 413 | WLR_OUTPUT_STATE_TRANSFORM | |
843 | 414 | WLR_OUTPUT_STATE_SCALE)) { | |
844 | if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { | ||
845 | arrange_layers(output); | 415 | arrange_layers(output); |
846 | arrange_output(output); | 416 | arrange_output(output); |
847 | transaction_commit_dirty(); | 417 | transaction_commit_dirty(); |
848 | 418 | ||
849 | update_output_manager_config(output->server); | 419 | update_output_manager_config(output->server); |
850 | } | 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 | } | ||
851 | } | 426 | } |
852 | 427 | ||
853 | static void handle_present(struct wl_listener *listener, void *data) { | 428 | static void handle_present(struct wl_listener *listener, void *data) { |
@@ -862,6 +437,13 @@ static void handle_present(struct wl_listener *listener, void *data) { | |||
862 | output->refresh_nsec = output_event->refresh; | 437 | output->refresh_nsec = output_event->refresh; |
863 | } | 438 | } |
864 | 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 | |||
865 | static unsigned int last_headless_num = 0; | 447 | static unsigned int last_headless_num = 0; |
866 | 448 | ||
867 | void handle_new_output(struct wl_listener *listener, void *data) { | 449 | void handle_new_output(struct wl_listener *listener, void *data) { |
@@ -883,10 +465,14 @@ void handle_new_output(struct wl_listener *listener, void *data) { | |||
883 | 465 | ||
884 | if (wlr_output->non_desktop) { | 466 | if (wlr_output->non_desktop) { |
885 | sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); | 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 | ||
886 | if (server->drm_lease_manager) { | 470 | if (server->drm_lease_manager) { |
887 | wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, | 471 | wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, |
888 | wlr_output); | 472 | wlr_output); |
889 | } | 473 | } |
474 | #endif | ||
475 | list_add(root->non_desktop_outputs, non_desktop); | ||
890 | return; | 476 | return; |
891 | } | 477 | } |
892 | 478 | ||
@@ -896,32 +482,46 @@ void handle_new_output(struct wl_listener *listener, void *data) { | |||
896 | return; | 482 | return; |
897 | } | 483 | } |
898 | 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 | } | ||
493 | |||
899 | struct sway_output *output = output_create(wlr_output); | 494 | struct sway_output *output = output_create(wlr_output); |
900 | if (!output) { | 495 | if (!output) { |
496 | sway_log(SWAY_ERROR, "Failed to create a sway output"); | ||
497 | wlr_scene_output_destroy(scene_output); | ||
901 | return; | 498 | return; |
902 | } | 499 | } |
500 | |||
903 | output->server = server; | 501 | output->server = server; |
904 | output->damage = wlr_output_damage_create(wlr_output); | 502 | output->scene_output = scene_output; |
905 | 503 | ||
504 | wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); | ||
505 | output->layout_destroy.notify = handle_layout_destroy; | ||
906 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | 506 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); |
907 | output->destroy.notify = handle_destroy; | 507 | output->destroy.notify = handle_destroy; |
908 | wl_signal_add(&wlr_output->events.commit, &output->commit); | 508 | wl_signal_add(&wlr_output->events.commit, &output->commit); |
909 | output->commit.notify = handle_commit; | 509 | output->commit.notify = handle_commit; |
910 | wl_signal_add(&wlr_output->events.mode, &output->mode); | ||
911 | output->mode.notify = handle_mode; | ||
912 | wl_signal_add(&wlr_output->events.present, &output->present); | 510 | wl_signal_add(&wlr_output->events.present, &output->present); |
913 | output->present.notify = handle_present; | 511 | output->present.notify = handle_present; |
914 | wl_signal_add(&output->damage->events.frame, &output->damage_frame); | 512 | wl_signal_add(&wlr_output->events.frame, &output->frame); |
915 | output->damage_frame.notify = damage_handle_frame; | 513 | output->frame.notify = handle_frame; |
916 | wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); | 514 | wl_signal_add(&wlr_output->events.request_state, &output->request_state); |
917 | output->damage_destroy.notify = damage_handle_destroy; | 515 | output->request_state.notify = handle_request_state; |
918 | 516 | ||
919 | 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, |
920 | output_repaint_timer_handler, output); | 518 | output_repaint_timer_handler, output); |
921 | 519 | ||
922 | struct output_config *oc = find_output_config(output); | 520 | if (server->session_lock.lock) { |
923 | apply_output_config(oc, output); | 521 | sway_session_lock_add_output(server->session_lock.lock, output); |
924 | free_output_config(oc); | 522 | } |
523 | |||
524 | apply_all_output_configs(); | ||
925 | 525 | ||
926 | transaction_commit_dirty(); | 526 | transaction_commit_dirty(); |
927 | 527 | ||
@@ -935,62 +535,103 @@ void handle_output_layout_change(struct wl_listener *listener, | |||
935 | update_output_manager_config(server); | 535 | update_output_manager_config(server); |
936 | } | 536 | } |
937 | 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 | |||
938 | static void output_manager_apply(struct sway_server *server, | 581 | static void output_manager_apply(struct sway_server *server, |
939 | struct wlr_output_configuration_v1 *config, bool test_only) { | 582 | struct wlr_output_configuration_v1 *config, bool test_only) { |
940 | // TODO: perform atomic tests on the whole backend atomically | 583 | size_t configs_len = wl_list_length(&root->all_outputs); |
941 | 584 | struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); | |
942 | struct wlr_output_configuration_head_v1 *config_head; | 585 | if (!configs) { |
943 | // First disable outputs we need to disable | 586 | return; |
944 | bool ok = true; | 587 | } |
945 | wl_list_for_each(config_head, &config->heads, link) { | 588 | |
946 | struct wlr_output *wlr_output = config_head->state.output; | 589 | int config_idx = 0; |
947 | struct sway_output *output = wlr_output->data; | 590 | struct sway_output *sway_output; |
948 | 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--; | ||
949 | continue; | 594 | continue; |
950 | } | 595 | } |
951 | struct output_config *oc = new_output_config(output->wlr_output->name); | ||
952 | oc->enabled = false; | ||
953 | 596 | ||
954 | if (test_only) { | 597 | struct matched_output_config *cfg = &configs[config_idx++]; |
955 | ok &= test_output_config(oc, output); | 598 | cfg->output = sway_output; |
956 | } else { | ||
957 | oc = store_output_config(oc); | ||
958 | ok &= apply_output_config(oc, output); | ||
959 | } | ||
960 | } | ||
961 | 599 | ||
962 | // Then enable outputs that need to | 600 | struct wlr_output_configuration_head_v1 *config_head; |
963 | wl_list_for_each(config_head, &config->heads, link) { | 601 | wl_list_for_each(config_head, &config->heads, link) { |
964 | struct wlr_output *wlr_output = config_head->state.output; | 602 | if (config_head->state.output == sway_output->wlr_output) { |
965 | struct sway_output *output = wlr_output->data; | 603 | cfg->config = output_config_for_config_head(config_head, sway_output); |
966 | if (!config_head->state.enabled) { | 604 | break; |
967 | continue; | 605 | } |
968 | } | 606 | } |
969 | struct output_config *oc = new_output_config(output->wlr_output->name); | 607 | if (!cfg->config) { |
970 | oc->enabled = true; | 608 | cfg->config = find_output_config(sway_output); |
971 | if (config_head->state.mode != NULL) { | ||
972 | struct wlr_output_mode *mode = config_head->state.mode; | ||
973 | oc->width = mode->width; | ||
974 | oc->height = mode->height; | ||
975 | oc->refresh_rate = mode->refresh / 1000.f; | ||
976 | } else { | ||
977 | oc->width = config_head->state.custom_mode.width; | ||
978 | oc->height = config_head->state.custom_mode.height; | ||
979 | oc->refresh_rate = | ||
980 | config_head->state.custom_mode.refresh / 1000.f; | ||
981 | } | 609 | } |
982 | oc->x = config_head->state.x; | 610 | } |
983 | oc->y = config_head->state.y; | 611 | |
984 | oc->transform = config_head->state.transform; | 612 | bool ok = apply_output_configs(configs, configs_len, test_only); |
985 | oc->scale = config_head->state.scale; | 613 | for (size_t idx = 0; idx < configs_len; idx++) { |
614 | struct matched_output_config *cfg = &configs[idx]; | ||
986 | 615 | ||
987 | if (test_only) { | 616 | // Only store new configs for successful non-test commits. Old configs, |
988 | 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 == sway_output->wlr_output) { | ||
623 | store_config = true; | ||
624 | break; | ||
625 | } | ||
626 | } | ||
627 | } | ||
628 | if (store_config) { | ||
629 | store_output_config(cfg->config); | ||
989 | } else { | 630 | } else { |
990 | oc = store_output_config(oc); | 631 | free_output_config(cfg->config); |
991 | ok &= apply_output_config(oc, output); | ||
992 | } | 632 | } |
993 | } | 633 | } |
634 | free(configs); | ||
994 | 635 | ||
995 | if (ok) { | 636 | if (ok) { |
996 | wlr_output_configuration_v1_send_succeeded(config); | 637 | wlr_output_configuration_v1_send_succeeded(config); |
@@ -1034,6 +675,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, | |||
1034 | oc->power = 1; | 675 | oc->power = 1; |
1035 | break; | 676 | break; |
1036 | } | 677 | } |
1037 | oc = store_output_config(oc); | 678 | store_output_config(oc); |
1038 | apply_output_config(oc, output); | 679 | apply_all_output_configs(); |
1039 | } | 680 | } |
diff --git a/sway/desktop/render.c b/sway/desktop/render.c deleted file mode 100644 index ed9ad490..00000000 --- a/sway/desktop/render.c +++ /dev/null | |||
@@ -1,1204 +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_compositor.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_output->renderer; | ||
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 = wlr_output->renderer; | ||
104 | struct sway_output *output = wlr_output->data; | ||
105 | |||
106 | pixman_region32_t damage; | ||
107 | pixman_region32_init(&damage); | ||
108 | pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, | ||
109 | dst_box->width, dst_box->height); | ||
110 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
111 | bool damaged = pixman_region32_not_empty(&damage); | ||
112 | if (!damaged) { | ||
113 | goto damage_finish; | ||
114 | } | ||
115 | |||
116 | int nrects; | ||
117 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
118 | for (int i = 0; i < nrects; ++i) { | ||
119 | scissor_output(wlr_output, &rects[i]); | ||
120 | set_scale_filter(wlr_output, texture, output->scale_filter); | ||
121 | if (src_box != NULL) { | ||
122 | wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha); | ||
123 | } else { | ||
124 | wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | damage_finish: | ||
129 | pixman_region32_fini(&damage); | ||
130 | } | ||
131 | |||
132 | static void render_surface_iterator(struct sway_output *output, | ||
133 | struct sway_view *view, struct wlr_surface *surface, | ||
134 | struct wlr_box *_box, void *_data) { | ||
135 | struct render_data *data = _data; | ||
136 | struct wlr_output *wlr_output = output->wlr_output; | ||
137 | pixman_region32_t *output_damage = data->damage; | ||
138 | float alpha = data->alpha; | ||
139 | |||
140 | struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
141 | if (!texture) { | ||
142 | return; | ||
143 | } | ||
144 | |||
145 | struct wlr_fbox src_box; | ||
146 | wlr_surface_get_buffer_source_box(surface, &src_box); | ||
147 | |||
148 | struct wlr_box proj_box = *_box; | ||
149 | scale_box(&proj_box, wlr_output->scale); | ||
150 | |||
151 | float matrix[9]; | ||
152 | enum wl_output_transform transform = | ||
153 | wlr_output_transform_invert(surface->current.transform); | ||
154 | wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, | ||
155 | wlr_output->transform_matrix); | ||
156 | |||
157 | struct wlr_box dst_box = *_box; | ||
158 | struct wlr_box *clip_box = data->clip_box; | ||
159 | if (clip_box != NULL) { | ||
160 | dst_box.width = fmin(dst_box.width, clip_box->width); | ||
161 | dst_box.height = fmin(dst_box.height, clip_box->height); | ||
162 | } | ||
163 | scale_box(&dst_box, wlr_output->scale); | ||
164 | |||
165 | render_texture(wlr_output, output_damage, texture, | ||
166 | &src_box, &dst_box, matrix, alpha); | ||
167 | |||
168 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
169 | wlr_output); | ||
170 | } | ||
171 | |||
172 | static void render_layer_toplevel(struct sway_output *output, | ||
173 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
174 | struct render_data data = { | ||
175 | .damage = damage, | ||
176 | .alpha = 1.0f, | ||
177 | }; | ||
178 | output_layer_for_each_toplevel_surface(output, layer_surfaces, | ||
179 | render_surface_iterator, &data); | ||
180 | } | ||
181 | |||
182 | static void render_layer_popups(struct sway_output *output, | ||
183 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
184 | struct render_data data = { | ||
185 | .damage = damage, | ||
186 | .alpha = 1.0f, | ||
187 | }; | ||
188 | output_layer_for_each_popup_surface(output, layer_surfaces, | ||
189 | render_surface_iterator, &data); | ||
190 | } | ||
191 | |||
192 | #if HAVE_XWAYLAND | ||
193 | static void render_unmanaged(struct sway_output *output, | ||
194 | pixman_region32_t *damage, struct wl_list *unmanaged) { | ||
195 | struct render_data data = { | ||
196 | .damage = damage, | ||
197 | .alpha = 1.0f, | ||
198 | }; | ||
199 | output_unmanaged_for_each_surface(output, unmanaged, | ||
200 | render_surface_iterator, &data); | ||
201 | } | ||
202 | #endif | ||
203 | |||
204 | static void render_drag_icons(struct sway_output *output, | ||
205 | pixman_region32_t *damage, struct wl_list *drag_icons) { | ||
206 | struct render_data data = { | ||
207 | .damage = damage, | ||
208 | .alpha = 1.0f, | ||
209 | }; | ||
210 | output_drag_icons_for_each_surface(output, drag_icons, | ||
211 | render_surface_iterator, &data); | ||
212 | } | ||
213 | |||
214 | // _box.x and .y are expected to be layout-local | ||
215 | // _box.width and .height are expected to be output-buffer-local | ||
216 | void render_rect(struct sway_output *output, | ||
217 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
218 | float color[static 4]) { | ||
219 | struct wlr_output *wlr_output = output->wlr_output; | ||
220 | struct wlr_renderer *renderer = wlr_output->renderer; | ||
221 | |||
222 | struct wlr_box box; | ||
223 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
224 | box.x -= output->lx * wlr_output->scale; | ||
225 | box.y -= output->ly * wlr_output->scale; | ||
226 | |||
227 | pixman_region32_t damage; | ||
228 | pixman_region32_init(&damage); | ||
229 | pixman_region32_union_rect(&damage, &damage, box.x, box.y, | ||
230 | box.width, box.height); | ||
231 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
232 | bool damaged = pixman_region32_not_empty(&damage); | ||
233 | if (!damaged) { | ||
234 | goto damage_finish; | ||
235 | } | ||
236 | |||
237 | int nrects; | ||
238 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
239 | for (int i = 0; i < nrects; ++i) { | ||
240 | scissor_output(wlr_output, &rects[i]); | ||
241 | wlr_render_rect(renderer, &box, color, | ||
242 | wlr_output->transform_matrix); | ||
243 | } | ||
244 | |||
245 | damage_finish: | ||
246 | pixman_region32_fini(&damage); | ||
247 | } | ||
248 | |||
249 | void premultiply_alpha(float color[4], float opacity) { | ||
250 | color[3] *= opacity; | ||
251 | color[0] *= color[3]; | ||
252 | color[1] *= color[3]; | ||
253 | color[2] *= color[3]; | ||
254 | } | ||
255 | |||
256 | static void render_view_toplevels(struct sway_view *view, | ||
257 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
258 | struct render_data data = { | ||
259 | .damage = damage, | ||
260 | .alpha = alpha, | ||
261 | }; | ||
262 | struct wlr_box clip_box; | ||
263 | if (!container_is_current_floating(view->container)) { | ||
264 | // As we pass the geometry offsets to the surface iterator, we will | ||
265 | // need to account for the offsets in the clip dimensions. | ||
266 | clip_box.width = view->container->current.content_width + view->geometry.x; | ||
267 | clip_box.height = view->container->current.content_height + view->geometry.y; | ||
268 | data.clip_box = &clip_box; | ||
269 | } | ||
270 | // Render all toplevels without descending into popups | ||
271 | double ox = view->container->surface_x - | ||
272 | output->lx - view->geometry.x; | ||
273 | double oy = view->container->surface_y - | ||
274 | output->ly - view->geometry.y; | ||
275 | output_surface_for_each_surface(output, view->surface, ox, oy, | ||
276 | render_surface_iterator, &data); | ||
277 | } | ||
278 | |||
279 | static void render_view_popups(struct sway_view *view, | ||
280 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
281 | struct render_data data = { | ||
282 | .damage = damage, | ||
283 | .alpha = alpha, | ||
284 | }; | ||
285 | output_view_for_each_popup_surface(output, view, | ||
286 | render_surface_iterator, &data); | ||
287 | } | ||
288 | |||
289 | static void render_saved_view(struct sway_view *view, | ||
290 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
291 | struct wlr_output *wlr_output = output->wlr_output; | ||
292 | |||
293 | if (wl_list_empty(&view->saved_buffers)) { | ||
294 | return; | ||
295 | } | ||
296 | |||
297 | bool floating = container_is_current_floating(view->container); | ||
298 | |||
299 | struct sway_saved_buffer *saved_buf; | ||
300 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
301 | if (!saved_buf->buffer->texture) { | ||
302 | continue; | ||
303 | } | ||
304 | |||
305 | struct wlr_box proj_box = { | ||
306 | .x = saved_buf->x - view->saved_geometry.x - output->lx, | ||
307 | .y = saved_buf->y - view->saved_geometry.y - output->ly, | ||
308 | .width = saved_buf->width, | ||
309 | .height = saved_buf->height, | ||
310 | }; | ||
311 | |||
312 | struct wlr_box output_box = { | ||
313 | .width = output->width, | ||
314 | .height = output->height, | ||
315 | }; | ||
316 | |||
317 | struct wlr_box intersection; | ||
318 | bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box); | ||
319 | if (!intersects) { | ||
320 | continue; | ||
321 | } | ||
322 | |||
323 | struct wlr_box dst_box = proj_box; | ||
324 | scale_box(&proj_box, wlr_output->scale); | ||
325 | |||
326 | float matrix[9]; | ||
327 | enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); | ||
328 | wlr_matrix_project_box(matrix, &proj_box, transform, 0, | ||
329 | wlr_output->transform_matrix); | ||
330 | |||
331 | if (!floating) { | ||
332 | dst_box.width = fmin(dst_box.width, | ||
333 | view->container->current.content_width - | ||
334 | (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x); | ||
335 | dst_box.height = fmin(dst_box.height, | ||
336 | view->container->current.content_height - | ||
337 | (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y); | ||
338 | } | ||
339 | scale_box(&dst_box, wlr_output->scale); | ||
340 | |||
341 | render_texture(wlr_output, damage, saved_buf->buffer->texture, | ||
342 | &saved_buf->source_box, &dst_box, matrix, alpha); | ||
343 | } | ||
344 | |||
345 | // FIXME: we should set the surface that this saved buffer originates from | ||
346 | // as sampled here. | ||
347 | // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * Render a view's surface and left/bottom/right borders. | ||
352 | */ | ||
353 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | ||
354 | struct sway_container *con, struct border_colors *colors) { | ||
355 | struct sway_view *view = con->view; | ||
356 | if (!wl_list_empty(&view->saved_buffers)) { | ||
357 | render_saved_view(view, output, damage, view->container->alpha); | ||
358 | } else if (view->surface) { | ||
359 | render_view_toplevels(view, output, damage, view->container->alpha); | ||
360 | } | ||
361 | |||
362 | if (con->current.border == B_NONE || con->current.border == B_CSD) { | ||
363 | return; | ||
364 | } | ||
365 | |||
366 | struct wlr_box box; | ||
367 | float output_scale = output->wlr_output->scale; | ||
368 | float color[4]; | ||
369 | struct sway_container_state *state = &con->current; | ||
370 | |||
371 | if (state->border_left) { | ||
372 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
373 | premultiply_alpha(color, con->alpha); | ||
374 | box.x = floor(state->x); | ||
375 | box.y = floor(state->content_y); | ||
376 | box.width = state->border_thickness; | ||
377 | box.height = state->content_height; | ||
378 | scale_box(&box, output_scale); | ||
379 | render_rect(output, damage, &box, color); | ||
380 | } | ||
381 | |||
382 | list_t *siblings = container_get_current_siblings(con); | ||
383 | enum sway_container_layout layout = | ||
384 | container_current_parent_layout(con); | ||
385 | |||
386 | if (state->border_right) { | ||
387 | if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) { | ||
388 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
389 | } else { | ||
390 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
391 | } | ||
392 | premultiply_alpha(color, con->alpha); | ||
393 | box.x = floor(state->content_x + state->content_width); | ||
394 | box.y = floor(state->content_y); | ||
395 | box.width = state->border_thickness; | ||
396 | box.height = state->content_height; | ||
397 | scale_box(&box, output_scale); | ||
398 | render_rect(output, damage, &box, color); | ||
399 | } | ||
400 | |||
401 | if (state->border_bottom) { | ||
402 | if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) { | ||
403 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
404 | } else { | ||
405 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
406 | } | ||
407 | premultiply_alpha(color, con->alpha); | ||
408 | box.x = floor(state->x); | ||
409 | box.y = floor(state->content_y + state->content_height); | ||
410 | box.width = state->width; | ||
411 | box.height = state->border_thickness; | ||
412 | scale_box(&box, output_scale); | ||
413 | render_rect(output, damage, &box, color); | ||
414 | } | ||
415 | } | ||
416 | |||
417 | /** | ||
418 | * Render a titlebar. | ||
419 | * | ||
420 | * Care must be taken not to render over the same pixel multiple times, | ||
421 | * otherwise the colors will be incorrect when using opacity. | ||
422 | * | ||
423 | * The height is: 1px border, 3px padding, font height, 3px padding, 1px border | ||
424 | * The left side is: 1px border, 2px padding, title | ||
425 | */ | ||
426 | static void render_titlebar(struct sway_output *output, | ||
427 | pixman_region32_t *output_damage, struct sway_container *con, | ||
428 | int x, int y, int width, | ||
429 | struct border_colors *colors, struct wlr_texture *title_texture, | ||
430 | struct wlr_texture *marks_texture) { | ||
431 | struct wlr_box box; | ||
432 | float color[4]; | ||
433 | float output_scale = output->wlr_output->scale; | ||
434 | double output_x = output->lx; | ||
435 | double output_y = output->ly; | ||
436 | int titlebar_border_thickness = config->titlebar_border_thickness; | ||
437 | int titlebar_h_padding = config->titlebar_h_padding; | ||
438 | int titlebar_v_padding = config->titlebar_v_padding; | ||
439 | enum alignment title_align = config->title_align; | ||
440 | |||
441 | // Single pixel bar above title | ||
442 | memcpy(&color, colors->border, sizeof(float) * 4); | ||
443 | premultiply_alpha(color, con->alpha); | ||
444 | box.x = x; | ||
445 | box.y = y; | ||
446 | box.width = width; | ||
447 | box.height = titlebar_border_thickness; | ||
448 | scale_box(&box, output_scale); | ||
449 | render_rect(output, output_damage, &box, color); | ||
450 | |||
451 | // Single pixel bar below title | ||
452 | box.x = x; | ||
453 | box.y = y + container_titlebar_height() - titlebar_border_thickness; | ||
454 | box.width = width; | ||
455 | box.height = titlebar_border_thickness; | ||
456 | scale_box(&box, output_scale); | ||
457 | render_rect(output, output_damage, &box, color); | ||
458 | |||
459 | // Single pixel left edge | ||
460 | box.x = x; | ||
461 | box.y = y + titlebar_border_thickness; | ||
462 | box.width = titlebar_border_thickness; | ||
463 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
464 | scale_box(&box, output_scale); | ||
465 | render_rect(output, output_damage, &box, color); | ||
466 | |||
467 | // Single pixel right edge | ||
468 | box.x = x + width - titlebar_border_thickness; | ||
469 | box.y = y + titlebar_border_thickness; | ||
470 | box.width = titlebar_border_thickness; | ||
471 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
472 | scale_box(&box, output_scale); | ||
473 | render_rect(output, output_damage, &box, color); | ||
474 | |||
475 | int inner_x = x - output_x + titlebar_h_padding; | ||
476 | int bg_y = y + titlebar_border_thickness; | ||
477 | size_t inner_width = width - titlebar_h_padding * 2; | ||
478 | |||
479 | // output-buffer local | ||
480 | int ob_inner_x = round(inner_x * output_scale); | ||
481 | int ob_inner_width = scale_length(inner_width, inner_x, output_scale); | ||
482 | int ob_bg_height = scale_length( | ||
483 | (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
484 | config->font_height, bg_y, output_scale); | ||
485 | |||
486 | // Marks | ||
487 | int ob_marks_x = 0; // output-buffer-local | ||
488 | int ob_marks_width = 0; // output-buffer-local | ||
489 | if (config->show_marks && marks_texture) { | ||
490 | struct wlr_box texture_box = { | ||
491 | .width = marks_texture->width, | ||
492 | .height = marks_texture->height, | ||
493 | }; | ||
494 | ob_marks_width = texture_box.width; | ||
495 | |||
496 | // The marks texture might be shorter than the config->font_height, in | ||
497 | // which case we need to pad it as evenly as possible above and below. | ||
498 | int ob_padding_total = ob_bg_height - texture_box.height; | ||
499 | int ob_padding_above = floor(ob_padding_total / 2.0); | ||
500 | int ob_padding_below = ceil(ob_padding_total / 2.0); | ||
501 | |||
502 | // Render texture. If the title is on the right, the marks will be on | ||
503 | // the left. Otherwise, they will be on the right. | ||
504 | if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) { | ||
505 | texture_box.x = ob_inner_x; | ||
506 | } else { | ||
507 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
508 | } | ||
509 | ob_marks_x = texture_box.x; | ||
510 | |||
511 | texture_box.y = round((bg_y - output_y) * output_scale) + | ||
512 | ob_padding_above; | ||
513 | |||
514 | float matrix[9]; | ||
515 | wlr_matrix_project_box(matrix, &texture_box, | ||
516 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
517 | 0.0, output->wlr_output->transform_matrix); | ||
518 | |||
519 | if (ob_inner_width < texture_box.width) { | ||
520 | texture_box.width = ob_inner_width; | ||
521 | } | ||
522 | render_texture(output->wlr_output, output_damage, marks_texture, | ||
523 | NULL, &texture_box, matrix, con->alpha); | ||
524 | |||
525 | // Padding above | ||
526 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
527 | premultiply_alpha(color, con->alpha); | ||
528 | box.x = texture_box.x + round(output_x * output_scale); | ||
529 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
530 | box.width = texture_box.width; | ||
531 | box.height = ob_padding_above; | ||
532 | render_rect(output, output_damage, &box, color); | ||
533 | |||
534 | // Padding below | ||
535 | box.y += ob_padding_above + texture_box.height; | ||
536 | box.height = ob_padding_below; | ||
537 | render_rect(output, output_damage, &box, color); | ||
538 | } | ||
539 | |||
540 | // Title text | ||
541 | int ob_title_x = 0; // output-buffer-local | ||
542 | int ob_title_width = 0; // output-buffer-local | ||
543 | if (title_texture) { | ||
544 | struct wlr_box texture_box = { | ||
545 | .width = title_texture->width, | ||
546 | .height = title_texture->height, | ||
547 | }; | ||
548 | |||
549 | // The effective output may be NULL when con is not on any output. | ||
550 | // This can happen because we render all children of containers, | ||
551 | // even those that are out of the bounds of any output. | ||
552 | struct sway_output *effective = container_get_effective_output(con); | ||
553 | float title_scale = effective ? effective->wlr_output->scale : output_scale; | ||
554 | texture_box.width = texture_box.width * output_scale / title_scale; | ||
555 | texture_box.height = texture_box.height * output_scale / title_scale; | ||
556 | ob_title_width = texture_box.width; | ||
557 | |||
558 | // The title texture might be shorter than the config->font_height, | ||
559 | // in which case we need to pad it above and below. | ||
560 | int ob_padding_above = round((titlebar_v_padding - | ||
561 | titlebar_border_thickness) * output_scale); | ||
562 | int ob_padding_below = ob_bg_height - ob_padding_above - | ||
563 | texture_box.height; | ||
564 | |||
565 | // Render texture | ||
566 | if (texture_box.width > ob_inner_width - ob_marks_width) { | ||
567 | texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width) | ||
568 | ? ob_marks_x + ob_marks_width : ob_inner_x; | ||
569 | } else if (title_align == ALIGN_LEFT) { | ||
570 | texture_box.x = ob_inner_x; | ||
571 | } else if (title_align == ALIGN_CENTER) { | ||
572 | // If there are marks visible, center between the edge and marks. | ||
573 | // Otherwise, center in the inner area. | ||
574 | if (ob_marks_width) { | ||
575 | texture_box.x = (ob_inner_x + ob_marks_x) / 2 | ||
576 | - texture_box.width / 2; | ||
577 | } else { | ||
578 | texture_box.x = ob_inner_x + ob_inner_width / 2 | ||
579 | - texture_box.width / 2; | ||
580 | } | ||
581 | } else { | ||
582 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
583 | } | ||
584 | ob_title_x = texture_box.x; | ||
585 | |||
586 | texture_box.y = | ||
587 | round((bg_y - output_y) * output_scale) + ob_padding_above; | ||
588 | |||
589 | float matrix[9]; | ||
590 | wlr_matrix_project_box(matrix, &texture_box, | ||
591 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
592 | 0.0, output->wlr_output->transform_matrix); | ||
593 | |||
594 | if (ob_inner_width - ob_marks_width < texture_box.width) { | ||
595 | texture_box.width = ob_inner_width - ob_marks_width; | ||
596 | } | ||
597 | |||
598 | render_texture(output->wlr_output, output_damage, title_texture, | ||
599 | NULL, &texture_box, matrix, con->alpha); | ||
600 | |||
601 | // Padding above | ||
602 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
603 | premultiply_alpha(color, con->alpha); | ||
604 | box.x = texture_box.x + round(output_x * output_scale); | ||
605 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
606 | box.width = texture_box.width; | ||
607 | box.height = ob_padding_above; | ||
608 | render_rect(output, output_damage, &box, color); | ||
609 | |||
610 | // Padding below | ||
611 | box.y += ob_padding_above + texture_box.height; | ||
612 | box.height = ob_padding_below; | ||
613 | render_rect(output, output_damage, &box, color); | ||
614 | } | ||
615 | |||
616 | // Determine the left + right extends of the textures (output-buffer local) | ||
617 | int ob_left_x, ob_left_width, ob_right_x, ob_right_width; | ||
618 | if (ob_title_width == 0 && ob_marks_width == 0) { | ||
619 | ob_left_x = ob_inner_x; | ||
620 | ob_left_width = 0; | ||
621 | ob_right_x = ob_inner_x; | ||
622 | ob_right_width = 0; | ||
623 | } else if (ob_title_x < ob_marks_x) { | ||
624 | ob_left_x = ob_title_x; | ||
625 | ob_left_width = ob_title_width; | ||
626 | ob_right_x = ob_marks_x; | ||
627 | ob_right_width = ob_marks_width; | ||
628 | } else { | ||
629 | ob_left_x = ob_marks_x; | ||
630 | ob_left_width = ob_marks_width; | ||
631 | ob_right_x = ob_title_x; | ||
632 | ob_right_width = ob_title_width; | ||
633 | } | ||
634 | if (ob_left_x < ob_inner_x) { | ||
635 | ob_left_x = ob_inner_x; | ||
636 | } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) { | ||
637 | ob_right_x = ob_left_x; | ||
638 | ob_right_width = ob_left_width; | ||
639 | } | ||
640 | |||
641 | // Filler between title and marks | ||
642 | box.width = ob_right_x - ob_left_x - ob_left_width; | ||
643 | if (box.width > 0) { | ||
644 | box.x = ob_left_x + ob_left_width + round(output_x * output_scale); | ||
645 | box.y = round(bg_y * output_scale); | ||
646 | box.height = ob_bg_height; | ||
647 | render_rect(output, output_damage, &box, color); | ||
648 | } | ||
649 | |||
650 | // Padding on left side | ||
651 | box.x = x + titlebar_border_thickness; | ||
652 | box.y = y + titlebar_border_thickness; | ||
653 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
654 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
655 | config->font_height; | ||
656 | scale_box(&box, output_scale); | ||
657 | int left_x = ob_left_x + round(output_x * output_scale); | ||
658 | if (box.x + box.width < left_x) { | ||
659 | box.width += left_x - box.x - box.width; | ||
660 | } | ||
661 | render_rect(output, output_damage, &box, color); | ||
662 | |||
663 | // Padding on right side | ||
664 | box.x = x + width - titlebar_h_padding; | ||
665 | box.y = y + titlebar_border_thickness; | ||
666 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
667 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
668 | config->font_height; | ||
669 | scale_box(&box, output_scale); | ||
670 | int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale); | ||
671 | if (right_rx < box.x) { | ||
672 | box.width += box.x - right_rx; | ||
673 | box.x = right_rx; | ||
674 | } | ||
675 | render_rect(output, output_damage, &box, color); | ||
676 | } | ||
677 | |||
678 | /** | ||
679 | * Render the top border line for a view using "border pixel". | ||
680 | */ | ||
681 | static void render_top_border(struct sway_output *output, | ||
682 | pixman_region32_t *output_damage, struct sway_container *con, | ||
683 | struct border_colors *colors) { | ||
684 | struct sway_container_state *state = &con->current; | ||
685 | if (!state->border_top) { | ||
686 | return; | ||
687 | } | ||
688 | struct wlr_box box; | ||
689 | float color[4]; | ||
690 | float output_scale = output->wlr_output->scale; | ||
691 | |||
692 | // Child border - top edge | ||
693 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
694 | premultiply_alpha(color, con->alpha); | ||
695 | box.x = floor(state->x); | ||
696 | box.y = floor(state->y); | ||
697 | box.width = state->width; | ||
698 | box.height = state->border_thickness; | ||
699 | scale_box(&box, output_scale); | ||
700 | render_rect(output, output_damage, &box, color); | ||
701 | } | ||
702 | |||
703 | struct parent_data { | ||
704 | enum sway_container_layout layout; | ||
705 | struct wlr_box box; | ||
706 | list_t *children; | ||
707 | bool focused; | ||
708 | struct sway_container *active_child; | ||
709 | }; | ||
710 | |||
711 | static void render_container(struct sway_output *output, | ||
712 | pixman_region32_t *damage, struct sway_container *con, bool parent_focused); | ||
713 | |||
714 | /** | ||
715 | * Render a container's children using a L_HORIZ or L_VERT layout. | ||
716 | * | ||
717 | * Wrap child views in borders and leave child containers borderless because | ||
718 | * they'll apply their own borders to their children. | ||
719 | */ | ||
720 | static void render_containers_linear(struct sway_output *output, | ||
721 | pixman_region32_t *damage, struct parent_data *parent) { | ||
722 | for (int i = 0; i < parent->children->length; ++i) { | ||
723 | struct sway_container *child = parent->children->items[i]; | ||
724 | |||
725 | if (child->view) { | ||
726 | struct sway_view *view = child->view; | ||
727 | struct border_colors *colors; | ||
728 | struct wlr_texture *title_texture; | ||
729 | struct wlr_texture *marks_texture; | ||
730 | struct sway_container_state *state = &child->current; | ||
731 | |||
732 | if (view_is_urgent(view)) { | ||
733 | colors = &config->border_colors.urgent; | ||
734 | title_texture = child->title_urgent; | ||
735 | marks_texture = child->marks_urgent; | ||
736 | } else if (state->focused || parent->focused) { | ||
737 | colors = &config->border_colors.focused; | ||
738 | title_texture = child->title_focused; | ||
739 | marks_texture = child->marks_focused; | ||
740 | } else if (child == parent->active_child) { | ||
741 | colors = &config->border_colors.focused_inactive; | ||
742 | title_texture = child->title_focused_inactive; | ||
743 | marks_texture = child->marks_focused_inactive; | ||
744 | } else { | ||
745 | colors = &config->border_colors.unfocused; | ||
746 | title_texture = child->title_unfocused; | ||
747 | marks_texture = child->marks_unfocused; | ||
748 | } | ||
749 | |||
750 | if (state->border == B_NORMAL) { | ||
751 | render_titlebar(output, damage, child, floor(state->x), | ||
752 | floor(state->y), state->width, colors, | ||
753 | title_texture, marks_texture); | ||
754 | } else if (state->border == B_PIXEL) { | ||
755 | render_top_border(output, damage, child, colors); | ||
756 | } | ||
757 | render_view(output, damage, child, colors); | ||
758 | } else { | ||
759 | render_container(output, damage, child, | ||
760 | parent->focused || child->current.focused); | ||
761 | } | ||
762 | } | ||
763 | } | ||
764 | |||
765 | static bool container_is_focused(struct sway_container *con, void *data) { | ||
766 | return con->current.focused; | ||
767 | } | ||
768 | |||
769 | static bool container_has_focused_child(struct sway_container *con) { | ||
770 | return container_find_child(con, container_is_focused, NULL); | ||
771 | } | ||
772 | |||
773 | /** | ||
774 | * Render a container's children using the L_TABBED layout. | ||
775 | */ | ||
776 | static void render_containers_tabbed(struct sway_output *output, | ||
777 | pixman_region32_t *damage, struct parent_data *parent) { | ||
778 | if (!parent->children->length) { | ||
779 | return; | ||
780 | } | ||
781 | struct sway_container *current = parent->active_child; | ||
782 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
783 | int tab_width = parent->box.width / parent->children->length; | ||
784 | |||
785 | // Render tabs | ||
786 | for (int i = 0; i < parent->children->length; ++i) { | ||
787 | struct sway_container *child = parent->children->items[i]; | ||
788 | struct sway_view *view = child->view; | ||
789 | struct sway_container_state *cstate = &child->current; | ||
790 | struct border_colors *colors; | ||
791 | struct wlr_texture *title_texture; | ||
792 | struct wlr_texture *marks_texture; | ||
793 | bool urgent = view ? | ||
794 | view_is_urgent(view) : container_has_urgent_child(child); | ||
795 | |||
796 | if (urgent) { | ||
797 | colors = &config->border_colors.urgent; | ||
798 | title_texture = child->title_urgent; | ||
799 | marks_texture = child->marks_urgent; | ||
800 | } else if (cstate->focused || parent->focused) { | ||
801 | colors = &config->border_colors.focused; | ||
802 | title_texture = child->title_focused; | ||
803 | marks_texture = child->marks_focused; | ||
804 | } else if (config->has_focused_tab_title && container_has_focused_child(child)) { | ||
805 | colors = &config->border_colors.focused_tab_title; | ||
806 | title_texture = child->title_focused_tab_title; | ||
807 | marks_texture = child->marks_focused_tab_title; | ||
808 | } else if (child == parent->active_child) { | ||
809 | colors = &config->border_colors.focused_inactive; | ||
810 | title_texture = child->title_focused_inactive; | ||
811 | marks_texture = child->marks_focused_inactive; | ||
812 | } else { | ||
813 | colors = &config->border_colors.unfocused; | ||
814 | title_texture = child->title_unfocused; | ||
815 | marks_texture = child->marks_unfocused; | ||
816 | } | ||
817 | |||
818 | int x = floor(cstate->x + tab_width * i); | ||
819 | |||
820 | // Make last tab use the remaining width of the parent | ||
821 | if (i == parent->children->length - 1) { | ||
822 | tab_width = parent->box.width - tab_width * i; | ||
823 | } | ||
824 | |||
825 | render_titlebar(output, damage, child, x, parent->box.y, tab_width, | ||
826 | colors, title_texture, marks_texture); | ||
827 | |||
828 | if (child == current) { | ||
829 | current_colors = colors; | ||
830 | } | ||
831 | } | ||
832 | |||
833 | // Render surface and left/right/bottom borders | ||
834 | if (current->view) { | ||
835 | render_view(output, damage, current, current_colors); | ||
836 | } else { | ||
837 | render_container(output, damage, current, | ||
838 | parent->focused || current->current.focused); | ||
839 | } | ||
840 | } | ||
841 | |||
842 | /** | ||
843 | * Render a container's children using the L_STACKED layout. | ||
844 | */ | ||
845 | static void render_containers_stacked(struct sway_output *output, | ||
846 | pixman_region32_t *damage, struct parent_data *parent) { | ||
847 | if (!parent->children->length) { | ||
848 | return; | ||
849 | } | ||
850 | struct sway_container *current = parent->active_child; | ||
851 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
852 | size_t titlebar_height = container_titlebar_height(); | ||
853 | |||
854 | // Render titles | ||
855 | for (int i = 0; i < parent->children->length; ++i) { | ||
856 | struct sway_container *child = parent->children->items[i]; | ||
857 | struct sway_view *view = child->view; | ||
858 | struct sway_container_state *cstate = &child->current; | ||
859 | struct border_colors *colors; | ||
860 | struct wlr_texture *title_texture; | ||
861 | struct wlr_texture *marks_texture; | ||
862 | bool urgent = view ? | ||
863 | view_is_urgent(view) : container_has_urgent_child(child); | ||
864 | |||
865 | if (urgent) { | ||
866 | colors = &config->border_colors.urgent; | ||
867 | title_texture = child->title_urgent; | ||
868 | marks_texture = child->marks_urgent; | ||
869 | } else if (cstate->focused || parent->focused) { | ||
870 | colors = &config->border_colors.focused; | ||
871 | title_texture = child->title_focused; | ||
872 | marks_texture = child->marks_focused; | ||
873 | } else if (config->has_focused_tab_title && container_has_focused_child(child)) { | ||
874 | colors = &config->border_colors.focused_tab_title; | ||
875 | title_texture = child->title_focused_tab_title; | ||
876 | marks_texture = child->marks_focused_tab_title; | ||
877 | } else if (child == parent->active_child) { | ||
878 | colors = &config->border_colors.focused_inactive; | ||
879 | title_texture = child->title_focused_inactive; | ||
880 | marks_texture = child->marks_focused_inactive; | ||
881 | } else { | ||
882 | colors = &config->border_colors.unfocused; | ||
883 | title_texture = child->title_unfocused; | ||
884 | marks_texture = child->marks_unfocused; | ||
885 | } | ||
886 | |||
887 | int y = parent->box.y + titlebar_height * i; | ||
888 | render_titlebar(output, damage, child, parent->box.x, y, | ||
889 | parent->box.width, colors, title_texture, marks_texture); | ||
890 | |||
891 | if (child == current) { | ||
892 | current_colors = colors; | ||
893 | } | ||
894 | } | ||
895 | |||
896 | // Render surface and left/right/bottom borders | ||
897 | if (current->view) { | ||
898 | render_view(output, damage, current, current_colors); | ||
899 | } else { | ||
900 | render_container(output, damage, current, | ||
901 | parent->focused || current->current.focused); | ||
902 | } | ||
903 | } | ||
904 | |||
905 | static void render_containers(struct sway_output *output, | ||
906 | pixman_region32_t *damage, struct parent_data *parent) { | ||
907 | if (config->hide_lone_tab && parent->children->length == 1) { | ||
908 | struct sway_container *child = parent->children->items[0]; | ||
909 | if (child->view) { | ||
910 | render_containers_linear(output,damage, parent); | ||
911 | return; | ||
912 | } | ||
913 | } | ||
914 | |||
915 | switch (parent->layout) { | ||
916 | case L_NONE: | ||
917 | case L_HORIZ: | ||
918 | case L_VERT: | ||
919 | render_containers_linear(output, damage, parent); | ||
920 | break; | ||
921 | case L_STACKED: | ||
922 | render_containers_stacked(output, damage, parent); | ||
923 | break; | ||
924 | case L_TABBED: | ||
925 | render_containers_tabbed(output, damage, parent); | ||
926 | break; | ||
927 | } | ||
928 | } | ||
929 | |||
930 | static void render_container(struct sway_output *output, | ||
931 | pixman_region32_t *damage, struct sway_container *con, bool focused) { | ||
932 | struct parent_data data = { | ||
933 | .layout = con->current.layout, | ||
934 | .box = { | ||
935 | .x = floor(con->current.x), | ||
936 | .y = floor(con->current.y), | ||
937 | .width = con->current.width, | ||
938 | .height = con->current.height, | ||
939 | }, | ||
940 | .children = con->current.children, | ||
941 | .focused = focused, | ||
942 | .active_child = con->current.focused_inactive_child, | ||
943 | }; | ||
944 | render_containers(output, damage, &data); | ||
945 | } | ||
946 | |||
947 | static void render_workspace(struct sway_output *output, | ||
948 | pixman_region32_t *damage, struct sway_workspace *ws, bool focused) { | ||
949 | struct parent_data data = { | ||
950 | .layout = ws->current.layout, | ||
951 | .box = { | ||
952 | .x = floor(ws->current.x), | ||
953 | .y = floor(ws->current.y), | ||
954 | .width = ws->current.width, | ||
955 | .height = ws->current.height, | ||
956 | }, | ||
957 | .children = ws->current.tiling, | ||
958 | .focused = focused, | ||
959 | .active_child = ws->current.focused_inactive_child, | ||
960 | }; | ||
961 | render_containers(output, damage, &data); | ||
962 | } | ||
963 | |||
964 | static void render_floating_container(struct sway_output *soutput, | ||
965 | pixman_region32_t *damage, struct sway_container *con) { | ||
966 | if (con->view) { | ||
967 | struct sway_view *view = con->view; | ||
968 | struct border_colors *colors; | ||
969 | struct wlr_texture *title_texture; | ||
970 | struct wlr_texture *marks_texture; | ||
971 | |||
972 | if (view_is_urgent(view)) { | ||
973 | colors = &config->border_colors.urgent; | ||
974 | title_texture = con->title_urgent; | ||
975 | marks_texture = con->marks_urgent; | ||
976 | } else if (con->current.focused) { | ||
977 | colors = &config->border_colors.focused; | ||
978 | title_texture = con->title_focused; | ||
979 | marks_texture = con->marks_focused; | ||
980 | } else { | ||
981 | colors = &config->border_colors.unfocused; | ||
982 | title_texture = con->title_unfocused; | ||
983 | marks_texture = con->marks_unfocused; | ||
984 | } | ||
985 | |||
986 | if (con->current.border == B_NORMAL) { | ||
987 | render_titlebar(soutput, damage, con, floor(con->current.x), | ||
988 | floor(con->current.y), con->current.width, colors, | ||
989 | title_texture, marks_texture); | ||
990 | } else if (con->current.border == B_PIXEL) { | ||
991 | render_top_border(soutput, damage, con, colors); | ||
992 | } | ||
993 | render_view(soutput, damage, con, colors); | ||
994 | } else { | ||
995 | render_container(soutput, damage, con, con->current.focused); | ||
996 | } | ||
997 | } | ||
998 | |||
999 | static void render_floating(struct sway_output *soutput, | ||
1000 | pixman_region32_t *damage) { | ||
1001 | for (int i = 0; i < root->outputs->length; ++i) { | ||
1002 | struct sway_output *output = root->outputs->items[i]; | ||
1003 | for (int j = 0; j < output->current.workspaces->length; ++j) { | ||
1004 | struct sway_workspace *ws = output->current.workspaces->items[j]; | ||
1005 | if (!workspace_is_visible(ws)) { | ||
1006 | continue; | ||
1007 | } | ||
1008 | for (int k = 0; k < ws->current.floating->length; ++k) { | ||
1009 | struct sway_container *floater = ws->current.floating->items[k]; | ||
1010 | if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { | ||
1011 | continue; | ||
1012 | } | ||
1013 | render_floating_container(soutput, damage, floater); | ||
1014 | } | ||
1015 | } | ||
1016 | } | ||
1017 | } | ||
1018 | |||
1019 | static void render_seatops(struct sway_output *output, | ||
1020 | pixman_region32_t *damage) { | ||
1021 | struct sway_seat *seat; | ||
1022 | wl_list_for_each(seat, &server.input->seats, link) { | ||
1023 | seatop_render(seat, output, damage); | ||
1024 | } | ||
1025 | } | ||
1026 | |||
1027 | void output_render(struct sway_output *output, struct timespec *when, | ||
1028 | pixman_region32_t *damage) { | ||
1029 | struct wlr_output *wlr_output = output->wlr_output; | ||
1030 | struct wlr_renderer *renderer = output->server->renderer; | ||
1031 | |||
1032 | struct sway_workspace *workspace = output->current.active_workspace; | ||
1033 | if (workspace == NULL) { | ||
1034 | return; | ||
1035 | } | ||
1036 | |||
1037 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
1038 | if (!fullscreen_con) { | ||
1039 | fullscreen_con = workspace->current.fullscreen; | ||
1040 | } | ||
1041 | |||
1042 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
1043 | |||
1044 | 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 (!pixman_region32_not_empty(damage)) { | ||
1051 | // Output isn't damaged but needs buffer swap | ||
1052 | goto renderer_end; | ||
1053 | } | ||
1054 | |||
1055 | if (debug.damage == DAMAGE_HIGHLIGHT) { | ||
1056 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | ||
1057 | } | ||
1058 | |||
1059 | if (server.session_lock.locked) { | ||
1060 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
1061 | if (server.session_lock.lock == NULL) { | ||
1062 | // abandoned lock -> red BG | ||
1063 | clear_color[0] = 1.f; | ||
1064 | } | ||
1065 | int nrects; | ||
1066 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1067 | for (int i = 0; i < nrects; ++i) { | ||
1068 | scissor_output(wlr_output, &rects[i]); | ||
1069 | wlr_renderer_clear(renderer, clear_color); | ||
1070 | } | ||
1071 | |||
1072 | if (server.session_lock.lock != NULL) { | ||
1073 | struct render_data data = { | ||
1074 | .damage = damage, | ||
1075 | .alpha = 1.0f, | ||
1076 | }; | ||
1077 | |||
1078 | struct wlr_session_lock_surface_v1 *lock_surface; | ||
1079 | wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { | ||
1080 | if (lock_surface->output != wlr_output) { | ||
1081 | continue; | ||
1082 | } | ||
1083 | if (!lock_surface->mapped) { | ||
1084 | continue; | ||
1085 | } | ||
1086 | |||
1087 | output_surface_for_each_surface(output, lock_surface->surface, | ||
1088 | 0.0, 0.0, render_surface_iterator, &data); | ||
1089 | } | ||
1090 | } | ||
1091 | goto renderer_end; | ||
1092 | } | ||
1093 | |||
1094 | if (output_has_opaque_overlay_layer_surface(output)) { | ||
1095 | goto render_overlay; | ||
1096 | } | ||
1097 | |||
1098 | if (fullscreen_con) { | ||
1099 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
1100 | |||
1101 | int nrects; | ||
1102 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1103 | for (int i = 0; i < nrects; ++i) { | ||
1104 | scissor_output(wlr_output, &rects[i]); | ||
1105 | wlr_renderer_clear(renderer, clear_color); | ||
1106 | } | ||
1107 | |||
1108 | if (fullscreen_con->view) { | ||
1109 | if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { | ||
1110 | render_saved_view(fullscreen_con->view, output, damage, 1.0f); | ||
1111 | } else if (fullscreen_con->view->surface) { | ||
1112 | render_view_toplevels(fullscreen_con->view, | ||
1113 | output, damage, 1.0f); | ||
1114 | } | ||
1115 | } else { | ||
1116 | render_container(output, damage, fullscreen_con, | ||
1117 | fullscreen_con->current.focused); | ||
1118 | } | ||
1119 | |||
1120 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
1121 | struct sway_container *floater = | ||
1122 | workspace->current.floating->items[i]; | ||
1123 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
1124 | render_floating_container(output, damage, floater); | ||
1125 | } | ||
1126 | } | ||
1127 | #if HAVE_XWAYLAND | ||
1128 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1129 | #endif | ||
1130 | } else { | ||
1131 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | ||
1132 | |||
1133 | int nrects; | ||
1134 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1135 | for (int i = 0; i < nrects; ++i) { | ||
1136 | scissor_output(wlr_output, &rects[i]); | ||
1137 | wlr_renderer_clear(renderer, clear_color); | ||
1138 | } | ||
1139 | |||
1140 | render_layer_toplevel(output, damage, | ||
1141 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1142 | render_layer_toplevel(output, damage, | ||
1143 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1144 | |||
1145 | render_workspace(output, damage, workspace, workspace->current.focused); | ||
1146 | render_floating(output, damage); | ||
1147 | #if HAVE_XWAYLAND | ||
1148 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1149 | #endif | ||
1150 | render_layer_toplevel(output, damage, | ||
1151 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1152 | |||
1153 | render_layer_popups(output, damage, | ||
1154 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1155 | render_layer_popups(output, damage, | ||
1156 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1157 | render_layer_popups(output, damage, | ||
1158 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1159 | } | ||
1160 | |||
1161 | render_seatops(output, damage); | ||
1162 | |||
1163 | struct sway_seat *seat = input_manager_current_seat(); | ||
1164 | struct sway_container *focus = seat_get_focused_container(seat); | ||
1165 | if (focus && focus->view) { | ||
1166 | render_view_popups(focus->view, output, damage, focus->alpha); | ||
1167 | } | ||
1168 | |||
1169 | render_overlay: | ||
1170 | render_layer_toplevel(output, damage, | ||
1171 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1172 | render_layer_popups(output, damage, | ||
1173 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1174 | render_drag_icons(output, damage, &root->drag_icons); | ||
1175 | |||
1176 | renderer_end: | ||
1177 | wlr_renderer_scissor(renderer, NULL); | ||
1178 | wlr_output_render_software_cursors(wlr_output, damage); | ||
1179 | wlr_renderer_end(renderer); | ||
1180 | |||
1181 | int width, height; | ||
1182 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1183 | |||
1184 | pixman_region32_t frame_damage; | ||
1185 | pixman_region32_init(&frame_damage); | ||
1186 | |||
1187 | enum wl_output_transform transform = | ||
1188 | wlr_output_transform_invert(wlr_output->transform); | ||
1189 | wlr_region_transform(&frame_damage, &output->damage->current, | ||
1190 | transform, width, height); | ||
1191 | |||
1192 | if (debug.damage != DAMAGE_DEFAULT) { | ||
1193 | pixman_region32_union_rect(&frame_damage, &frame_damage, | ||
1194 | 0, 0, wlr_output->width, wlr_output->height); | ||
1195 | } | ||
1196 | |||
1197 | wlr_output_set_damage(wlr_output, &frame_damage); | ||
1198 | pixman_region32_fini(&frame_damage); | ||
1199 | |||
1200 | if (!wlr_output_commit(wlr_output)) { | ||
1201 | return; | ||
1202 | } | ||
1203 | output->last_frame = *when; | ||
1204 | } | ||
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c deleted file mode 100644 index 1d7b536d..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_compositor.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 f5a3a053..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 | ||
@@ -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 8da922d5..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,55 +18,39 @@ | |||
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_view_coords(struct sway_view_child *child, | ||
25 | int *sx, int *sy) { | ||
26 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
27 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; | ||
28 | |||
29 | wlr_xdg_popup_get_toplevel_coords(wlr_popup, | ||
30 | wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x, | ||
31 | wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y, | ||
32 | sx, sy); | ||
33 | } | ||
34 | |||
35 | static void popup_destroy(struct sway_view_child *child) { | ||
36 | if (!sway_assert(child->impl == &popup_impl, | ||
37 | "Expected an xdg_shell popup")) { | ||
38 | return; | ||
39 | } | ||
40 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
41 | wl_list_remove(&popup->new_popup.link); | ||
42 | wl_list_remove(&popup->destroy.link); | ||
43 | free(popup); | ||
44 | } | ||
45 | |||
46 | static const struct sway_view_child_impl popup_impl = { | ||
47 | .get_view_coords = popup_get_view_coords, | ||
48 | .destroy = popup_destroy, | ||
49 | }; | ||
50 | |||
51 | static struct sway_xdg_popup *popup_create( | 21 | static struct sway_xdg_popup *popup_create( |
52 | 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); | ||
53 | 24 | ||
54 | 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) { |
55 | struct sway_xdg_popup *popup = | 26 | struct sway_xdg_popup *popup = |
56 | wl_container_of(listener, popup, new_popup); | 27 | wl_container_of(listener, popup, new_popup); |
57 | struct wlr_xdg_popup *wlr_popup = data; | 28 | struct wlr_xdg_popup *wlr_popup = data; |
58 | popup_create(wlr_popup, popup->child.view); | 29 | popup_create(wlr_popup, popup->view, popup->xdg_surface_tree); |
59 | } | 30 | } |
60 | 31 | ||
61 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 32 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
62 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); | 33 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); |
63 | 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); | ||
64 | } | 41 | } |
65 | 42 | ||
66 | static void popup_unconstrain(struct sway_xdg_popup *popup) { | 43 | static void popup_unconstrain(struct sway_xdg_popup *popup) { |
67 | struct sway_view *view = popup->child.view; | 44 | struct sway_view *view = popup->view; |
68 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; | 45 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; |
69 | 46 | ||
70 | struct sway_output *output = view->container->pending.workspace->output; | 47 | struct sway_workspace *workspace = view->container->pending.workspace; |
48 | if (!workspace) { | ||
49 | // is null if in the scratchpad | ||
50 | return; | ||
51 | } | ||
52 | |||
53 | struct sway_output *output = workspace->output; | ||
71 | 54 | ||
72 | // 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 |
73 | // of the popup | 56 | // of the popup |
@@ -81,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { | |||
81 | 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); |
82 | } | 65 | } |
83 | 66 | ||
84 | static struct sway_xdg_popup *popup_create( | 67 | static void popup_handle_surface_commit(struct wl_listener *listener, void *data) { |
85 | 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) { | ||
86 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; | 81 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; |
87 | 82 | ||
88 | struct sway_xdg_popup *popup = | 83 | struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); |
89 | calloc(1, sizeof(struct sway_xdg_popup)); | 84 | if (!popup) { |
90 | 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); | ||
91 | return NULL; | 94 | return NULL; |
92 | } | 95 | } |
93 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | 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); | ||
102 | return NULL; | ||
103 | } | ||
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 | |||
94 | popup->wlr_xdg_popup = xdg_surface->popup; | 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; | ||
95 | 120 | ||
121 | wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit); | ||
122 | popup->surface_commit.notify = popup_handle_surface_commit; | ||
96 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 123 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
97 | popup->new_popup.notify = popup_handle_new_popup; | 124 | popup->new_popup.notify = popup_handle_new_popup; |
98 | 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); | ||
99 | popup->destroy.notify = popup_handle_destroy; | 128 | popup->destroy.notify = popup_handle_destroy; |
100 | 129 | ||
101 | wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); | ||
102 | wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); | ||
103 | |||
104 | popup_unconstrain(popup); | ||
105 | |||
106 | return popup; | 130 | return popup; |
107 | } | 131 | } |
108 | 132 | ||
109 | |||
110 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( | 133 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( |
111 | struct sway_view *view) { | 134 | struct sway_view *view) { |
112 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, | 135 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, |
@@ -163,12 +186,19 @@ static void set_tiled(struct sway_view *view, bool tiled) { | |||
163 | if (xdg_shell_view_from_view(view) == NULL) { | 186 | if (xdg_shell_view_from_view(view) == NULL) { |
164 | return; | 187 | return; |
165 | } | 188 | } |
166 | enum wlr_edges edges = WLR_EDGE_NONE; | 189 | if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >= |
167 | if (tiled) { | 190 | XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { |
168 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | | 191 | enum wlr_edges edges = WLR_EDGE_NONE; |
169 | WLR_EDGE_BOTTOM; | 192 | if (tiled) { |
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); | ||
170 | } | 201 | } |
171 | wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); | ||
172 | } | 202 | } |
173 | 203 | ||
174 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { | 204 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { |
@@ -194,24 +224,6 @@ static bool wants_floating(struct sway_view *view) { | |||
194 | || toplevel->parent; | 224 | || toplevel->parent; |
195 | } | 225 | } |
196 | 226 | ||
197 | static void for_each_surface(struct sway_view *view, | ||
198 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
199 | if (xdg_shell_view_from_view(view) == NULL) { | ||
200 | return; | ||
201 | } | ||
202 | wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator, | ||
203 | user_data); | ||
204 | } | ||
205 | |||
206 | static void for_each_popup_surface(struct sway_view *view, | ||
207 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
208 | if (xdg_shell_view_from_view(view) == NULL) { | ||
209 | return; | ||
210 | } | ||
211 | wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base, | ||
212 | iterator, user_data); | ||
213 | } | ||
214 | |||
215 | static bool is_transient_for(struct sway_view *child, | 227 | static bool is_transient_for(struct sway_view *child, |
216 | struct sway_view *ancestor) { | 228 | struct sway_view *ancestor) { |
217 | if (xdg_shell_view_from_view(child) == NULL) { | 229 | if (xdg_shell_view_from_view(child) == NULL) { |
@@ -259,8 +271,6 @@ static const struct sway_view_impl view_impl = { | |||
259 | .set_fullscreen = set_fullscreen, | 271 | .set_fullscreen = set_fullscreen, |
260 | .set_resizing = set_resizing, | 272 | .set_resizing = set_resizing, |
261 | .wants_floating = wants_floating, | 273 | .wants_floating = wants_floating, |
262 | .for_each_surface = for_each_surface, | ||
263 | .for_each_popup_surface = for_each_popup_surface, | ||
264 | .is_transient_for = is_transient_for, | 274 | .is_transient_for = is_transient_for, |
265 | .close = _close, | 275 | .close = _close, |
266 | .close_popups = close_popups, | 276 | .close_popups = close_popups, |
@@ -273,6 +283,20 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
273 | struct sway_view *view = &xdg_shell_view->view; | 283 | struct sway_view *view = &xdg_shell_view->view; |
274 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; | 284 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; |
275 | 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 | } | ||
299 | |||
276 | struct wlr_box new_geo; | 300 | struct wlr_box new_geo; |
277 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); | 301 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); |
278 | bool new_size = new_geo.width != view->geometry.width || | 302 | bool new_size = new_geo.width != view->geometry.width || |
@@ -284,23 +308,32 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
284 | // The client changed its surface size in this commit. For floating | 308 | // The client changed its surface size in this commit. For floating |
285 | // containers, we resize the container to match. For tiling containers, | 309 | // containers, we resize the container to match. For tiling containers, |
286 | // we only recenter the surface. | 310 | // we only recenter the surface. |
287 | desktop_damage_view(view); | ||
288 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | 311 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); |
289 | if (container_is_floating(view->container)) { | 312 | if (container_is_floating(view->container)) { |
290 | 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 | } | ||
291 | transaction_commit_dirty_client(); | 319 | transaction_commit_dirty_client(); |
292 | } else { | ||
293 | view_center_surface(view); | ||
294 | } | 320 | } |
295 | desktop_damage_view(view); | 321 | |
322 | view_center_and_clip_surface(view); | ||
296 | } | 323 | } |
297 | 324 | ||
298 | if (view->container->node.instruction) { | 325 | if (view->container->node.instruction) { |
299 | transaction_notify_view_ready_by_serial(view, | 326 | bool successful = transaction_notify_view_ready_by_serial(view, |
300 | xdg_surface->current.configure_serial); | 327 | xdg_surface->current.configure_serial); |
301 | } | ||
302 | 328 | ||
303 | view_damage_from(view); | 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 | } | ||
336 | } | ||
304 | } | 337 | } |
305 | 338 | ||
306 | static void handle_set_title(struct wl_listener *listener, void *data) { | 339 | static void handle_set_title(struct wl_listener *listener, void *data) { |
@@ -315,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) { | |||
315 | struct sway_xdg_shell_view *xdg_shell_view = | 348 | struct sway_xdg_shell_view *xdg_shell_view = |
316 | wl_container_of(listener, xdg_shell_view, set_app_id); | 349 | wl_container_of(listener, xdg_shell_view, set_app_id); |
317 | struct sway_view *view = &xdg_shell_view->view; | 350 | struct sway_view *view = &xdg_shell_view->view; |
351 | view_update_app_id(view); | ||
318 | view_execute_criteria(view); | 352 | view_execute_criteria(view); |
319 | } | 353 | } |
320 | 354 | ||
@@ -322,7 +356,16 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { | |||
322 | struct sway_xdg_shell_view *xdg_shell_view = | 356 | struct sway_xdg_shell_view *xdg_shell_view = |
323 | wl_container_of(listener, xdg_shell_view, new_popup); | 357 | wl_container_of(listener, xdg_shell_view, new_popup); |
324 | struct wlr_xdg_popup *wlr_popup = data; | 358 | struct wlr_xdg_popup *wlr_popup = data; |
325 | 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); | ||
326 | } | 369 | } |
327 | 370 | ||
328 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | 371 | static void handle_request_maximize(struct wl_listener *listener, void *data) { |
@@ -338,7 +381,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
338 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; | 381 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; |
339 | struct sway_view *view = &xdg_shell_view->view; | 382 | struct sway_view *view = &xdg_shell_view->view; |
340 | 383 | ||
341 | if (!toplevel->base->mapped) { | 384 | if (!toplevel->base->surface->mapped) { |
342 | return; | 385 | return; |
343 | } | 386 | } |
344 | 387 | ||
@@ -403,7 +446,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
403 | 446 | ||
404 | view_unmap(view); | 447 | view_unmap(view); |
405 | 448 | ||
406 | wl_list_remove(&xdg_shell_view->commit.link); | ||
407 | wl_list_remove(&xdg_shell_view->new_popup.link); | 449 | wl_list_remove(&xdg_shell_view->new_popup.link); |
408 | wl_list_remove(&xdg_shell_view->request_maximize.link); | 450 | wl_list_remove(&xdg_shell_view->request_maximize.link); |
409 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | 451 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); |
@@ -446,10 +488,6 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
446 | 488 | ||
447 | transaction_commit_dirty(); | 489 | transaction_commit_dirty(); |
448 | 490 | ||
449 | xdg_shell_view->commit.notify = handle_commit; | ||
450 | wl_signal_add(&toplevel->base->surface->events.commit, | ||
451 | &xdg_shell_view->commit); | ||
452 | |||
453 | xdg_shell_view->new_popup.notify = handle_new_popup; | 491 | xdg_shell_view->new_popup.notify = handle_new_popup; |
454 | wl_signal_add(&toplevel->base->events.new_popup, | 492 | wl_signal_add(&toplevel->base->events.new_popup, |
455 | &xdg_shell_view->new_popup); | 493 | &xdg_shell_view->new_popup); |
@@ -489,6 +527,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
489 | wl_list_remove(&xdg_shell_view->destroy.link); | 527 | wl_list_remove(&xdg_shell_view->destroy.link); |
490 | wl_list_remove(&xdg_shell_view->map.link); | 528 | wl_list_remove(&xdg_shell_view->map.link); |
491 | wl_list_remove(&xdg_shell_view->unmap.link); | 529 | wl_list_remove(&xdg_shell_view->unmap.link); |
530 | wl_list_remove(&xdg_shell_view->commit.link); | ||
492 | view->wlr_xdg_toplevel = NULL; | 531 | view->wlr_xdg_toplevel = NULL; |
493 | if (view->xdg_decoration) { | 532 | if (view->xdg_decoration) { |
494 | view->xdg_decoration->view = NULL; | 533 | view->xdg_decoration->view = NULL; |
@@ -501,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface( | |||
501 | return xdg_surface->data; | 540 | return xdg_surface->data; |
502 | } | 541 | } |
503 | 542 | ||
504 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | 543 | void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { |
505 | struct wlr_xdg_surface *xdg_surface = data; | 544 | struct wlr_xdg_toplevel *xdg_toplevel = data; |
506 | |||
507 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
508 | sway_log(SWAY_DEBUG, "New xdg_shell popup"); | ||
509 | return; | ||
510 | } | ||
511 | 545 | ||
512 | 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'", |
513 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 547 | xdg_toplevel->title, xdg_toplevel->app_id); |
514 | wlr_xdg_surface_ping(xdg_surface); | 548 | wlr_xdg_surface_ping(xdg_toplevel->base); |
515 | 549 | ||
516 | struct sway_xdg_shell_view *xdg_shell_view = | 550 | struct sway_xdg_shell_view *xdg_shell_view = |
517 | calloc(1, sizeof(struct sway_xdg_shell_view)); | 551 | calloc(1, sizeof(struct sway_xdg_shell_view)); |
@@ -519,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
519 | return; | 553 | return; |
520 | } | 554 | } |
521 | 555 | ||
522 | 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)) { |
523 | xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; | 557 | free(xdg_shell_view); |
558 | return; | ||
559 | } | ||
560 | xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel; | ||
524 | 561 | ||
525 | xdg_shell_view->map.notify = handle_map; | 562 | xdg_shell_view->map.notify = handle_map; |
526 | 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); |
527 | 564 | ||
528 | xdg_shell_view->unmap.notify = handle_unmap; | 565 | xdg_shell_view->unmap.notify = handle_unmap; |
529 | 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); | ||
530 | 571 | ||
531 | xdg_shell_view->destroy.notify = handle_destroy; | 572 | xdg_shell_view->destroy.notify = handle_destroy; |
532 | 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; | ||
533 | 578 | ||
534 | xdg_surface->data = xdg_shell_view; | 579 | wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel, |
580 | XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); | ||
535 | } | 581 | } |
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 7c5dde53..270cf08f 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -1,21 +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> |
9 | #include <xcb/xcb_icccm.h> | 10 | #include <xcb/xcb_icccm.h> |
10 | #include "log.h" | 11 | #include "log.h" |
11 | #include "sway/desktop.h" | ||
12 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
13 | #include "sway/input/cursor.h" | 13 | #include "sway/input/cursor.h" |
14 | #include "sway/input/input-manager.h" | 14 | #include "sway/input/input-manager.h" |
15 | #include "sway/input/seat.h" | 15 | #include "sway/input/seat.h" |
16 | #include "sway/output.h" | 16 | #include "sway/output.h" |
17 | #include "sway/scene_descriptor.h" | ||
17 | #include "sway/tree/arrange.h" | 18 | #include "sway/tree/arrange.h" |
18 | #include "sway/tree/container.h" | 19 | #include "sway/tree/container.h" |
20 | #include "sway/server.h" | ||
19 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
20 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
21 | 23 | ||
@@ -43,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener, | |||
43 | ev->width, ev->height); | 45 | ev->width, ev->height); |
44 | } | 46 | } |
45 | 47 | ||
46 | static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { | ||
47 | struct sway_xwayland_unmanaged *surface = | ||
48 | wl_container_of(listener, surface, commit); | ||
49 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
50 | |||
51 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
52 | false); | ||
53 | } | ||
54 | |||
55 | 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) { |
56 | struct sway_xwayland_unmanaged *surface = | 49 | struct sway_xwayland_unmanaged *surface = |
57 | wl_container_of(listener, surface, set_geometry); | 50 | wl_container_of(listener, surface, set_geometry); |
58 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 51 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
59 | 52 | ||
60 | if (xsurface->x != surface->lx || xsurface->y != surface->ly) { | 53 | wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y); |
61 | // Surface has moved | ||
62 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
63 | true); | ||
64 | surface->lx = xsurface->x; | ||
65 | surface->ly = xsurface->y; | ||
66 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
67 | true); | ||
68 | } | ||
69 | } | 54 | } |
70 | 55 | ||
71 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | 56 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { |
@@ -73,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | |||
73 | wl_container_of(listener, surface, map); | 58 | wl_container_of(listener, surface, map); |
74 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 59 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
75 | 60 | ||
76 | wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); | 61 | surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged, |
62 | xsurface->surface); | ||
77 | 63 | ||
78 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); | 64 | if (surface->surface_scene) { |
79 | surface->set_geometry.notify = unmanaged_handle_set_geometry; | 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); | ||
80 | 69 | ||
81 | wl_signal_add(&xsurface->surface->events.commit, &surface->commit); | 70 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); |
82 | surface->commit.notify = unmanaged_handle_commit; | 71 | surface->set_geometry.notify = unmanaged_handle_set_geometry; |
83 | 72 | } | |
84 | surface->lx = xsurface->x; | ||
85 | surface->ly = xsurface->y; | ||
86 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); | ||
87 | 73 | ||
88 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { | 74 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { |
89 | struct sway_seat *seat = input_manager_current_seat(); | 75 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -97,10 +83,13 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
97 | struct sway_xwayland_unmanaged *surface = | 83 | struct sway_xwayland_unmanaged *surface = |
98 | wl_container_of(listener, surface, unmap); | 84 | wl_container_of(listener, surface, unmap); |
99 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 85 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
100 | desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); | 86 | |
101 | wl_list_remove(&surface->link); | 87 | if (surface->surface_scene) { |
102 | wl_list_remove(&surface->set_geometry.link); | 88 | wl_list_remove(&surface->set_geometry.link); |
103 | wl_list_remove(&surface->commit.link); | 89 | |
90 | wlr_scene_node_destroy(&surface->surface_scene->buffer->node); | ||
91 | surface->surface_scene = NULL; | ||
92 | } | ||
104 | 93 | ||
105 | struct sway_seat *seat = input_manager_current_seat(); | 94 | struct sway_seat *seat = input_manager_current_seat(); |
106 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { | 95 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { |
@@ -123,8 +112,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
123 | } | 112 | } |
124 | 113 | ||
125 | static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { | 114 | static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { |
126 | struct wlr_xwayland_surface *xsurface = data; | 115 | struct sway_xwayland_unmanaged *surface = |
127 | if (!xsurface->mapped) { | 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) { | ||
128 | return; | 119 | return; |
129 | } | 120 | } |
130 | struct sway_seat *seat = input_manager_current_seat(); | 121 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -136,12 +127,29 @@ static void unmanaged_handle_request_activate(struct wl_listener *listener, void | |||
136 | seat_set_focus_surface(seat, xsurface->surface, false); | 127 | seat_set_focus_surface(seat, xsurface->surface, false); |
137 | } | 128 | } |
138 | 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 | |||
139 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { | 147 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { |
140 | struct sway_xwayland_unmanaged *surface = | 148 | struct sway_xwayland_unmanaged *surface = |
141 | wl_container_of(listener, surface, destroy); | 149 | wl_container_of(listener, surface, destroy); |
142 | wl_list_remove(&surface->request_configure.link); | 150 | wl_list_remove(&surface->request_configure.link); |
143 | wl_list_remove(&surface->map.link); | 151 | wl_list_remove(&surface->associate.link); |
144 | wl_list_remove(&surface->unmap.link); | 152 | wl_list_remove(&surface->dissociate.link); |
145 | wl_list_remove(&surface->destroy.link); | 153 | wl_list_remove(&surface->destroy.link); |
146 | wl_list_remove(&surface->override_redirect.link); | 154 | wl_list_remove(&surface->override_redirect.link); |
147 | wl_list_remove(&surface->request_activate.link); | 155 | wl_list_remove(&surface->request_activate.link); |
@@ -149,6 +157,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { | |||
149 | } | 157 | } |
150 | 158 | ||
151 | 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); | ||
152 | 161 | ||
153 | 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); |
154 | 163 | ||
@@ -157,14 +166,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi | |||
157 | wl_container_of(listener, surface, override_redirect); | 166 | wl_container_of(listener, surface, override_redirect); |
158 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 167 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
159 | 168 | ||
160 | bool mapped = xsurface->mapped; | 169 | bool associated = xsurface->surface != NULL; |
170 | bool mapped = associated && xsurface->surface->mapped; | ||
161 | if (mapped) { | 171 | if (mapped) { |
162 | unmanaged_handle_unmap(&surface->unmap, NULL); | 172 | unmanaged_handle_unmap(&surface->unmap, NULL); |
163 | } | 173 | } |
174 | if (associated) { | ||
175 | unmanaged_handle_dissociate(&surface->dissociate, NULL); | ||
176 | } | ||
164 | 177 | ||
165 | unmanaged_handle_destroy(&surface->destroy, NULL); | 178 | unmanaged_handle_destroy(&surface->destroy, NULL); |
166 | xsurface->data = NULL; | 179 | xsurface->data = NULL; |
180 | |||
167 | 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 | } | ||
168 | if (mapped) { | 185 | if (mapped) { |
169 | handle_map(&xwayland_view->map, xsurface); | 186 | handle_map(&xwayland_view->map, xsurface); |
170 | } | 187 | } |
@@ -184,10 +201,10 @@ static struct sway_xwayland_unmanaged *create_unmanaged( | |||
184 | wl_signal_add(&xsurface->events.request_configure, | 201 | wl_signal_add(&xsurface->events.request_configure, |
185 | &surface->request_configure); | 202 | &surface->request_configure); |
186 | surface->request_configure.notify = unmanaged_handle_request_configure; | 203 | surface->request_configure.notify = unmanaged_handle_request_configure; |
187 | wl_signal_add(&xsurface->events.map, &surface->map); | 204 | wl_signal_add(&xsurface->events.associate, &surface->associate); |
188 | surface->map.notify = unmanaged_handle_map; | 205 | surface->associate.notify = unmanaged_handle_associate; |
189 | wl_signal_add(&xsurface->events.unmap, &surface->unmap); | 206 | wl_signal_add(&xsurface->events.dissociate, &surface->dissociate); |
190 | surface->unmap.notify = unmanaged_handle_unmap; | 207 | surface->dissociate.notify = unmanaged_handle_dissociate; |
191 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); | 208 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); |
192 | surface->destroy.notify = unmanaged_handle_destroy; | 209 | surface->destroy.notify = unmanaged_handle_destroy; |
193 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); | 210 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); |
@@ -396,17 +413,6 @@ static const struct sway_view_impl view_impl = { | |||
396 | .destroy = destroy, | 413 | .destroy = destroy, |
397 | }; | 414 | }; |
398 | 415 | ||
399 | static void get_geometry(struct sway_view *view, struct wlr_box *box) { | ||
400 | box->x = box->y = 0; | ||
401 | if (view->surface) { | ||
402 | box->width = view->surface->current.width; | ||
403 | box->height = view->surface->current.height; | ||
404 | } else { | ||
405 | box->width = 0; | ||
406 | box->height = 0; | ||
407 | } | ||
408 | } | ||
409 | |||
410 | static void handle_commit(struct wl_listener *listener, void *data) { | 416 | static void handle_commit(struct wl_listener *listener, void *data) { |
411 | struct sway_xwayland_view *xwayland_view = | 417 | struct sway_xwayland_view *xwayland_view = |
412 | wl_container_of(listener, xwayland_view, commit); | 418 | wl_container_of(listener, xwayland_view, commit); |
@@ -414,34 +420,38 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
414 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 420 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
415 | struct wlr_surface_state *state = &xsurface->surface->current; | 421 | struct wlr_surface_state *state = &xsurface->surface->current; |
416 | 422 | ||
417 | struct wlr_box new_geo; | 423 | struct wlr_box new_geo = {0}; |
418 | get_geometry(view, &new_geo); | 424 | new_geo.width = state->width; |
425 | new_geo.height = state->height; | ||
426 | |||
419 | bool new_size = new_geo.width != view->geometry.width || | 427 | bool new_size = new_geo.width != view->geometry.width || |
420 | new_geo.height != view->geometry.height || | 428 | new_geo.height != view->geometry.height; |
421 | new_geo.x != view->geometry.x || | ||
422 | new_geo.y != view->geometry.y; | ||
423 | 429 | ||
424 | if (new_size) { | 430 | if (new_size) { |
425 | // The client changed its surface size in this commit. For floating | 431 | // The client changed its surface size in this commit. For floating |
426 | // containers, we resize the container to match. For tiling containers, | 432 | // containers, we resize the container to match. For tiling containers, |
427 | // we only recenter the surface. | 433 | // we only recenter the surface. |
428 | desktop_damage_view(view); | ||
429 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | 434 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); |
430 | if (container_is_floating(view->container)) { | 435 | if (container_is_floating(view->container)) { |
431 | view_update_size(view); | 436 | view_update_size(view); |
432 | transaction_commit_dirty_client(); | 437 | transaction_commit_dirty_client(); |
433 | } else { | ||
434 | view_center_surface(view); | ||
435 | } | 438 | } |
436 | desktop_damage_view(view); | 439 | |
440 | view_center_and_clip_surface(view); | ||
437 | } | 441 | } |
438 | 442 | ||
439 | if (view->container->node.instruction) { | 443 | if (view->container->node.instruction) { |
440 | transaction_notify_view_ready_by_geometry(view, | 444 | bool successful = transaction_notify_view_ready_by_geometry(view, |
441 | xsurface->x, xsurface->y, state->width, state->height); | 445 | xsurface->x, xsurface->y, state->width, state->height); |
442 | } | ||
443 | 446 | ||
444 | 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 | } | ||
445 | } | 455 | } |
446 | 456 | ||
447 | static void handle_destroy(struct wl_listener *listener, void *data) { | 457 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -466,11 +476,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
466 | wl_list_remove(&xwayland_view->set_title.link); | 476 | wl_list_remove(&xwayland_view->set_title.link); |
467 | wl_list_remove(&xwayland_view->set_class.link); | 477 | wl_list_remove(&xwayland_view->set_class.link); |
468 | 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); | ||
469 | wl_list_remove(&xwayland_view->set_window_type.link); | 480 | wl_list_remove(&xwayland_view->set_window_type.link); |
470 | wl_list_remove(&xwayland_view->set_hints.link); | 481 | wl_list_remove(&xwayland_view->set_hints.link); |
471 | wl_list_remove(&xwayland_view->set_decorations.link); | 482 | wl_list_remove(&xwayland_view->set_decorations.link); |
472 | wl_list_remove(&xwayland_view->map.link); | 483 | wl_list_remove(&xwayland_view->associate.link); |
473 | wl_list_remove(&xwayland_view->unmap.link); | 484 | wl_list_remove(&xwayland_view->dissociate.link); |
474 | wl_list_remove(&xwayland_view->override_redirect.link); | 485 | wl_list_remove(&xwayland_view->override_redirect.link); |
475 | view_begin_destroy(&xwayland_view->view); | 486 | view_begin_destroy(&xwayland_view->view); |
476 | } | 487 | } |
@@ -484,16 +495,28 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
484 | return; | 495 | return; |
485 | } | 496 | } |
486 | 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 | |||
487 | view_unmap(view); | 506 | view_unmap(view); |
507 | } | ||
488 | 508 | ||
489 | 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; | ||
490 | } | 513 | } |
491 | 514 | ||
492 | static void handle_map(struct wl_listener *listener, void *data) { | 515 | static void handle_map(struct wl_listener *listener, void *data) { |
493 | struct sway_xwayland_view *xwayland_view = | 516 | struct sway_xwayland_view *xwayland_view = |
494 | wl_container_of(listener, xwayland_view, map); | 517 | wl_container_of(listener, xwayland_view, map); |
495 | struct wlr_xwayland_surface *xsurface = data; | ||
496 | 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; | ||
497 | 520 | ||
498 | view->natural_width = xsurface->width; | 521 | view->natural_width = xsurface->width; |
499 | view->natural_height = xsurface->height; | 522 | view->natural_height = xsurface->height; |
@@ -506,23 +529,42 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
506 | // Put it back into the tree | 529 | // Put it back into the tree |
507 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); | 530 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); |
508 | 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 | |||
509 | transaction_commit_dirty(); | 541 | transaction_commit_dirty(); |
510 | } | 542 | } |
511 | 543 | ||
544 | static void handle_dissociate(struct wl_listener *listener, void *data); | ||
545 | |||
512 | static void handle_override_redirect(struct wl_listener *listener, void *data) { | 546 | static void handle_override_redirect(struct wl_listener *listener, void *data) { |
513 | struct sway_xwayland_view *xwayland_view = | 547 | struct sway_xwayland_view *xwayland_view = |
514 | wl_container_of(listener, xwayland_view, override_redirect); | 548 | wl_container_of(listener, xwayland_view, override_redirect); |
515 | struct wlr_xwayland_surface *xsurface = data; | ||
516 | 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; | ||
517 | 551 | ||
518 | bool mapped = xsurface->mapped; | 552 | bool associated = xsurface->surface != NULL; |
553 | bool mapped = associated && xsurface->surface->mapped; | ||
519 | if (mapped) { | 554 | if (mapped) { |
520 | handle_unmap(&xwayland_view->unmap, NULL); | 555 | handle_unmap(&xwayland_view->unmap, NULL); |
521 | } | 556 | } |
557 | if (associated) { | ||
558 | handle_dissociate(&xwayland_view->dissociate, NULL); | ||
559 | } | ||
522 | 560 | ||
523 | handle_destroy(&xwayland_view->destroy, view); | 561 | handle_destroy(&xwayland_view->destroy, view); |
524 | xsurface->data = NULL; | 562 | xsurface->data = NULL; |
563 | |||
525 | 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 | } | ||
526 | if (mapped) { | 568 | if (mapped) { |
527 | unmanaged_handle_map(&unmanaged->map, xsurface); | 569 | unmanaged_handle_map(&unmanaged->map, xsurface); |
528 | } | 570 | } |
@@ -534,7 +576,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { | |||
534 | struct wlr_xwayland_surface_configure_event *ev = data; | 576 | struct wlr_xwayland_surface_configure_event *ev = data; |
535 | struct sway_view *view = &xwayland_view->view; | 577 | struct sway_view *view = &xwayland_view->view; |
536 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 578 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
537 | if (!xsurface->mapped) { | 579 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
538 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, | 580 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, |
539 | ev->width, ev->height); | 581 | ev->width, ev->height); |
540 | return; | 582 | return; |
@@ -563,7 +605,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
563 | wl_container_of(listener, xwayland_view, request_fullscreen); | 605 | wl_container_of(listener, xwayland_view, request_fullscreen); |
564 | struct sway_view *view = &xwayland_view->view; | 606 | struct sway_view *view = &xwayland_view->view; |
565 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 607 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
566 | if (!xsurface->mapped) { | 608 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
567 | return; | 609 | return; |
568 | } | 610 | } |
569 | container_set_fullscreen(view->container, xsurface->fullscreen); | 611 | container_set_fullscreen(view->container, xsurface->fullscreen); |
@@ -577,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) { | |||
577 | wl_container_of(listener, xwayland_view, request_minimize); | 619 | wl_container_of(listener, xwayland_view, request_minimize); |
578 | struct sway_view *view = &xwayland_view->view; | 620 | struct sway_view *view = &xwayland_view->view; |
579 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 621 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
580 | if (!xsurface->mapped) { | 622 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
581 | return; | 623 | return; |
582 | } | 624 | } |
583 | 625 | ||
@@ -592,7 +634,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { | |||
592 | wl_container_of(listener, xwayland_view, request_move); | 634 | wl_container_of(listener, xwayland_view, request_move); |
593 | struct sway_view *view = &xwayland_view->view; | 635 | struct sway_view *view = &xwayland_view->view; |
594 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 636 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
595 | if (!xsurface->mapped) { | 637 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
596 | return; | 638 | return; |
597 | } | 639 | } |
598 | if (!container_is_floating(view->container) || | 640 | if (!container_is_floating(view->container) || |
@@ -608,7 +650,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { | |||
608 | wl_container_of(listener, xwayland_view, request_resize); | 650 | wl_container_of(listener, xwayland_view, request_resize); |
609 | struct sway_view *view = &xwayland_view->view; | 651 | struct sway_view *view = &xwayland_view->view; |
610 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 652 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
611 | if (!xsurface->mapped) { | 653 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
612 | return; | 654 | return; |
613 | } | 655 | } |
614 | if (!container_is_floating(view->container)) { | 656 | if (!container_is_floating(view->container)) { |
@@ -624,10 +666,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) { | |||
624 | wl_container_of(listener, xwayland_view, request_activate); | 666 | wl_container_of(listener, xwayland_view, request_activate); |
625 | struct sway_view *view = &xwayland_view->view; | 667 | struct sway_view *view = &xwayland_view->view; |
626 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 668 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
627 | if (!xsurface->mapped) { | 669 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
628 | return; | 670 | return; |
629 | } | 671 | } |
630 | view_request_activate(view); | 672 | view_request_activate(view, NULL); |
631 | 673 | ||
632 | transaction_commit_dirty(); | 674 | transaction_commit_dirty(); |
633 | } | 675 | } |
@@ -637,7 +679,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { | |||
637 | wl_container_of(listener, xwayland_view, set_title); | 679 | wl_container_of(listener, xwayland_view, set_title); |
638 | struct sway_view *view = &xwayland_view->view; | 680 | struct sway_view *view = &xwayland_view->view; |
639 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 681 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
640 | if (!xsurface->mapped) { | 682 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
641 | return; | 683 | return; |
642 | } | 684 | } |
643 | view_update_title(view, false); | 685 | view_update_title(view, false); |
@@ -649,7 +691,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) { | |||
649 | wl_container_of(listener, xwayland_view, set_class); | 691 | wl_container_of(listener, xwayland_view, set_class); |
650 | struct sway_view *view = &xwayland_view->view; | 692 | struct sway_view *view = &xwayland_view->view; |
651 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 693 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
652 | if (!xsurface->mapped) { | 694 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
653 | return; | 695 | return; |
654 | } | 696 | } |
655 | view_execute_criteria(view); | 697 | view_execute_criteria(view); |
@@ -660,18 +702,43 @@ static void handle_set_role(struct wl_listener *listener, void *data) { | |||
660 | wl_container_of(listener, xwayland_view, set_role); | 702 | wl_container_of(listener, xwayland_view, set_role); |
661 | struct sway_view *view = &xwayland_view->view; | 703 | struct sway_view *view = &xwayland_view->view; |
662 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 704 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
663 | if (!xsurface->mapped) { | 705 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
664 | return; | 706 | return; |
665 | } | 707 | } |
666 | view_execute_criteria(view); | 708 | view_execute_criteria(view); |
667 | } | 709 | } |
668 | 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 | |||
669 | 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) { |
670 | struct sway_xwayland_view *xwayland_view = | 737 | struct sway_xwayland_view *xwayland_view = |
671 | wl_container_of(listener, xwayland_view, set_window_type); | 738 | wl_container_of(listener, xwayland_view, set_window_type); |
672 | struct sway_view *view = &xwayland_view->view; | 739 | struct sway_view *view = &xwayland_view->view; |
673 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 740 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
674 | if (!xsurface->mapped) { | 741 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
675 | return; | 742 | return; |
676 | } | 743 | } |
677 | view_execute_criteria(view); | 744 | view_execute_criteria(view); |
@@ -682,7 +749,7 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { | |||
682 | wl_container_of(listener, xwayland_view, set_hints); | 749 | wl_container_of(listener, xwayland_view, set_hints); |
683 | struct sway_view *view = &xwayland_view->view; | 750 | struct sway_view *view = &xwayland_view->view; |
684 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 751 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
685 | if (!xsurface->mapped) { | 752 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
686 | return; | 753 | return; |
687 | } | 754 | } |
688 | const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); | 755 | const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); |
@@ -697,6 +764,24 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { | |||
697 | } | 764 | } |
698 | } | 765 | } |
699 | 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 | |||
700 | struct sway_view *view_from_wlr_xwayland_surface( | 785 | struct sway_view *view_from_wlr_xwayland_surface( |
701 | struct wlr_xwayland_surface *xsurface) { | 786 | struct wlr_xwayland_surface *xsurface) { |
702 | return xsurface->data; | 787 | return xsurface->data; |
@@ -712,7 +797,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
712 | return NULL; | 797 | return NULL; |
713 | } | 798 | } |
714 | 799 | ||
715 | 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 | } | ||
716 | xwayland_view->view.wlr_xwayland_surface = xsurface; | 804 | xwayland_view->view.wlr_xwayland_surface = xsurface; |
717 | 805 | ||
718 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); | 806 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); |
@@ -751,6 +839,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
751 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); | 839 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); |
752 | xwayland_view->set_role.notify = handle_set_role; | 840 | xwayland_view->set_role.notify = handle_set_role; |
753 | 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 | |||
754 | wl_signal_add(&xsurface->events.set_window_type, | 846 | wl_signal_add(&xsurface->events.set_window_type, |
755 | &xwayland_view->set_window_type); | 847 | &xwayland_view->set_window_type); |
756 | xwayland_view->set_window_type.notify = handle_set_window_type; | 848 | xwayland_view->set_window_type.notify = handle_set_window_type; |
@@ -762,11 +854,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
762 | &xwayland_view->set_decorations); | 854 | &xwayland_view->set_decorations); |
763 | xwayland_view->set_decorations.notify = handle_set_decorations; | 855 | xwayland_view->set_decorations.notify = handle_set_decorations; |
764 | 856 | ||
765 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); | 857 | wl_signal_add(&xsurface->events.associate, &xwayland_view->associate); |
766 | xwayland_view->unmap.notify = handle_unmap; | 858 | xwayland_view->associate.notify = handle_associate; |
767 | 859 | ||
768 | wl_signal_add(&xsurface->events.map, &xwayland_view->map); | 860 | wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate); |
769 | xwayland_view->map.notify = handle_map; | 861 | xwayland_view->dissociate.notify = handle_dissociate; |
770 | 862 | ||
771 | wl_signal_add(&xsurface->events.set_override_redirect, | 863 | wl_signal_add(&xsurface->events.set_override_redirect, |
772 | &xwayland_view->override_redirect); | 864 | &xwayland_view->override_redirect); |
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 2ee63124..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,98 @@ 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 | // find the output the cursor is on | 47 | struct wlr_scene_node *scene_node = NULL; |
87 | struct wlr_output *wlr_output = wlr_output_layout_output_at( | ||
88 | root->output_layout, lx, ly); | ||
89 | if (wlr_output == NULL) { | ||
90 | return NULL; | ||
91 | } | ||
92 | struct sway_output *output = wlr_output->data; | ||
93 | if (!output || !output->enabled) { | ||
94 | // output is being destroyed or is being enabled | ||
95 | return NULL; | ||
96 | } | ||
97 | double ox = lx, oy = ly; | ||
98 | wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); | ||
99 | 48 | ||
100 | // layer surfaces on the overlay layer are rendered on top | 49 | struct wlr_scene_node *node; |
101 | if ((*surface = layer_surface_at(output, | 50 | wl_list_for_each_reverse(node, &root->layer_tree->children, link) { |
102 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 51 | struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node); |
103 | ox, oy, sx, sy))) { | ||
104 | return NULL; | ||
105 | } | ||
106 | 52 | ||
107 | // check for unmanaged views | 53 | bool non_interactive = scene_descriptor_try_get(&layer->node, |
108 | #if HAVE_XWAYLAND | 54 | SWAY_SCENE_DESC_NON_INTERACTIVE); |
109 | struct wl_list *unmanaged = &root->xwayland_unmanaged; | 55 | if (non_interactive) { |
110 | struct sway_xwayland_unmanaged *unmanaged_surface; | 56 | continue; |
111 | wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { | ||
112 | struct wlr_xwayland_surface *xsurface = | ||
113 | unmanaged_surface->wlr_xwayland_surface; | ||
114 | |||
115 | double _sx = lx - unmanaged_surface->lx; | ||
116 | double _sy = ly - unmanaged_surface->ly; | ||
117 | if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { | ||
118 | *surface = xsurface->surface; | ||
119 | *sx = _sx; | ||
120 | *sy = _sy; | ||
121 | return NULL; | ||
122 | } | 57 | } |
123 | } | ||
124 | #endif | ||
125 | 58 | ||
126 | if (root->fullscreen_global) { | 59 | scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy); |
127 | // Try fullscreen container | 60 | if (scene_node) { |
128 | struct sway_container *con = tiling_container_at( | 61 | break; |
129 | &root->fullscreen_global->node, lx, ly, surface, sx, sy); | ||
130 | if (con) { | ||
131 | return &con->node; | ||
132 | } | 62 | } |
133 | return NULL; | ||
134 | } | 63 | } |
135 | 64 | ||
136 | // find the focused workspace on the output for this seat | 65 | if (scene_node) { |
137 | struct sway_workspace *ws = output_get_active_workspace(output); | 66 | // determine what wlr_surface we clicked on |
138 | if (!ws) { | 67 | if (scene_node->type == WLR_SCENE_NODE_BUFFER) { |
139 | return NULL; | 68 | struct wlr_scene_buffer *scene_buffer = |
140 | } | 69 | wlr_scene_buffer_from_node(scene_node); |
70 | struct wlr_scene_surface *scene_surface = | ||
71 | wlr_scene_surface_try_from_buffer(scene_buffer); | ||
141 | 72 | ||
142 | if (ws->fullscreen) { | 73 | if (scene_surface) { |
143 | // Try transient containers | 74 | *surface = scene_surface->surface; |
144 | for (int i = 0; i < ws->floating->length; ++i) { | ||
145 | struct sway_container *floater = ws->floating->items[i]; | ||
146 | if (container_is_transient_for(floater, ws->fullscreen)) { | ||
147 | struct sway_container *con = tiling_container_at( | ||
148 | &floater->node, lx, ly, surface, sx, sy); | ||
149 | if (con) { | ||
150 | return &con->node; | ||
151 | } | ||
152 | } | 75 | } |
153 | } | 76 | } |
154 | // Try fullscreen container | 77 | |
155 | struct sway_container *con = | 78 | // determine what container we clicked on |
156 | tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); | 79 | struct wlr_scene_node *current = scene_node; |
157 | if (con) { | 80 | while (true) { |
158 | return &con->node; | 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 | } | ||
114 | #endif | ||
115 | |||
116 | if (!current->parent) { | ||
117 | break; | ||
118 | } | ||
119 | |||
120 | current = ¤t->parent->node; | ||
159 | } | 121 | } |
160 | return NULL; | ||
161 | } | ||
162 | if ((*surface = layer_surface_popup_at(output, | ||
163 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
164 | ox, oy, sx, sy))) { | ||
165 | return NULL; | ||
166 | } | ||
167 | if ((*surface = layer_surface_popup_at(output, | ||
168 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
169 | ox, oy, sx, sy))) { | ||
170 | return NULL; | ||
171 | } | ||
172 | if ((*surface = layer_surface_popup_at(output, | ||
173 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
174 | ox, oy, sx, sy))) { | ||
175 | return NULL; | ||
176 | } | ||
177 | if ((*surface = layer_surface_at(output, | ||
178 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
179 | ox, oy, sx, sy))) { | ||
180 | return NULL; | ||
181 | } | 122 | } |
182 | 123 | ||
183 | struct sway_container *c; | 124 | // if we aren't on a container, determine what workspace we are on |
184 | if ((c = container_at(ws, lx, ly, surface, sx, sy))) { | 125 | struct wlr_output *wlr_output = wlr_output_layout_output_at( |
185 | return &c->node; | 126 | root->output_layout, lx, ly); |
127 | if (wlr_output == NULL) { | ||
128 | return NULL; | ||
186 | } | 129 | } |
187 | 130 | ||
188 | if ((*surface = layer_surface_at(output, | 131 | struct sway_output *output = wlr_output->data; |
189 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | 132 | if (!output || !output->enabled) { |
190 | ox, oy, sx, sy))) { | 133 | // output is being destroyed or is being enabled |
191 | return NULL; | 134 | return NULL; |
192 | } | 135 | } |
193 | if ((*surface = layer_surface_at(output, | 136 | |
194 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | 137 | struct sway_workspace *ws = output_get_active_workspace(output); |
195 | ox, oy, sx, sy))) { | 138 | if (!ws) { |
196 | return NULL; | 139 | return NULL; |
197 | } | 140 | } |
198 | 141 | ||
@@ -221,7 +164,7 @@ void cursor_update_image(struct sway_cursor *cursor, | |||
221 | // Try a node's resize edge | 164 | // Try a node's resize edge |
222 | 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); |
223 | if (edge == WLR_EDGE_NONE) { | 166 | if (edge == WLR_EDGE_NONE) { |
224 | cursor_set_image(cursor, "left_ptr", NULL); | 167 | cursor_set_image(cursor, "default", NULL); |
225 | } else if (container_is_floating(node->sway_container)) { | 168 | } else if (container_is_floating(node->sway_container)) { |
226 | cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); | 169 | cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); |
227 | } else { | 170 | } else { |
@@ -232,12 +175,12 @@ void cursor_update_image(struct sway_cursor *cursor, | |||
232 | } | 175 | } |
233 | } | 176 | } |
234 | } else { | 177 | } else { |
235 | cursor_set_image(cursor, "left_ptr", NULL); | 178 | cursor_set_image(cursor, "default", NULL); |
236 | } | 179 | } |
237 | } | 180 | } |
238 | 181 | ||
239 | static void cursor_hide(struct sway_cursor *cursor) { | 182 | static void cursor_hide(struct sway_cursor *cursor) { |
240 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | 183 | wlr_cursor_unset_image(cursor->cursor); |
241 | cursor->hidden = true; | 184 | cursor->hidden = true; |
242 | wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); | 185 | wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); |
243 | } | 186 | } |
@@ -300,7 +243,7 @@ static enum sway_input_idle_source idle_source_from_device( | |||
300 | return IDLE_SOURCE_POINTER; | 243 | return IDLE_SOURCE_POINTER; |
301 | case WLR_INPUT_DEVICE_TOUCH: | 244 | case WLR_INPUT_DEVICE_TOUCH: |
302 | return IDLE_SOURCE_TOUCH; | 245 | return IDLE_SOURCE_TOUCH; |
303 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 246 | case WLR_INPUT_DEVICE_TABLET: |
304 | return IDLE_SOURCE_TABLET_TOOL; | 247 | return IDLE_SOURCE_TABLET_TOOL; |
305 | case WLR_INPUT_DEVICE_TABLET_PAD: | 248 | case WLR_INPUT_DEVICE_TABLET_PAD: |
306 | return IDLE_SOURCE_TABLET_PAD; | 249 | return IDLE_SOURCE_TABLET_PAD; |
@@ -349,7 +292,7 @@ void cursor_unhide(struct sway_cursor *cursor) { | |||
349 | 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)); |
350 | } | 293 | } |
351 | 294 | ||
352 | static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | 295 | void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, |
353 | struct wlr_input_device *device, double dx, double dy, | 296 | struct wlr_input_device *device, double dx, double dy, |
354 | double dx_unaccel, double dy_unaccel) { | 297 | double dx_unaccel, double dy_unaccel) { |
355 | wlr_relative_pointer_manager_v1_send_relative_motion( | 298 | wlr_relative_pointer_manager_v1_send_relative_motion( |
@@ -413,7 +356,7 @@ static void handle_pointer_motion_absolute( | |||
413 | 356 | ||
414 | void dispatch_cursor_button(struct sway_cursor *cursor, | 357 | void dispatch_cursor_button(struct sway_cursor *cursor, |
415 | 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, |
416 | enum wlr_button_state state) { | 359 | enum wl_pointer_button_state state) { |
417 | if (time_msec == 0) { | 360 | if (time_msec == 0) { |
418 | time_msec = get_current_time_msec(); | 361 | time_msec = get_current_time_msec(); |
419 | } | 362 | } |
@@ -425,7 +368,7 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) { | |||
425 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); | 368 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); |
426 | struct wlr_pointer_button_event *event = data; | 369 | struct wlr_pointer_button_event *event = data; |
427 | 370 | ||
428 | if (event->state == WLR_BUTTON_PRESSED) { | 371 | if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) { |
429 | cursor->pressed_button_count++; | 372 | cursor->pressed_button_count++; |
430 | } else { | 373 | } else { |
431 | if (cursor->pressed_button_count > 0) { | 374 | if (cursor->pressed_button_count > 0) { |
@@ -464,43 +407,16 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { | |||
464 | cursor_hide(cursor); | 407 | cursor_hide(cursor); |
465 | 408 | ||
466 | struct sway_seat *seat = cursor->seat; | 409 | struct sway_seat *seat = cursor->seat; |
467 | struct wlr_seat *wlr_seat = seat->wlr_seat; | ||
468 | struct wlr_surface *surface = NULL; | ||
469 | 410 | ||
470 | double lx, ly; | 411 | double lx, ly; |
471 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, | 412 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, |
472 | event->x, event->y, &lx, &ly); | 413 | event->x, event->y, &lx, &ly); |
473 | double sx, sy; | ||
474 | struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy); | ||
475 | 414 | ||
476 | seat->touch_id = event->touch_id; | 415 | seat->touch_id = event->touch_id; |
477 | seat->touch_x = lx; | 416 | seat->touch_x = lx; |
478 | seat->touch_y = ly; | 417 | seat->touch_y = ly; |
479 | 418 | ||
480 | if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { | 419 | seatop_touch_down(seat, event, lx, ly); |
481 | if (seat_is_input_allowed(seat, surface)) { | ||
482 | wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, | ||
483 | event->touch_id, sx, sy); | ||
484 | |||
485 | if (focused_node) { | ||
486 | seat_set_focus(seat, focused_node); | ||
487 | } | ||
488 | } | ||
489 | } else if (!cursor->simulating_pointer_from_touch && | ||
490 | (!surface || seat_is_input_allowed(seat, surface))) { | ||
491 | // Fallback to cursor simulation. | ||
492 | // The pointer_touch_id state is needed, so drags are not aborted when over | ||
493 | // a surface supporting touch and multi touch events don't interfere. | ||
494 | cursor->simulating_pointer_from_touch = true; | ||
495 | cursor->pointer_touch_id = seat->touch_id; | ||
496 | double dx, dy; | ||
497 | dx = lx - cursor->cursor->x; | ||
498 | dy = ly - cursor->cursor->y; | ||
499 | pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy, | ||
500 | dx, dy); | ||
501 | dispatch_cursor_button(cursor, &event->touch->base, event->time_msec, | ||
502 | BTN_LEFT, WLR_BUTTON_PRESSED); | ||
503 | } | ||
504 | } | 420 | } |
505 | 421 | ||
506 | static void handle_touch_up(struct wl_listener *listener, void *data) { | 422 | static void handle_touch_up(struct wl_listener *listener, void *data) { |
@@ -508,16 +424,34 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { | |||
508 | struct wlr_touch_up_event *event = data; | 424 | struct wlr_touch_up_event *event = data; |
509 | cursor_handle_activity_from_device(cursor, &event->touch->base); | 425 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
510 | 426 | ||
511 | struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; | 427 | struct sway_seat *seat = cursor->seat; |
512 | 428 | ||
513 | if (cursor->simulating_pointer_from_touch) { | 429 | if (cursor->simulating_pointer_from_touch) { |
514 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { | 430 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { |
515 | cursor->pointer_touch_up = true; | 431 | cursor->pointer_touch_up = true; |
516 | dispatch_cursor_button(cursor, &event->touch->base, | 432 | dispatch_cursor_button(cursor, &event->touch->base, |
517 | event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); | 433 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); |
518 | } | 434 | } |
519 | } else { | 435 | } else { |
520 | wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); | 436 | seatop_touch_up(seat, event); |
437 | } | ||
438 | } | ||
439 | |||
440 | static void handle_touch_cancel(struct wl_listener *listener, void *data) { | ||
441 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel); | ||
442 | struct wlr_touch_cancel_event *event = data; | ||
443 | cursor_handle_activity_from_device(cursor, &event->touch->base); | ||
444 | |||
445 | struct sway_seat *seat = cursor->seat; | ||
446 | |||
447 | if (cursor->simulating_pointer_from_touch) { | ||
448 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { | ||
449 | cursor->pointer_touch_up = true; | ||
450 | dispatch_cursor_button(cursor, &event->touch->base, | ||
451 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); | ||
452 | } | ||
453 | } else { | ||
454 | seatop_touch_cancel(seat, event); | ||
521 | } | 455 | } |
522 | } | 456 | } |
523 | 457 | ||
@@ -528,25 +462,16 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { | |||
528 | cursor_handle_activity_from_device(cursor, &event->touch->base); | 462 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
529 | 463 | ||
530 | struct sway_seat *seat = cursor->seat; | 464 | struct sway_seat *seat = cursor->seat; |
531 | struct wlr_seat *wlr_seat = seat->wlr_seat; | ||
532 | struct wlr_surface *surface = NULL; | ||
533 | 465 | ||
534 | double lx, ly; | 466 | double lx, ly; |
535 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, | 467 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, |
536 | event->x, event->y, &lx, &ly); | 468 | event->x, event->y, &lx, &ly); |
537 | double sx, sy; | ||
538 | node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); | ||
539 | 469 | ||
540 | if (seat->touch_id == event->touch_id) { | 470 | if (seat->touch_id == event->touch_id) { |
541 | seat->touch_x = lx; | 471 | seat->touch_x = lx; |
542 | seat->touch_y = ly; | 472 | seat->touch_y = ly; |
543 | 473 | ||
544 | struct sway_drag_icon *drag_icon; | 474 | drag_icons_update_position(seat); |
545 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
546 | if (drag_icon->seat == seat) { | ||
547 | drag_icon_update_position(drag_icon); | ||
548 | } | ||
549 | } | ||
550 | } | 475 | } |
551 | 476 | ||
552 | if (cursor->simulating_pointer_from_touch) { | 477 | if (cursor->simulating_pointer_from_touch) { |
@@ -557,9 +482,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { | |||
557 | pointer_motion(cursor, event->time_msec, &event->touch->base, | 482 | pointer_motion(cursor, event->time_msec, &event->touch->base, |
558 | dx, dy, dx, dy); | 483 | dx, dy, dx, dy); |
559 | } | 484 | } |
560 | } else if (surface) { | 485 | } else { |
561 | wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, | 486 | seatop_touch_motion(seat, event, lx, ly); |
562 | event->touch_id, sx, sy); | ||
563 | } | 487 | } |
564 | } | 488 | } |
565 | 489 | ||
@@ -594,7 +518,7 @@ static void apply_mapping_from_region(struct wlr_input_device *device, | |||
594 | double x1 = region->x1, x2 = region->x2; | 518 | double x1 = region->x1, x2 = region->x2; |
595 | double y1 = region->y1, y2 = region->y2; | 519 | double y1 = region->y1, y2 = region->y2; |
596 | 520 | ||
597 | if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { | 521 | if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) { |
598 | struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); | 522 | struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); |
599 | if (tablet->width_mm == 0 || tablet->height_mm == 0) { | 523 | if (tablet->width_mm == 0 || tablet->height_mm == 0) { |
600 | return; | 524 | return; |
@@ -737,7 +661,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { | |||
737 | event->state == WLR_TABLET_TOOL_TIP_UP) { | 661 | event->state == WLR_TABLET_TOOL_TIP_UP) { |
738 | cursor->simulating_pointer_from_tool_tip = false; | 662 | cursor->simulating_pointer_from_tool_tip = false; |
739 | dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, | 663 | dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, |
740 | BTN_LEFT, WLR_BUTTON_RELEASED); | 664 | BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); |
741 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 665 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
742 | } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { | 666 | } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { |
743 | // 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 |
@@ -749,7 +673,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { | |||
749 | } else { | 673 | } else { |
750 | cursor->simulating_pointer_from_tool_tip = true; | 674 | cursor->simulating_pointer_from_tool_tip = true; |
751 | dispatch_cursor_button(cursor, &event->tablet->base, | 675 | dispatch_cursor_button(cursor, &event->tablet->base, |
752 | event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED); | 676 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); |
753 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 677 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
754 | } | 678 | } |
755 | } else { | 679 | } else { |
@@ -818,31 +742,71 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { | |||
818 | node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, | 742 | node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, |
819 | &surface, &sx, &sy); | 743 | &surface, &sx, &sy); |
820 | 744 | ||
821 | 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 | |||
822 | // 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 |
823 | // which simulated pointer buttons | 774 | // which simulated pointer buttons |
824 | switch (event->state) { | 775 | switch (event->state) { |
825 | case WLR_BUTTON_PRESSED: | 776 | case WLR_BUTTON_PRESSED: |
826 | if (cursor->tool_buttons == 0) { | 777 | if (cursor->tool_buttons == 0) { |
827 | dispatch_cursor_button(cursor, &event->tablet->base, | 778 | dispatch_cursor_button(cursor, &event->tablet->base, |
828 | event->time_msec, BTN_RIGHT, event->state); | 779 | event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED); |
829 | } | 780 | } |
830 | cursor->tool_buttons++; | ||
831 | break; | 781 | break; |
832 | case WLR_BUTTON_RELEASED: | 782 | case WLR_BUTTON_RELEASED: |
833 | if (cursor->tool_buttons == 1) { | 783 | if (cursor->tool_buttons <= 1) { |
834 | dispatch_cursor_button(cursor, &event->tablet->base, | 784 | dispatch_cursor_button(cursor, &event->tablet->base, |
835 | event->time_msec, BTN_RIGHT, event->state); | 785 | event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED); |
836 | } | 786 | } |
837 | cursor->tool_buttons--; | ||
838 | break; | 787 | break; |
839 | } | 788 | } |
840 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 789 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
841 | 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); | ||
842 | } | 795 | } |
843 | 796 | ||
844 | wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, | 797 | // Update tool button count. |
845 | 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 | } | ||
846 | } | 810 | } |
847 | 811 | ||
848 | static void check_constraint_region(struct sway_cursor *cursor) { | 812 | static void check_constraint_region(struct sway_cursor *cursor) { |
@@ -1028,10 +992,9 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image, | |||
1028 | } | 992 | } |
1029 | 993 | ||
1030 | if (!image) { | 994 | if (!image) { |
1031 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | 995 | wlr_cursor_unset_image(cursor->cursor); |
1032 | } else if (!current_image || strcmp(current_image, image) != 0) { | 996 | } else if (!current_image || strcmp(current_image, image) != 0) { |
1033 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, | 997 | wlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image); |
1034 | cursor->cursor); | ||
1035 | } | 998 | } |
1036 | } | 999 | } |
1037 | 1000 | ||
@@ -1078,6 +1041,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { | |||
1078 | wl_list_remove(&cursor->frame.link); | 1041 | wl_list_remove(&cursor->frame.link); |
1079 | wl_list_remove(&cursor->touch_down.link); | 1042 | wl_list_remove(&cursor->touch_down.link); |
1080 | wl_list_remove(&cursor->touch_up.link); | 1043 | wl_list_remove(&cursor->touch_up.link); |
1044 | wl_list_remove(&cursor->touch_cancel.link); | ||
1081 | wl_list_remove(&cursor->touch_motion.link); | 1045 | wl_list_remove(&cursor->touch_motion.link); |
1082 | wl_list_remove(&cursor->touch_frame.link); | 1046 | wl_list_remove(&cursor->touch_frame.link); |
1083 | wl_list_remove(&cursor->tool_axis.link); | 1047 | wl_list_remove(&cursor->tool_axis.link); |
@@ -1114,9 +1078,6 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
1114 | wl_list_init(&cursor->image_surface_destroy.link); | 1078 | wl_list_init(&cursor->image_surface_destroy.link); |
1115 | cursor->image_surface_destroy.notify = handle_image_surface_destroy; | 1079 | cursor->image_surface_destroy.notify = handle_image_surface_destroy; |
1116 | 1080 | ||
1117 | // gesture events | ||
1118 | cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); | ||
1119 | |||
1120 | wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin); | 1081 | wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin); |
1121 | cursor->hold_begin.notify = handle_pointer_hold_begin; | 1082 | cursor->hold_begin.notify = handle_pointer_hold_begin; |
1122 | wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end); | 1083 | wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end); |
@@ -1159,6 +1120,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
1159 | wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); | 1120 | wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); |
1160 | cursor->touch_up.notify = handle_touch_up; | 1121 | cursor->touch_up.notify = handle_touch_up; |
1161 | 1122 | ||
1123 | wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel); | ||
1124 | cursor->touch_cancel.notify = handle_touch_cancel; | ||
1125 | |||
1162 | wl_signal_add(&wlr_cursor->events.touch_motion, | 1126 | wl_signal_add(&wlr_cursor->events.touch_motion, |
1163 | &cursor->touch_motion); | 1127 | &cursor->touch_motion); |
1164 | cursor->touch_motion.notify = handle_touch_motion; | 1128 | cursor->touch_motion.notify = handle_touch_motion; |
@@ -1251,11 +1215,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) { | |||
1251 | // Get event code from name | 1215 | // Get event code from name |
1252 | int code = libevdev_event_code_from_name(EV_KEY, name); | 1216 | int code = libevdev_event_code_from_name(EV_KEY, name); |
1253 | if (code == -1) { | 1217 | if (code == -1) { |
1254 | size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; | 1218 | *error = format_str("Unknown event %s", name); |
1255 | *error = malloc(len); | ||
1256 | if (*error) { | ||
1257 | snprintf(*error, len, "Unknown event %s", name); | ||
1258 | } | ||
1259 | return 0; | 1219 | return 0; |
1260 | } | 1220 | } |
1261 | return code; | 1221 | return code; |
@@ -1277,13 +1237,8 @@ uint32_t get_mouse_bindcode(const char *name, char **error) { | |||
1277 | } | 1237 | } |
1278 | const char *event = libevdev_event_code_get_name(EV_KEY, code); | 1238 | const char *event = libevdev_event_code_get_name(EV_KEY, code); |
1279 | if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { | 1239 | if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { |
1280 | 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", |
1281 | code, event ? event : "(null)") + 1; | 1241 | code, event ? event : "(null)"); |
1282 | *error = malloc(len); | ||
1283 | if (*error) { | ||
1284 | snprintf(*error, len, "Event code %d (%s) is not a button", | ||
1285 | code, event ? event : "(null)"); | ||
1286 | } | ||
1287 | return 0; | 1242 | return 0; |
1288 | } | 1243 | } |
1289 | return code; | 1244 | return code; |
@@ -1316,12 +1271,15 @@ const char *get_mouse_button_name(uint32_t button) { | |||
1316 | static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { | 1271 | static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { |
1317 | struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; | 1272 | struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; |
1318 | 1273 | ||
1319 | if (constraint->current.committed & | 1274 | if (constraint->current.cursor_hint.enabled) { |
1320 | WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { | ||
1321 | double sx = constraint->current.cursor_hint.x; | 1275 | double sx = constraint->current.cursor_hint.x; |
1322 | double sy = constraint->current.cursor_hint.y; | 1276 | double sy = constraint->current.cursor_hint.y; |
1323 | 1277 | ||
1324 | 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 | |||
1325 | struct sway_container *con = view->container; | 1283 | struct sway_container *con = view->container; |
1326 | 1284 | ||
1327 | double lx = sx + con->pending.content_x - view->geometry.x; | 1285 | double lx = sx + con->pending.content_x - view->geometry.x; |
@@ -1372,12 +1330,9 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) { | |||
1372 | sway_constraint->destroy.notify = handle_constraint_destroy; | 1330 | sway_constraint->destroy.notify = handle_constraint_destroy; |
1373 | wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); | 1331 | wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); |
1374 | 1332 | ||
1375 | struct sway_node *focus = seat_get_focus(seat); | 1333 | struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; |
1376 | if (focus && node_is_view(focus)) { | 1334 | if (surface && surface == constraint->surface) { |
1377 | struct wlr_surface *surface = focus->sway_container->view->surface; | 1335 | sway_cursor_constrain(seat->cursor, constraint); |
1378 | if (surface == constraint->surface) { | ||
1379 | sway_cursor_constrain(seat->cursor, constraint); | ||
1380 | } | ||
1381 | } | 1336 | } |
1382 | } | 1337 | } |
1383 | 1338 | ||
@@ -1435,3 +1390,26 @@ void sway_cursor_constrain(struct sway_cursor *cursor, | |||
1435 | wl_signal_add(&constraint->surface->events.commit, | 1390 | wl_signal_add(&constraint->surface->events.commit, |
1436 | &cursor->constraint_commit); | 1391 | &cursor->constraint_commit); |
1437 | } | 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 4a0bce0e..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,33 +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 | |||
279 | static void handle_inhibit_activate(struct wl_listener *listener, void *data) { | ||
280 | struct sway_input_manager *input_manager = wl_container_of( | ||
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 | ||
288 | static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) { | 289 | if (config_changed) { |
289 | struct sway_input_manager *input_manager = wl_container_of( | 290 | ipc_event_input("libinput_config", input_device); |
290 | listener, input_manager, inhibit_deactivate); | ||
291 | struct sway_seat *seat; | ||
292 | if (server.session_lock.locked) { | ||
293 | // Don't deactivate the grab of a screenlocker | ||
294 | return; | ||
295 | } | ||
296 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
297 | seat_set_exclusive_client(seat, NULL); | ||
298 | struct sway_node *previous = seat_get_focus(seat); | ||
299 | if (previous) { | ||
300 | // Hack to get seat to re-focus the return value of get_focus | ||
301 | seat_set_focus(seat, NULL); | ||
302 | seat_set_focus(seat, previous); | ||
303 | } | ||
304 | } | 291 | } |
305 | } | 292 | } |
306 | 293 | ||
@@ -446,6 +433,20 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) { | |||
446 | } | 433 | } |
447 | } | 434 | } |
448 | 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 | |||
449 | struct sway_input_manager *input_manager_create(struct sway_server *server) { | 450 | struct sway_input_manager *input_manager_create(struct sway_server *server) { |
450 | struct sway_input_manager *input = | 451 | struct sway_input_manager *input = |
451 | calloc(1, sizeof(struct sway_input_manager)); | 452 | calloc(1, sizeof(struct sway_input_manager)); |
@@ -472,14 +473,6 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { | |||
472 | &input->virtual_pointer_new); | 473 | &input->virtual_pointer_new); |
473 | input->virtual_pointer_new.notify = handle_virtual_pointer; | 474 | input->virtual_pointer_new.notify = handle_virtual_pointer; |
474 | 475 | ||
475 | input->inhibit = wlr_input_inhibit_manager_create(server->wl_display); | ||
476 | input->inhibit_activate.notify = handle_inhibit_activate; | ||
477 | wl_signal_add(&input->inhibit->events.activate, | ||
478 | &input->inhibit_activate); | ||
479 | input->inhibit_deactivate.notify = handle_inhibit_deactivate; | ||
480 | wl_signal_add(&input->inhibit->events.deactivate, | ||
481 | &input->inhibit_deactivate); | ||
482 | |||
483 | input->keyboard_shortcuts_inhibit = | 476 | input->keyboard_shortcuts_inhibit = |
484 | wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); | 477 | wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); |
485 | input->keyboard_shortcuts_inhibit_new_inhibitor.notify = | 478 | input->keyboard_shortcuts_inhibit_new_inhibitor.notify = |
@@ -487,6 +480,17 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { | |||
487 | wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, | 480 | wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, |
488 | &input->keyboard_shortcuts_inhibit_new_inhibitor); | 481 | &input->keyboard_shortcuts_inhibit_new_inhibitor); |
489 | 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 | |||
490 | return input; | 494 | return input; |
491 | } | 495 | } |
492 | 496 | ||
@@ -524,21 +528,50 @@ static void retranslate_keysyms(struct input_config *input_config) { | |||
524 | return; | 528 | return; |
525 | } | 529 | } |
526 | } | 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 | } | ||
527 | } | 543 | } |
528 | 544 | ||
529 | static void input_manager_configure_input( | 545 | static void input_manager_configure_input( |
530 | struct sway_input_device *input_device) { | 546 | struct sway_input_device *input_device) { |
531 | 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 | ||
532 | struct sway_seat *seat = NULL; | 552 | struct sway_seat *seat = NULL; |
533 | wl_list_for_each(seat, &server.input->seats, link) { | 553 | wl_list_for_each(seat, &server.input->seats, link) { |
534 | seat_configure_device(seat, input_device); | 554 | seat_configure_device(seat, input_device); |
535 | } | 555 | } |
556 | if (config_changed) { | ||
557 | ipc_event_input("libinput_config", input_device); | ||
558 | } | ||
536 | } | 559 | } |
537 | 560 | ||
538 | void input_manager_configure_all_inputs(void) { | 561 | void input_manager_configure_all_input_mappings(void) { |
539 | struct sway_input_device *input_device = NULL; | 562 | struct sway_input_device *input_device; |
540 | wl_list_for_each(input_device, &server.input->devices, link) { | 563 | wl_list_for_each(input_device, &server.input->devices, link) { |
541 | 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 | ||
542 | } | 575 | } |
543 | } | 576 | } |
544 | 577 | ||
@@ -560,7 +593,9 @@ void input_manager_apply_input_config(struct input_config *input_config) { | |||
560 | } | 593 | } |
561 | 594 | ||
562 | 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 | ||
563 | sway_input_reset_libinput_device(input_device); | 597 | sway_input_reset_libinput_device(input_device); |
598 | #endif | ||
564 | struct sway_seat *seat = NULL; | 599 | struct sway_seat *seat = NULL; |
565 | wl_list_for_each(seat, &server.input->seats, link) { | 600 | wl_list_for_each(seat, &server.input->seats, link) { |
566 | seat_reset_device(seat, input_device); | 601 | seat_reset_device(seat, input_device); |
@@ -568,6 +603,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) { | |||
568 | } | 603 | } |
569 | 604 | ||
570 | 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 | |||
571 | struct sway_input_device *input_device = NULL; | 613 | struct sway_input_device *input_device = NULL; |
572 | wl_list_for_each(input_device, &server.input->devices, link) { | 614 | wl_list_for_each(input_device, &server.input->devices, link) { |
573 | input_manager_reset_input(input_device); | 615 | input_manager_reset_input(input_device); |
@@ -576,7 +618,6 @@ void input_manager_reset_all_inputs(void) { | |||
576 | // 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, |
577 | // 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 |
578 | // need their keyboard disarmed. | 620 | // need their keyboard disarmed. |
579 | struct sway_seat *seat; | ||
580 | wl_list_for_each(seat, &server.input->seats, link) { | 621 | wl_list_for_each(seat, &server.input->seats, link) { |
581 | struct sway_keyboard_group *group; | 622 | struct sway_keyboard_group *group; |
582 | 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 5e5692f1..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 | } |
@@ -404,8 +406,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
404 | char *device_identifier = input_device_get_identifier(wlr_device); | 406 | char *device_identifier = input_device_get_identifier(wlr_device); |
405 | bool exact_identifier = keyboard->wlr->group != NULL; | 407 | bool exact_identifier = keyboard->wlr->group != NULL; |
406 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); | 408 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); |
407 | bool input_inhibited = seat->exclusive_client != NULL || | 409 | bool locked = server.session_lock.lock; |
408 | server.session_lock.locked; | ||
409 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = | 410 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = |
410 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); | 411 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); |
411 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; | 412 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; |
@@ -423,17 +424,17 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
423 | struct sway_binding *binding_released = NULL; | 424 | struct sway_binding *binding_released = NULL; |
424 | get_active_binding(&keyboard->state_keycodes, | 425 | get_active_binding(&keyboard->state_keycodes, |
425 | config->current_mode->keycode_bindings, &binding_released, | 426 | config->current_mode->keycode_bindings, &binding_released, |
426 | keyinfo.code_modifiers, true, input_inhibited, | 427 | keyinfo.code_modifiers, true, locked, |
427 | shortcuts_inhibited, device_identifier, | 428 | shortcuts_inhibited, device_identifier, |
428 | exact_identifier, keyboard->effective_layout); | 429 | exact_identifier, keyboard->effective_layout); |
429 | get_active_binding(&keyboard->state_keysyms_raw, | 430 | get_active_binding(&keyboard->state_keysyms_raw, |
430 | config->current_mode->keysym_bindings, &binding_released, | 431 | config->current_mode->keysym_bindings, &binding_released, |
431 | keyinfo.raw_modifiers, true, input_inhibited, | 432 | keyinfo.raw_modifiers, true, locked, |
432 | shortcuts_inhibited, device_identifier, | 433 | shortcuts_inhibited, device_identifier, |
433 | exact_identifier, keyboard->effective_layout); | 434 | exact_identifier, keyboard->effective_layout); |
434 | get_active_binding(&keyboard->state_keysyms_translated, | 435 | get_active_binding(&keyboard->state_keysyms_translated, |
435 | config->current_mode->keysym_bindings, &binding_released, | 436 | config->current_mode->keysym_bindings, &binding_released, |
436 | keyinfo.translated_modifiers, true, input_inhibited, | 437 | keyinfo.translated_modifiers, true, locked, |
437 | shortcuts_inhibited, device_identifier, | 438 | shortcuts_inhibited, device_identifier, |
438 | exact_identifier, keyboard->effective_layout); | 439 | exact_identifier, keyboard->effective_layout); |
439 | 440 | ||
@@ -455,17 +456,17 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
455 | if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { | 456 | if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { |
456 | get_active_binding(&keyboard->state_keycodes, | 457 | get_active_binding(&keyboard->state_keycodes, |
457 | config->current_mode->keycode_bindings, &binding, | 458 | config->current_mode->keycode_bindings, &binding, |
458 | keyinfo.code_modifiers, false, input_inhibited, | 459 | keyinfo.code_modifiers, false, locked, |
459 | shortcuts_inhibited, device_identifier, | 460 | shortcuts_inhibited, device_identifier, |
460 | exact_identifier, keyboard->effective_layout); | 461 | exact_identifier, keyboard->effective_layout); |
461 | get_active_binding(&keyboard->state_keysyms_raw, | 462 | get_active_binding(&keyboard->state_keysyms_raw, |
462 | config->current_mode->keysym_bindings, &binding, | 463 | config->current_mode->keysym_bindings, &binding, |
463 | keyinfo.raw_modifiers, false, input_inhibited, | 464 | keyinfo.raw_modifiers, false, locked, |
464 | shortcuts_inhibited, device_identifier, | 465 | shortcuts_inhibited, device_identifier, |
465 | exact_identifier, keyboard->effective_layout); | 466 | exact_identifier, keyboard->effective_layout); |
466 | get_active_binding(&keyboard->state_keysyms_translated, | 467 | get_active_binding(&keyboard->state_keysyms_translated, |
467 | config->current_mode->keysym_bindings, &binding, | 468 | config->current_mode->keysym_bindings, &binding, |
468 | keyinfo.translated_modifiers, false, input_inhibited, | 469 | keyinfo.translated_modifiers, false, locked, |
469 | shortcuts_inhibited, device_identifier, | 470 | shortcuts_inhibited, device_identifier, |
470 | exact_identifier, keyboard->effective_layout); | 471 | exact_identifier, keyboard->effective_layout); |
471 | } | 472 | } |
@@ -715,23 +716,11 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | |||
715 | 716 | ||
716 | static void handle_xkb_context_log(struct xkb_context *context, | 717 | static void handle_xkb_context_log(struct xkb_context *context, |
717 | enum xkb_log_level level, const char *format, va_list args) { | 718 | enum xkb_log_level level, const char *format, va_list args) { |
718 | va_list args_copy; | 719 | char *error = vformat_str(format, args); |
719 | va_copy(args_copy, args); | ||
720 | size_t length = vsnprintf(NULL, 0, format, args_copy) + 1; | ||
721 | va_end(args_copy); | ||
722 | |||
723 | char *error = malloc(length); | ||
724 | if (!error) { | ||
725 | sway_log(SWAY_ERROR, "Failed to allocate libxkbcommon log message"); | ||
726 | return; | ||
727 | } | ||
728 | |||
729 | va_copy(args_copy, args); | ||
730 | vsnprintf(error, length, format, args_copy); | ||
731 | va_end(args_copy); | ||
732 | 720 | ||
733 | if (error[length - 2] == '\n') { | 721 | size_t len = strlen(error); |
734 | error[length - 2] = '\0'; | 722 | if (error[len - 1] == '\n') { |
723 | error[len - 1] = '\0'; | ||
735 | } | 724 | } |
736 | 725 | ||
737 | sway_log_importance_t importance = SWAY_DEBUG; | 726 | sway_log_importance_t importance = SWAY_DEBUG; |
@@ -752,7 +741,7 @@ static void handle_xkb_context_log(struct xkb_context *context, | |||
752 | 741 | ||
753 | struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, | 742 | struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, |
754 | char **error) { | 743 | char **error) { |
755 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | 744 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); |
756 | if (!sway_assert(context, "cannot create XKB context")) { | 745 | if (!sway_assert(context, "cannot create XKB context")) { |
757 | return NULL; | 746 | return NULL; |
758 | } | 747 | } |
@@ -766,13 +755,8 @@ struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, | |||
766 | if (!keymap_file) { | 755 | if (!keymap_file) { |
767 | 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); |
768 | if (error) { | 757 | if (error) { |
769 | size_t len = snprintf(NULL, 0, "cannot read xkb file %s: %s", | 758 | *error = format_str("cannot read xkb file %s: %s", |
770 | ic->xkb_file, strerror(errno)) + 1; | 759 | ic->xkb_file, strerror(errno)); |
771 | *error = malloc(len); | ||
772 | if (*error) { | ||
773 | snprintf(*error, len, "cannot read xkb_file %s: %s", | ||
774 | ic->xkb_file, strerror(errno)); | ||
775 | } | ||
776 | } | 760 | } |
777 | goto cleanup; | 761 | goto cleanup; |
778 | } | 762 | } |
@@ -1068,8 +1052,12 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
1068 | } | 1052 | } |
1069 | } | 1053 | } |
1070 | 1054 | ||
1055 | // If the seat has no active keyboard, set this one | ||
1071 | struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; | 1056 | struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; |
1072 | wlr_seat_set_keyboard(seat, keyboard->wlr); | 1057 | struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard; |
1058 | if (current_keyboard == NULL) { | ||
1059 | wlr_seat_set_keyboard(seat, keyboard->wlr); | ||
1060 | } | ||
1073 | 1061 | ||
1074 | wl_list_remove(&keyboard->keyboard_key.link); | 1062 | wl_list_remove(&keyboard->keyboard_key.link); |
1075 | wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); | 1063 | wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); |
diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 3c0f359d..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 | } | ||
218 | 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); | ||
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); |
diff --git a/sway/input/seat.c b/sway/input/seat.c index b21e1b86..0c5672bc 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -1,12 +1,12 @@ | |||
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> |
@@ -17,7 +17,7 @@ | |||
17 | #include "list.h" | 17 | #include "list.h" |
18 | #include "log.h" | 18 | #include "log.h" |
19 | #include "sway/config.h" | 19 | #include "sway/config.h" |
20 | #include "sway/desktop.h" | 20 | #include "sway/scene_descriptor.h" |
21 | #include "sway/input/cursor.h" | 21 | #include "sway/input/cursor.h" |
22 | #include "sway/input/input-manager.h" | 22 | #include "sway/input/input-manager.h" |
23 | #include "sway/input/keyboard.h" | 23 | #include "sway/input/keyboard.h" |
@@ -67,6 +67,12 @@ static void seat_node_destroy(struct sway_seat_node *seat_node) { | |||
67 | } | 67 | } |
68 | 68 | ||
69 | 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 | |||
70 | if (seat == config->handler_context.seat) { | 76 | if (seat == config->handler_context.seat) { |
71 | config->handler_context.seat = input_manager_get_default_seat(); | 77 | config->handler_context.seat = input_manager_get_default_seat(); |
72 | } | 78 | } |
@@ -87,10 +93,11 @@ void seat_destroy(struct sway_seat *seat) { | |||
87 | wl_list_remove(&seat->request_set_selection.link); | 93 | wl_list_remove(&seat->request_set_selection.link); |
88 | wl_list_remove(&seat->request_set_primary_selection.link); | 94 | wl_list_remove(&seat->request_set_primary_selection.link); |
89 | wl_list_remove(&seat->link); | 95 | wl_list_remove(&seat->link); |
90 | wlr_seat_destroy(seat->wlr_seat); | 96 | wl_list_remove(&seat->destroy.link); |
91 | for (int i = 0; i < seat->deferred_bindings->length; i++) { | 97 | for (int i = 0; i < seat->deferred_bindings->length; i++) { |
92 | free_sway_binding(seat->deferred_bindings->items[i]); | 98 | free_sway_binding(seat->deferred_bindings->items[i]); |
93 | } | 99 | } |
100 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
94 | list_free(seat->deferred_bindings); | 101 | list_free(seat->deferred_bindings); |
95 | free(seat->prev_workspace_name); | 102 | free(seat->prev_workspace_name); |
96 | free(seat); | 103 | free(seat); |
@@ -98,21 +105,10 @@ void seat_destroy(struct sway_seat *seat) { | |||
98 | 105 | ||
99 | void seat_idle_notify_activity(struct sway_seat *seat, | 106 | void seat_idle_notify_activity(struct sway_seat *seat, |
100 | enum sway_input_idle_source source) { | 107 | enum sway_input_idle_source source) { |
101 | uint32_t mask = seat->idle_inhibit_sources; | 108 | if ((source & seat->idle_inhibit_sources) == 0) { |
102 | struct wlr_idle_timeout *timeout; | 109 | return; |
103 | int ntimers = 0, nidle = 0; | ||
104 | wl_list_for_each(timeout, &server.idle->idle_timers, link) { | ||
105 | ++ntimers; | ||
106 | if (timeout->idle_state) { | ||
107 | ++nidle; | ||
108 | } | ||
109 | } | ||
110 | if (nidle == ntimers) { | ||
111 | mask = seat->idle_wake_sources; | ||
112 | } | ||
113 | if ((source & mask) > 0) { | ||
114 | wlr_idle_notify_activity(server.idle, seat->wlr_seat); | ||
115 | } | 110 | } |
111 | wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat); | ||
116 | } | 112 | } |
117 | 113 | ||
118 | /** | 114 | /** |
@@ -174,11 +170,11 @@ static void seat_keyboard_notify_enter(struct sway_seat *seat, | |||
174 | state->pressed_keycodes, state->npressed, &keyboard->modifiers); | 170 | state->pressed_keycodes, state->npressed, &keyboard->modifiers); |
175 | } | 171 | } |
176 | 172 | ||
177 | static void seat_tablet_pads_notify_enter(struct sway_seat *seat, | 173 | static void seat_tablet_pads_set_focus(struct sway_seat *seat, |
178 | struct wlr_surface *surface) { | 174 | struct wlr_surface *surface) { |
179 | struct sway_seat_device *seat_device; | 175 | struct sway_seat_device *seat_device; |
180 | wl_list_for_each(seat_device, &seat->devices, link) { | 176 | wl_list_for_each(seat_device, &seat->devices, link) { |
181 | sway_tablet_pad_notify_enter(seat_device->tablet_pad, surface); | 177 | sway_tablet_pad_set_focus(seat_device->tablet_pad, surface); |
182 | } | 178 | } |
183 | } | 179 | } |
184 | 180 | ||
@@ -202,7 +198,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { | |||
202 | #endif | 198 | #endif |
203 | 199 | ||
204 | seat_keyboard_notify_enter(seat, view->surface); | 200 | seat_keyboard_notify_enter(seat, view->surface); |
205 | seat_tablet_pads_notify_enter(seat, view->surface); | 201 | seat_tablet_pads_set_focus(seat, view->surface); |
206 | sway_input_method_relay_set_focus(&seat->im_relay, view->surface); | 202 | sway_input_method_relay_set_focus(&seat->im_relay, view->surface); |
207 | 203 | ||
208 | struct wlr_pointer_constraint_v1 *constraint = | 204 | struct wlr_pointer_constraint_v1 *constraint = |
@@ -212,15 +208,6 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { | |||
212 | } | 208 | } |
213 | } | 209 | } |
214 | 210 | ||
215 | void sway_force_focus(struct wlr_surface *surface) { | ||
216 | struct sway_seat *seat; | ||
217 | wl_list_for_each(seat, &server.input->seats, link) { | ||
218 | seat_keyboard_notify_enter(seat, surface); | ||
219 | seat_tablet_pads_notify_enter(seat, surface); | ||
220 | sway_input_method_relay_set_focus(&seat->im_relay, surface); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | void seat_for_each_node(struct sway_seat *seat, | 211 | void seat_for_each_node(struct sway_seat *seat, |
225 | void (*f)(struct sway_node *node, void *data), void *data) { | 212 | void (*f)(struct sway_node *node, void *data), void *data) { |
226 | struct sway_seat_node *current = NULL; | 213 | struct sway_seat_node *current = NULL; |
@@ -372,25 +359,15 @@ static void handle_new_node(struct wl_listener *listener, void *data) { | |||
372 | seat_node_from_node(seat, node); | 359 | seat_node_from_node(seat, node); |
373 | } | 360 | } |
374 | 361 | ||
375 | 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) { |
376 | if (!icon->wlr_drag_icon->mapped) { | 363 | struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON); |
377 | return; | ||
378 | } | ||
379 | desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true); | ||
380 | } | ||
381 | |||
382 | void drag_icon_update_position(struct sway_drag_icon *icon) { | ||
383 | drag_icon_damage_whole(icon); | ||
384 | |||
385 | struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; | ||
386 | struct sway_seat *seat = icon->seat; | ||
387 | struct wlr_cursor *cursor = seat->cursor->cursor; | 364 | struct wlr_cursor *cursor = seat->cursor->cursor; |
365 | |||
388 | switch (wlr_icon->drag->grab_type) { | 366 | switch (wlr_icon->drag->grab_type) { |
389 | case WLR_DRAG_GRAB_KEYBOARD: | 367 | case WLR_DRAG_GRAB_KEYBOARD: |
390 | return; | 368 | return; |
391 | case WLR_DRAG_GRAB_KEYBOARD_POINTER: | 369 | case WLR_DRAG_GRAB_KEYBOARD_POINTER: |
392 | icon->x = cursor->x + wlr_icon->surface->sx; | 370 | wlr_scene_node_set_position(node, cursor->x, cursor->y); |
393 | icon->y = cursor->y + wlr_icon->surface->sy; | ||
394 | break; | 371 | break; |
395 | case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; | 372 | case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; |
396 | struct wlr_touch_point *point = | 373 | struct wlr_touch_point *point = |
@@ -398,39 +375,15 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { | |||
398 | if (point == NULL) { | 375 | if (point == NULL) { |
399 | return; | 376 | return; |
400 | } | 377 | } |
401 | icon->x = seat->touch_x + wlr_icon->surface->sx; | 378 | wlr_scene_node_set_position(node, seat->touch_x, seat->touch_y); |
402 | icon->y = seat->touch_y + wlr_icon->surface->sy; | ||
403 | } | 379 | } |
404 | |||
405 | drag_icon_damage_whole(icon); | ||
406 | } | ||
407 | |||
408 | static void drag_icon_handle_surface_commit(struct wl_listener *listener, | ||
409 | void *data) { | ||
410 | struct sway_drag_icon *icon = | ||
411 | wl_container_of(listener, icon, surface_commit); | ||
412 | drag_icon_update_position(icon); | ||
413 | } | ||
414 | |||
415 | static void drag_icon_handle_map(struct wl_listener *listener, void *data) { | ||
416 | struct sway_drag_icon *icon = wl_container_of(listener, icon, map); | ||
417 | drag_icon_damage_whole(icon); | ||
418 | } | 380 | } |
419 | 381 | ||
420 | static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) { | 382 | void drag_icons_update_position(struct sway_seat *seat) { |
421 | struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap); | 383 | struct wlr_scene_node *node; |
422 | drag_icon_damage_whole(icon); | 384 | wl_list_for_each(node, &seat->drag_icons->children, link) { |
423 | } | 385 | drag_icon_update_position(seat, node); |
424 | 386 | } | |
425 | static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { | ||
426 | struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy); | ||
427 | icon->wlr_drag_icon->data = NULL; | ||
428 | wl_list_remove(&icon->link); | ||
429 | wl_list_remove(&icon->surface_commit.link); | ||
430 | wl_list_remove(&icon->unmap.link); | ||
431 | wl_list_remove(&icon->map.link); | ||
432 | wl_list_remove(&icon->destroy.link); | ||
433 | free(icon); | ||
434 | } | 387 | } |
435 | 388 | ||
436 | static void drag_handle_destroy(struct wl_listener *listener, void *data) { | 389 | static void drag_handle_destroy(struct wl_listener *listener, void *data) { |
@@ -502,27 +455,20 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { | |||
502 | 455 | ||
503 | struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; | 456 | struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; |
504 | if (wlr_drag_icon != NULL) { | 457 | if (wlr_drag_icon != NULL) { |
505 | 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); |
506 | if (icon == NULL) { | 459 | if (!tree) { |
507 | sway_log(SWAY_ERROR, "Allocation failed"); | 460 | sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree"); |
508 | return; | 461 | return; |
509 | } | 462 | } |
510 | icon->seat = seat; | ||
511 | icon->wlr_drag_icon = wlr_drag_icon; | ||
512 | wlr_drag_icon->data = icon; | ||
513 | 463 | ||
514 | icon->surface_commit.notify = drag_icon_handle_surface_commit; | 464 | if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON, |
515 | wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); | 465 | wlr_drag_icon)) { |
516 | icon->unmap.notify = drag_icon_handle_unmap; | 466 | sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor"); |
517 | wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); | 467 | wlr_scene_node_destroy(&tree->node); |
518 | icon->map.notify = drag_icon_handle_map; | 468 | return; |
519 | wl_signal_add(&wlr_drag_icon->events.map, &icon->map); | 469 | } |
520 | icon->destroy.notify = drag_icon_handle_destroy; | ||
521 | wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); | ||
522 | |||
523 | wl_list_insert(&root->drag_icons, &icon->link); | ||
524 | 470 | ||
525 | drag_icon_update_position(icon); | 471 | drag_icon_update_position(seat, &tree->node); |
526 | } | 472 | } |
527 | seatop_begin_default(seat); | 473 | seatop_begin_default(seat); |
528 | } | 474 | } |
@@ -569,8 +515,18 @@ struct sway_seat *seat_create(const char *seat_name) { | |||
569 | return NULL; | 515 | return NULL; |
570 | } | 516 | } |
571 | 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 | |||
572 | seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); | 527 | seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); |
573 | 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); | ||
574 | free(seat); | 530 | free(seat); |
575 | return NULL; | 531 | return NULL; |
576 | } | 532 | } |
@@ -578,11 +534,15 @@ struct sway_seat *seat_create(const char *seat_name) { | |||
578 | 534 | ||
579 | seat->cursor = sway_cursor_create(seat); | 535 | seat->cursor = sway_cursor_create(seat); |
580 | if (!seat->cursor) { | 536 | if (!seat->cursor) { |
537 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
581 | wlr_seat_destroy(seat->wlr_seat); | 538 | wlr_seat_destroy(seat->wlr_seat); |
582 | free(seat); | 539 | free(seat); |
583 | return NULL; | 540 | return NULL; |
584 | } | 541 | } |
585 | 542 | ||
543 | seat->destroy.notify = handle_seat_destroy; | ||
544 | wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy); | ||
545 | |||
586 | seat->idle_inhibit_sources = seat->idle_wake_sources = | 546 | seat->idle_inhibit_sources = seat->idle_wake_sources = |
587 | IDLE_SOURCE_KEYBOARD | | 547 | IDLE_SOURCE_KEYBOARD | |
588 | IDLE_SOURCE_POINTER | | 548 | IDLE_SOURCE_POINTER | |
@@ -656,7 +616,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { | |||
656 | case WLR_INPUT_DEVICE_TOUCH: | 616 | case WLR_INPUT_DEVICE_TOUCH: |
657 | caps |= WL_SEAT_CAPABILITY_TOUCH; | 617 | caps |= WL_SEAT_CAPABILITY_TOUCH; |
658 | break; | 618 | break; |
659 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 619 | case WLR_INPUT_DEVICE_TABLET: |
660 | caps |= WL_SEAT_CAPABILITY_POINTER; | 620 | caps |= WL_SEAT_CAPABILITY_POINTER; |
661 | break; | 621 | break; |
662 | case WLR_INPUT_DEVICE_SWITCH: | 622 | case WLR_INPUT_DEVICE_SWITCH: |
@@ -674,7 +634,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { | |||
674 | } else { | 634 | } else { |
675 | wlr_seat_set_capabilities(seat->wlr_seat, caps); | 635 | wlr_seat_set_capabilities(seat->wlr_seat, caps); |
676 | if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { | 636 | if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { |
677 | cursor_set_image(seat->cursor, "left_ptr", NULL); | 637 | cursor_set_image(seat->cursor, "default", NULL); |
678 | } | 638 | } |
679 | } | 639 | } |
680 | } | 640 | } |
@@ -714,19 +674,28 @@ static const char *get_builtin_output_name(void) { | |||
714 | 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) { |
715 | switch (seat_device->input_device->wlr_device->type) { | 675 | switch (seat_device->input_device->wlr_device->type) { |
716 | case WLR_INPUT_DEVICE_TOUCH: | 676 | case WLR_INPUT_DEVICE_TOUCH: |
717 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 677 | case WLR_INPUT_DEVICE_TABLET: |
718 | return true; | 678 | return true; |
719 | default: | 679 | default: |
720 | return false; | 680 | return false; |
721 | } | 681 | } |
722 | } | 682 | } |
723 | 683 | ||
724 | static void seat_apply_input_config(struct sway_seat *seat, | 684 | static void seat_apply_input_mapping(struct sway_seat *seat, |
725 | struct sway_seat_device *sway_device) { | 685 | struct sway_seat_device *sway_device) { |
726 | struct input_config *ic = | 686 | struct input_config *ic = |
727 | input_device_get_config(sway_device->input_device); | 687 | input_device_get_config(sway_device->input_device); |
728 | 688 | ||
729 | 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", | ||
730 | sway_device->input_device->identifier); | 699 | sway_device->input_device->identifier); |
731 | 700 | ||
732 | 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; |
@@ -754,6 +723,7 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
754 | mapped_to_output = NULL; | 723 | mapped_to_output = NULL; |
755 | break; | 724 | break; |
756 | } | 725 | } |
726 | #if WLR_HAS_LIBINPUT_BACKEND | ||
757 | 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) && |
758 | sway_libinput_device_is_builtin(sway_device->input_device)) { | 728 | sway_libinput_device_is_builtin(sway_device->input_device)) { |
759 | mapped_to_output = get_builtin_output_name(); | 729 | mapped_to_output = get_builtin_output_name(); |
@@ -762,6 +732,10 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
762 | mapped_to_output, sway_device->input_device->identifier); | 732 | mapped_to_output, sway_device->input_device->identifier); |
763 | } | 733 | } |
764 | } | 734 | } |
735 | #else | ||
736 | (void)is_touch_or_tablet_tool; | ||
737 | (void)get_builtin_output_name; | ||
738 | #endif | ||
765 | if (mapped_to_output == NULL) { | 739 | if (mapped_to_output == NULL) { |
766 | return; | 740 | return; |
767 | } | 741 | } |
@@ -805,12 +779,9 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
805 | 779 | ||
806 | static void seat_configure_pointer(struct sway_seat *seat, | 780 | static void seat_configure_pointer(struct sway_seat *seat, |
807 | struct sway_seat_device *sway_device) { | 781 | struct sway_seat_device *sway_device) { |
808 | if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { | 782 | seat_configure_xcursor(seat); |
809 | seat_configure_xcursor(seat); | ||
810 | } | ||
811 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 783 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
812 | sway_device->input_device->wlr_device); | 784 | sway_device->input_device->wlr_device); |
813 | seat_apply_input_config(seat, sway_device); | ||
814 | wl_event_source_timer_update( | 785 | wl_event_source_timer_update( |
815 | seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); | 786 | seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); |
816 | } | 787 | } |
@@ -821,8 +792,15 @@ static void seat_configure_keyboard(struct sway_seat *seat, | |||
821 | sway_keyboard_create(seat, seat_device); | 792 | sway_keyboard_create(seat, seat_device); |
822 | } | 793 | } |
823 | sway_keyboard_configure(seat_device->keyboard); | 794 | sway_keyboard_configure(seat_device->keyboard); |
824 | wlr_seat_set_keyboard(seat->wlr_seat, | 795 | |
825 | wlr_keyboard_from_input_device(seat_device->input_device->wlr_device)); | 796 | // We only need to update the current keyboard, as the rest will be updated |
797 | // as they are activated. | ||
798 | struct wlr_keyboard *wlr_keyboard = | ||
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 | } | ||
826 | 804 | ||
827 | // force notify reenter to pick up the new configuration. This reuses | 805 | // force notify reenter to pick up the new configuration. This reuses |
828 | // the current focused surface to avoid breaking input grabs. | 806 | // the current focused surface to avoid breaking input grabs. |
@@ -838,7 +816,6 @@ static void seat_configure_switch(struct sway_seat *seat, | |||
838 | if (!seat_device->switch_device) { | 816 | if (!seat_device->switch_device) { |
839 | sway_switch_create(seat, seat_device); | 817 | sway_switch_create(seat, seat_device); |
840 | } | 818 | } |
841 | seat_apply_input_config(seat, seat_device); | ||
842 | sway_switch_configure(seat_device->switch_device); | 819 | sway_switch_configure(seat_device->switch_device); |
843 | } | 820 | } |
844 | 821 | ||
@@ -846,7 +823,6 @@ static void seat_configure_touch(struct sway_seat *seat, | |||
846 | struct sway_seat_device *sway_device) { | 823 | struct sway_seat_device *sway_device) { |
847 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 824 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
848 | sway_device->input_device->wlr_device); | 825 | sway_device->input_device->wlr_device); |
849 | seat_apply_input_config(seat, sway_device); | ||
850 | } | 826 | } |
851 | 827 | ||
852 | static void seat_configure_tablet_tool(struct sway_seat *seat, | 828 | static void seat_configure_tablet_tool(struct sway_seat *seat, |
@@ -857,7 +833,6 @@ static void seat_configure_tablet_tool(struct sway_seat *seat, | |||
857 | sway_configure_tablet(sway_device->tablet); | 833 | sway_configure_tablet(sway_device->tablet); |
858 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 834 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
859 | sway_device->input_device->wlr_device); | 835 | sway_device->input_device->wlr_device); |
860 | seat_apply_input_config(seat, sway_device); | ||
861 | } | 836 | } |
862 | 837 | ||
863 | static void seat_configure_tablet_pad(struct sway_seat *seat, | 838 | static void seat_configure_tablet_pad(struct sway_seat *seat, |
@@ -907,13 +882,25 @@ void seat_configure_device(struct sway_seat *seat, | |||
907 | case WLR_INPUT_DEVICE_TOUCH: | 882 | case WLR_INPUT_DEVICE_TOUCH: |
908 | seat_configure_touch(seat, seat_device); | 883 | seat_configure_touch(seat, seat_device); |
909 | break; | 884 | break; |
910 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 885 | case WLR_INPUT_DEVICE_TABLET: |
911 | seat_configure_tablet_tool(seat, seat_device); | 886 | seat_configure_tablet_tool(seat, seat_device); |
912 | break; | 887 | break; |
913 | case WLR_INPUT_DEVICE_TABLET_PAD: | 888 | case WLR_INPUT_DEVICE_TABLET_PAD: |
914 | seat_configure_tablet_pad(seat, seat_device); | 889 | seat_configure_tablet_pad(seat, seat_device); |
915 | break; | 890 | break; |
916 | } | 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); | ||
917 | } | 904 | } |
918 | 905 | ||
919 | void seat_reset_device(struct sway_seat *seat, | 906 | void seat_reset_device(struct sway_seat *seat, |
@@ -934,7 +921,7 @@ void seat_reset_device(struct sway_seat *seat, | |||
934 | case WLR_INPUT_DEVICE_TOUCH: | 921 | case WLR_INPUT_DEVICE_TOUCH: |
935 | seat_reset_input_config(seat, seat_device); | 922 | seat_reset_input_config(seat, seat_device); |
936 | break; | 923 | break; |
937 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 924 | case WLR_INPUT_DEVICE_TABLET: |
938 | seat_reset_input_config(seat, seat_device); | 925 | seat_reset_input_config(seat, seat_device); |
939 | break; | 926 | break; |
940 | case WLR_INPUT_DEVICE_TABLET_PAD: | 927 | case WLR_INPUT_DEVICE_TABLET_PAD: |
@@ -1030,7 +1017,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
1030 | 1017 | ||
1031 | wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); | 1018 | wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); |
1032 | struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( | 1019 | struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( |
1033 | server.xwayland.xcursor_manager, "left_ptr", 1); | 1020 | server.xwayland.xcursor_manager, "default", 1); |
1034 | if (xcursor != NULL) { | 1021 | if (xcursor != NULL) { |
1035 | struct wlr_xcursor_image *image = xcursor->images[0]; | 1022 | struct wlr_xcursor_image *image = xcursor->images[0]; |
1036 | wlr_xwayland_set_cursor( | 1023 | wlr_xwayland_set_cursor( |
@@ -1056,33 +1043,35 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
1056 | sway_log(SWAY_ERROR, | 1043 | sway_log(SWAY_ERROR, |
1057 | "Cannot create XCursor manager for theme '%s'", cursor_theme); | 1044 | "Cannot create XCursor manager for theme '%s'", cursor_theme); |
1058 | } | 1045 | } |
1059 | } | ||
1060 | 1046 | ||
1061 | for (int i = 0; i < root->outputs->length; ++i) { | 1047 | |
1062 | struct sway_output *sway_output = root->outputs->items[i]; | 1048 | for (int i = 0; i < root->outputs->length; ++i) { |
1063 | struct wlr_output *output = sway_output->wlr_output; | 1049 | struct sway_output *sway_output = root->outputs->items[i]; |
1064 | bool result = | 1050 | struct wlr_output *output = sway_output->wlr_output; |
1065 | wlr_xcursor_manager_load(seat->cursor->xcursor_manager, | 1051 | bool result = |
1066 | output->scale); | 1052 | wlr_xcursor_manager_load(seat->cursor->xcursor_manager, |
1067 | if (!result) { | 1053 | output->scale); |
1068 | sway_log(SWAY_ERROR, | 1054 | if (!result) { |
1069 | "Cannot load xcursor theme for output '%s' with scale %f", | 1055 | sway_log(SWAY_ERROR, |
1070 | output->name, output->scale); | 1056 | "Cannot load xcursor theme for output '%s' with scale %f", |
1057 | output->name, output->scale); | ||
1058 | } | ||
1071 | } | 1059 | } |
1072 | } | ||
1073 | 1060 | ||
1074 | // 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 |
1075 | cursor_set_image(seat->cursor, NULL, NULL); | 1062 | cursor_set_image(seat->cursor, NULL, NULL); |
1076 | cursor_set_image(seat->cursor, "left_ptr", NULL); | 1063 | cursor_set_image(seat->cursor, "default", NULL); |
1077 | wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, | 1064 | wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, |
1078 | seat->cursor->cursor->y); | 1065 | seat->cursor->cursor->y); |
1066 | } | ||
1079 | } | 1067 | } |
1080 | 1068 | ||
1081 | bool seat_is_input_allowed(struct sway_seat *seat, | 1069 | bool seat_is_input_allowed(struct sway_seat *seat, |
1082 | struct wlr_surface *surface) { | 1070 | struct wlr_surface *surface) { |
1083 | struct wl_client *client = wl_resource_get_client(surface->resource); | 1071 | if (server.session_lock.lock) { |
1084 | return seat->exclusive_client == client || | 1072 | return sway_session_lock_has_surface(server.session_lock.lock, surface); |
1085 | (seat->exclusive_client == NULL && !server.session_lock.locked); | 1073 | } |
1074 | return true; | ||
1086 | } | 1075 | } |
1087 | 1076 | ||
1088 | static void send_unfocus(struct sway_container *con, void *data) { | 1077 | static void send_unfocus(struct sway_container *con, void *data) { |
@@ -1141,15 +1130,7 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1141 | } | 1130 | } |
1142 | } | 1131 | } |
1143 | 1132 | ||
1144 | 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) { |
1145 | if (seat->focused_layer) { | ||
1146 | struct wlr_layer_surface_v1 *layer = seat->focused_layer; | ||
1147 | seat_set_focus_layer(seat, NULL); | ||
1148 | seat_set_focus(seat, node); | ||
1149 | seat_set_focus_layer(seat, layer); | ||
1150 | return; | ||
1151 | } | ||
1152 | |||
1153 | struct sway_node *last_focus = seat_get_focus(seat); | 1134 | struct sway_node *last_focus = seat_get_focus(seat); |
1154 | if (last_focus == node) { | 1135 | if (last_focus == node) { |
1155 | return; | 1136 | return; |
@@ -1183,11 +1164,6 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1183 | return; | 1164 | return; |
1184 | } | 1165 | } |
1185 | 1166 | ||
1186 | // Deny setting focus when an input grab or lockscreen is active | ||
1187 | if (container && container->view && !seat_is_input_allowed(seat, container->view->surface)) { | ||
1188 | return; | ||
1189 | } | ||
1190 | |||
1191 | struct sway_output *new_output = | 1167 | struct sway_output *new_output = |
1192 | new_workspace ? new_workspace->output : NULL; | 1168 | new_workspace ? new_workspace->output : NULL; |
1193 | 1169 | ||
@@ -1287,6 +1263,24 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1287 | } | 1263 | } |
1288 | } | 1264 | } |
1289 | 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 | |||
1290 | void seat_set_focus_container(struct sway_seat *seat, | 1284 | void seat_set_focus_container(struct sway_seat *seat, |
1291 | struct sway_container *con) { | 1285 | struct sway_container *con) { |
1292 | seat_set_focus(seat, con ? &con->node : NULL); | 1286 | seat_set_focus(seat, con ? &con->node : NULL); |
@@ -1312,7 +1306,7 @@ void seat_set_focus_surface(struct sway_seat *seat, | |||
1312 | } | 1306 | } |
1313 | 1307 | ||
1314 | sway_input_method_relay_set_focus(&seat->im_relay, surface); | 1308 | sway_input_method_relay_set_focus(&seat->im_relay, surface); |
1315 | seat_tablet_pads_notify_enter(seat, surface); | 1309 | seat_tablet_pads_set_focus(seat, surface); |
1316 | } | 1310 | } |
1317 | 1311 | ||
1318 | void seat_set_focus_layer(struct sway_seat *seat, | 1312 | void seat_set_focus_layer(struct sway_seat *seat, |
@@ -1326,28 +1320,23 @@ void seat_set_focus_layer(struct sway_seat *seat, | |||
1326 | seat_set_focus(seat, previous); | 1320 | seat_set_focus(seat, previous); |
1327 | } | 1321 | } |
1328 | return; | 1322 | return; |
1329 | } else if (!layer || seat->focused_layer == layer) { | 1323 | } else if (!layer) { |
1330 | return; | 1324 | return; |
1331 | } | 1325 | } |
1332 | assert(layer->mapped); | 1326 | assert(layer->surface->mapped); |
1333 | seat_set_focus_surface(seat, layer->surface, true); | 1327 | if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP && |
1334 | if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { | 1328 | layer->current.keyboard_interactive |
1335 | seat->focused_layer = layer; | 1329 | == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { |
1330 | seat->has_exclusive_layer = true; | ||
1336 | } | 1331 | } |
1337 | } | 1332 | if (seat->focused_layer == layer) { |
1338 | |||
1339 | void seat_set_exclusive_client(struct sway_seat *seat, | ||
1340 | struct wl_client *client) { | ||
1341 | if (!client) { | ||
1342 | seat->exclusive_client = client; | ||
1343 | // Triggers a refocus of the topmost surface layer if necessary | ||
1344 | // TODO: Make layer surface focus per-output based on cursor position | ||
1345 | for (int i = 0; i < root->outputs->length; ++i) { | ||
1346 | struct sway_output *output = root->outputs->items[i]; | ||
1347 | arrange_layers(output); | ||
1348 | } | ||
1349 | return; | 1333 | return; |
1350 | } | 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) { | ||
1351 | if (seat->focused_layer) { | 1340 | if (seat->focused_layer) { |
1352 | if (wl_resource_get_client(seat->focused_layer->resource) != client) { | 1341 | if (wl_resource_get_client(seat->focused_layer->resource) != client) { |
1353 | seat_set_focus_layer(seat, NULL); | 1342 | seat_set_focus_layer(seat, NULL); |
@@ -1374,7 +1363,6 @@ void seat_set_exclusive_client(struct sway_seat *seat, | |||
1374 | now.tv_nsec / 1000, point->touch_id); | 1363 | now.tv_nsec / 1000, point->touch_id); |
1375 | } | 1364 | } |
1376 | } | 1365 | } |
1377 | seat->exclusive_client = client; | ||
1378 | } | 1366 | } |
1379 | 1367 | ||
1380 | struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, | 1368 | struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, |
@@ -1541,7 +1529,7 @@ struct seat_config *seat_get_config_by_name(const char *name) { | |||
1541 | } | 1529 | } |
1542 | 1530 | ||
1543 | 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, |
1544 | uint32_t button, enum wlr_button_state state) { | 1532 | uint32_t button, enum wl_pointer_button_state state) { |
1545 | 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, |
1546 | time_msec, button, state); | 1534 | time_msec, button, state); |
1547 | } | 1535 | } |
@@ -1578,7 +1566,7 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) { | |||
1578 | 1566 | ||
1579 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, | 1567 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, |
1580 | struct wlr_input_device *device, uint32_t button, | 1568 | struct wlr_input_device *device, uint32_t button, |
1581 | enum wlr_button_state state) { | 1569 | enum wl_pointer_button_state state) { |
1582 | if (seat->seatop_impl->button) { | 1570 | if (seat->seatop_impl->button) { |
1583 | seat->seatop_impl->button(seat, time_msec, device, button, state); | 1571 | seat->seatop_impl->button(seat, time_msec, device, button, state); |
1584 | } | 1572 | } |
@@ -1597,6 +1585,32 @@ void seatop_pointer_axis(struct sway_seat *seat, | |||
1597 | } | 1585 | } |
1598 | } | 1586 | } |
1599 | 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 | |||
1600 | void seatop_tablet_tool_tip(struct sway_seat *seat, | 1614 | void seatop_tablet_tool_tip(struct sway_seat *seat, |
1601 | struct sway_tablet_tool *tool, uint32_t time_msec, | 1615 | struct sway_tablet_tool *tool, uint32_t time_msec, |
1602 | enum wlr_tablet_tool_tip_state state) { | 1616 | enum wlr_tablet_tool_tip_state state) { |
@@ -1685,13 +1699,6 @@ void seatop_end(struct sway_seat *seat) { | |||
1685 | seat->seatop_impl = NULL; | 1699 | seat->seatop_impl = NULL; |
1686 | } | 1700 | } |
1687 | 1701 | ||
1688 | void seatop_render(struct sway_seat *seat, struct sway_output *output, | ||
1689 | pixman_region32_t *damage) { | ||
1690 | if (seat->seatop_impl->render) { | ||
1691 | seat->seatop_impl->render(seat, output, damage); | ||
1692 | } | ||
1693 | } | ||
1694 | |||
1695 | bool seatop_allows_set_cursor(struct sway_seat *seat) { | 1702 | bool seatop_allows_set_cursor(struct sway_seat *seat) { |
1696 | return seat->seatop_impl->allow_set_cursor; | 1703 | return seat->seatop_impl->allow_set_cursor; |
1697 | } | 1704 | } |
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 875426bf..0c6f7c5e 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c | |||
@@ -1,7 +1,7 @@ | |||
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 "gesture.h" |
@@ -9,7 +9,9 @@ | |||
9 | #include "sway/input/cursor.h" | 9 | #include "sway/input/cursor.h" |
10 | #include "sway/input/seat.h" | 10 | #include "sway/input/seat.h" |
11 | #include "sway/input/tablet.h" | 11 | #include "sway/input/tablet.h" |
12 | #include "sway/layers.h" | ||
12 | #include "sway/output.h" | 13 | #include "sway/output.h" |
14 | #include "sway/scene_descriptor.h" | ||
13 | #include "sway/tree/view.h" | 15 | #include "sway/tree/view.h" |
14 | #include "sway/tree/workspace.h" | 16 | #include "sway/tree/workspace.h" |
15 | #include "log.h" | 17 | #include "log.h" |
@@ -53,6 +55,9 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { | |||
53 | while (cont) { | 55 | while (cont) { |
54 | if (container_parent_layout(cont) == layout) { | 56 | if (container_parent_layout(cont) == layout) { |
55 | list_t *siblings = container_get_siblings(cont); | 57 | list_t *siblings = container_get_siblings(cont); |
58 | if (!siblings) { | ||
59 | return false; | ||
60 | } | ||
56 | int index = list_find(siblings, cont); | 61 | int index = list_find(siblings, cont); |
57 | if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { | 62 | if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { |
58 | return false; | 63 | return false; |
@@ -228,14 +233,15 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
228 | struct sway_container *cont = node && node->type == N_CONTAINER ? | 233 | struct sway_container *cont = node && node->type == N_CONTAINER ? |
229 | node->sway_container : NULL; | 234 | node->sway_container : NULL; |
230 | 235 | ||
231 | 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) { | ||
232 | // Handle tapping a layer surface | 242 | // Handle tapping a layer surface |
233 | struct wlr_layer_surface_v1 *layer = | 243 | seat_set_focus_layer(seat, layer); |
234 | wlr_layer_surface_v1_from_wlr_surface(surface); | 244 | transaction_commit_dirty(); |
235 | if (layer->current.keyboard_interactive) { | ||
236 | seat_set_focus_layer(seat, layer); | ||
237 | transaction_commit_dirty(); | ||
238 | } | ||
239 | } else if (cont) { | 245 | } else if (cont) { |
240 | bool is_floating_or_child = container_is_floating_or_child(cont); | 246 | bool is_floating_or_child = container_is_floating_or_child(cont); |
241 | bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); | 247 | bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); |
@@ -260,20 +266,17 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
260 | 266 | ||
261 | // Handle tapping on a container surface | 267 | // Handle tapping on a container surface |
262 | seat_set_focus_container(seat, cont); | 268 | seat_set_focus_container(seat, cont); |
263 | seatop_begin_down(seat, node->sway_container, time_msec, sx, sy); | 269 | seatop_begin_down(seat, node->sway_container, sx, sy); |
264 | } | 270 | } |
265 | #if HAVE_XWAYLAND | 271 | #if HAVE_XWAYLAND |
266 | // Handle tapping on an xwayland unmanaged view | 272 | // Handle tapping on an xwayland unmanaged view |
267 | else if (wlr_surface_is_xwayland_surface(surface)) { | 273 | else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && |
268 | struct wlr_xwayland_surface *xsurface = | 274 | xsurface->override_redirect && |
269 | wlr_xwayland_surface_from_wlr_surface(surface); | 275 | wlr_xwayland_or_surface_wants_focus(xsurface)) { |
270 | if (xsurface->override_redirect && | 276 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; |
271 | wlr_xwayland_or_surface_wants_focus(xsurface)) { | 277 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
272 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | 278 | seat_set_focus_surface(seat, xsurface->surface, false); |
273 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 279 | transaction_commit_dirty(); |
274 | seat_set_focus_surface(seat, xsurface->surface, false); | ||
275 | transaction_commit_dirty(); | ||
276 | } | ||
277 | } | 280 | } |
278 | #endif | 281 | #endif |
279 | 282 | ||
@@ -287,7 +290,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
287 | 290 | ||
288 | static bool trigger_pointer_button_binding(struct sway_seat *seat, | 291 | static bool trigger_pointer_button_binding(struct sway_seat *seat, |
289 | struct wlr_input_device *device, uint32_t button, | 292 | struct wlr_input_device *device, uint32_t button, |
290 | enum wlr_button_state state, uint32_t modifiers, | 293 | enum wl_pointer_button_state state, uint32_t modifiers, |
291 | 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) { |
292 | // 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 |
293 | // pointer input for one. Emulated input should not trigger bindings. The | 296 | // pointer input for one. Emulated input should not trigger bindings. The |
@@ -301,7 +304,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat, | |||
301 | char *device_identifier = device ? input_device_get_identifier(device) | 304 | char *device_identifier = device ? input_device_get_identifier(device) |
302 | : strdup("*"); | 305 | : strdup("*"); |
303 | struct sway_binding *binding = NULL; | 306 | struct sway_binding *binding = NULL; |
304 | if (state == WLR_BUTTON_PRESSED) { | 307 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { |
305 | state_add_button(e, button); | 308 | state_add_button(e, button); |
306 | binding = get_active_mouse_binding(e, | 309 | binding = get_active_mouse_binding(e, |
307 | config->current_mode->mouse_bindings, modifiers, false, | 310 | config->current_mode->mouse_bindings, modifiers, false, |
@@ -326,7 +329,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat, | |||
326 | 329 | ||
327 | 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, |
328 | struct wlr_input_device *device, uint32_t button, | 331 | struct wlr_input_device *device, uint32_t button, |
329 | enum wlr_button_state state) { | 332 | enum wl_pointer_button_state state) { |
330 | struct sway_cursor *cursor = seat->cursor; | 333 | struct sway_cursor *cursor = seat->cursor; |
331 | 334 | ||
332 | // Determine what's under the cursor | 335 | // Determine what's under the cursor |
@@ -359,7 +362,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
359 | 362 | ||
360 | // Handle clicking an empty workspace | 363 | // Handle clicking an empty workspace |
361 | if (node && node->type == N_WORKSPACE) { | 364 | if (node && node->type == N_WORKSPACE) { |
362 | if (state == WLR_BUTTON_PRESSED) { | 365 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { |
363 | seat_set_focus(seat, node); | 366 | seat_set_focus(seat, node); |
364 | transaction_commit_dirty(); | 367 | transaction_commit_dirty(); |
365 | } | 368 | } |
@@ -367,16 +370,15 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
367 | return; | 370 | return; |
368 | } | 371 | } |
369 | 372 | ||
370 | // Handle clicking a layer surface | 373 | // Handle clicking a layer surface and its popups/subsurfaces |
371 | if (surface && wlr_surface_is_layer_surface(surface)) { | 374 | struct wlr_layer_surface_v1 *layer = NULL; |
372 | struct wlr_layer_surface_v1 *layer = | 375 | if ((layer = toplevel_layer_surface_from_surface(surface))) { |
373 | wlr_layer_surface_v1_from_wlr_surface(surface); | ||
374 | if (layer->current.keyboard_interactive) { | 376 | if (layer->current.keyboard_interactive) { |
375 | seat_set_focus_layer(seat, layer); | 377 | seat_set_focus_layer(seat, layer); |
376 | transaction_commit_dirty(); | 378 | transaction_commit_dirty(); |
377 | } | 379 | } |
378 | if (state == WLR_BUTTON_PRESSED) { | 380 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { |
379 | seatop_begin_down_on_surface(seat, surface, time_msec, sx, sy); | 381 | seatop_begin_down_on_surface(seat, surface, sx, sy); |
380 | } | 382 | } |
381 | seat_pointer_notify_button(seat, time_msec, button, state); | 383 | seat_pointer_notify_button(seat, time_msec, button, state); |
382 | return; | 384 | return; |
@@ -384,7 +386,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
384 | 386 | ||
385 | // Handle tiling resize via border | 387 | // Handle tiling resize via border |
386 | if (cont && resize_edge && button == BTN_LEFT && | 388 | if (cont && resize_edge && button == BTN_LEFT && |
387 | state == WLR_BUTTON_PRESSED && !is_floating) { | 389 | state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) { |
388 | // 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 |
389 | // 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 |
390 | // 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. |
@@ -402,7 +404,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
402 | // Handle tiling resize via mod | 404 | // Handle tiling resize via mod |
403 | bool mod_pressed = modifiers & config->floating_mod; | 405 | bool mod_pressed = modifiers & config->floating_mod; |
404 | if (cont && !is_floating_or_child && mod_pressed && | 406 | if (cont && !is_floating_or_child && mod_pressed && |
405 | state == WLR_BUTTON_PRESSED) { | 407 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
406 | uint32_t btn_resize = config->floating_mod_inverse ? | 408 | uint32_t btn_resize = config->floating_mod_inverse ? |
407 | BTN_LEFT : BTN_RIGHT; | 409 | BTN_LEFT : BTN_RIGHT; |
408 | if (button == btn_resize) { | 410 | if (button == btn_resize) { |
@@ -429,13 +431,31 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
429 | } | 431 | } |
430 | } | 432 | } |
431 | 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 | |||
432 | // Handle beginning floating move | 454 | // Handle beginning floating move |
433 | if (cont && is_floating_or_child && !is_fullscreen_or_child && | 455 | if (cont && is_floating_or_child && !is_fullscreen_or_child && |
434 | state == WLR_BUTTON_PRESSED) { | 456 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
435 | 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; |
436 | if (button == btn_move && (mod_pressed || on_titlebar)) { | 458 | if (button == btn_move && (mod_pressed || on_titlebar)) { |
437 | seat_set_focus_container(seat, | ||
438 | seat_get_focus_inactive_view(seat, &cont->node)); | ||
439 | seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); | 459 | seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); |
440 | return; | 460 | return; |
441 | } | 461 | } |
@@ -443,9 +463,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
443 | 463 | ||
444 | // Handle beginning floating resize | 464 | // Handle beginning floating resize |
445 | if (cont && is_floating_or_child && !is_fullscreen_or_child && | 465 | if (cont && is_floating_or_child && !is_fullscreen_or_child && |
446 | state == WLR_BUTTON_PRESSED) { | 466 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
447 | // Via border | 467 | // Via border |
448 | 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); | ||
449 | seatop_begin_resize_floating(seat, cont, resize_edge); | 470 | seatop_begin_resize_floating(seat, cont, resize_edge); |
450 | return; | 471 | return; |
451 | } | 472 | } |
@@ -460,6 +481,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
460 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; | 481 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; |
461 | edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? | 482 | edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? |
462 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; | 483 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; |
484 | seat_set_focus_container(seat, floater); | ||
463 | seatop_begin_resize_floating(seat, floater, edge); | 485 | seatop_begin_resize_floating(seat, floater, edge); |
464 | return; | 486 | return; |
465 | } | 487 | } |
@@ -467,55 +489,43 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
467 | 489 | ||
468 | // Handle moving a tiling container | 490 | // Handle moving a tiling container |
469 | if (config->tiling_drag && (mod_pressed || on_titlebar) && | 491 | if (config->tiling_drag && (mod_pressed || on_titlebar) && |
470 | state == WLR_BUTTON_PRESSED && !is_floating_or_child && | 492 | state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child && |
471 | cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { | 493 | cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { |
472 | struct sway_container *focus = seat_get_focused_container(seat); | ||
473 | bool focused = focus == cont || container_has_ancestor(focus, cont); | ||
474 | if (on_titlebar && !focused) { | ||
475 | node = seat_get_focus_inactive(seat, &cont->node); | ||
476 | seat_set_focus(seat, node); | ||
477 | } | ||
478 | |||
479 | // If moving a container by its title bar, use a threshold for the drag | 494 | // If moving a container by its title bar, use a threshold for the drag |
480 | if (!mod_pressed && config->tiling_drag_threshold > 0) { | 495 | if (!mod_pressed && config->tiling_drag_threshold > 0) { |
481 | seatop_begin_move_tiling_threshold(seat, cont); | 496 | seatop_begin_move_tiling_threshold(seat, cont); |
482 | } else { | 497 | } else { |
483 | seatop_begin_move_tiling(seat, cont); | 498 | seatop_begin_move_tiling(seat, cont); |
484 | } | 499 | } |
500 | |||
485 | return; | 501 | return; |
486 | } | 502 | } |
487 | 503 | ||
488 | // Handle mousedown on a container surface | 504 | // Handle mousedown on a container surface |
489 | if (surface && cont && state == WLR_BUTTON_PRESSED) { | 505 | if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { |
490 | seat_set_focus_container(seat, cont); | 506 | seatop_begin_down(seat, cont, sx, sy); |
491 | seatop_begin_down(seat, cont, time_msec, sx, sy); | 507 | seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED); |
492 | seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); | ||
493 | return; | 508 | return; |
494 | } | 509 | } |
495 | 510 | ||
496 | // Handle clicking a container surface or decorations | 511 | // Handle clicking a container surface or decorations |
497 | if (cont && state == WLR_BUTTON_PRESSED) { | 512 | if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { |
498 | node = seat_get_focus_inactive(seat, &cont->node); | ||
499 | seat_set_focus(seat, node); | ||
500 | transaction_commit_dirty(); | ||
501 | seat_pointer_notify_button(seat, time_msec, button, state); | 513 | seat_pointer_notify_button(seat, time_msec, button, state); |
502 | return; | 514 | return; |
503 | } | 515 | } |
504 | 516 | ||
505 | #if HAVE_XWAYLAND | 517 | #if HAVE_XWAYLAND |
506 | // Handle clicking on xwayland unmanaged view | 518 | // Handle clicking on xwayland unmanaged view |
507 | if (surface && wlr_surface_is_xwayland_surface(surface)) { | 519 | struct wlr_xwayland_surface *xsurface; |
508 | struct wlr_xwayland_surface *xsurface = | 520 | if (surface && |
509 | wlr_xwayland_surface_from_wlr_surface(surface); | 521 | (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && |
510 | if (xsurface->override_redirect && | 522 | xsurface->override_redirect && |
511 | wlr_xwayland_or_surface_wants_focus(xsurface)) { | 523 | wlr_xwayland_or_surface_wants_focus(xsurface)) { |
512 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | 524 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; |
513 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 525 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
514 | seat_set_focus_surface(seat, xsurface->surface, false); | 526 | seat_set_focus_surface(seat, xsurface->surface, false); |
515 | transaction_commit_dirty(); | 527 | transaction_commit_dirty(); |
516 | seat_pointer_notify_button(seat, time_msec, button, state); | 528 | seat_pointer_notify_button(seat, time_msec, button, state); |
517 | return; | ||
518 | } | ||
519 | } | 529 | } |
520 | #endif | 530 | #endif |
521 | 531 | ||
@@ -538,6 +548,21 @@ static void check_focus_follows_mouse(struct sway_seat *seat, | |||
538 | if (wlr_output == NULL) { | 548 | if (wlr_output == NULL) { |
539 | return; | 549 | return; |
540 | } | 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 | |||
541 | struct sway_output *hovered_output = wlr_output->data; | 566 | struct sway_output *hovered_output = wlr_output->data; |
542 | if (focus && hovered_output != node_get_output(focus)) { | 567 | if (focus && hovered_output != node_get_output(focus)) { |
543 | struct sway_workspace *ws = output_get_active_workspace(hovered_output); | 568 | struct sway_workspace *ws = output_get_active_workspace(hovered_output); |
@@ -598,12 +623,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
598 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 623 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
599 | } | 624 | } |
600 | 625 | ||
601 | struct sway_drag_icon *drag_icon; | 626 | drag_icons_update_position(seat); |
602 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
603 | if (drag_icon->seat == seat) { | ||
604 | drag_icon_update_position(drag_icon); | ||
605 | } | ||
606 | } | ||
607 | 627 | ||
608 | e->previous_node = node; | 628 | e->previous_node = node; |
609 | } | 629 | } |
@@ -633,25 +653,50 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, | |||
633 | 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); |
634 | } | 654 | } |
635 | 655 | ||
636 | struct sway_drag_icon *drag_icon; | 656 | drag_icons_update_position(seat); |
637 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
638 | if (drag_icon->seat == seat) { | ||
639 | drag_icon_update_position(drag_icon); | ||
640 | } | ||
641 | } | ||
642 | 657 | ||
643 | e->previous_node = node; | 658 | e->previous_node = node; |
644 | } | 659 | } |
645 | 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 | |||
646 | /*----------------------------------------\ | 691 | /*----------------------------------------\ |
647 | * Functions used by handle_pointer_axis / | 692 | * Functions used by handle_pointer_axis / |
648 | *--------------------------------------*/ | 693 | *--------------------------------------*/ |
649 | 694 | ||
650 | static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { | 695 | static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { |
651 | switch (event->orientation) { | 696 | switch (event->orientation) { |
652 | case WLR_AXIS_ORIENTATION_VERTICAL: | 697 | case WL_POINTER_AXIS_VERTICAL_SCROLL: |
653 | return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; | 698 | return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; |
654 | case WLR_AXIS_ORIENTATION_HORIZONTAL: | 699 | case WL_POINTER_AXIS_HORIZONTAL_SCROLL: |
655 | return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; | 700 | return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; |
656 | default: | 701 | default: |
657 | sway_log(SWAY_DEBUG, "Unknown axis orientation"); | 702 | sway_log(SWAY_DEBUG, "Unknown axis orientation"); |
@@ -708,6 +753,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
708 | 753 | ||
709 | // 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) |
710 | if (!handled && (on_titlebar || on_titlebar_border)) { | 755 | if (!handled && (on_titlebar || on_titlebar_border)) { |
756 | struct sway_node *new_focus; | ||
711 | enum sway_container_layout layout = container_parent_layout(cont); | 757 | enum sway_container_layout layout = container_parent_layout(cont); |
712 | if (layout == L_TABBED || layout == L_STACKED) { | 758 | if (layout == L_TABBED || layout == L_STACKED) { |
713 | struct sway_node *tabcontainer = node_get_parent(node); | 759 | struct sway_node *tabcontainer = node_get_parent(node); |
@@ -715,7 +761,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
715 | seat_get_active_tiling_child(seat, tabcontainer); | 761 | seat_get_active_tiling_child(seat, tabcontainer); |
716 | list_t *siblings = container_get_siblings(cont); | 762 | list_t *siblings = container_get_siblings(cont); |
717 | int desired = list_find(siblings, active->sway_container) + | 763 | int desired = list_find(siblings, active->sway_container) + |
718 | round(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); | 764 | roundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); |
719 | if (desired < 0) { | 765 | if (desired < 0) { |
720 | desired = 0; | 766 | desired = 0; |
721 | } else if (desired >= siblings->length) { | 767 | } else if (desired >= siblings->length) { |
@@ -724,14 +770,16 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
724 | 770 | ||
725 | struct sway_container *new_sibling_con = siblings->items[desired]; | 771 | struct sway_container *new_sibling_con = siblings->items[desired]; |
726 | struct sway_node *new_sibling = &new_sibling_con->node; | 772 | struct sway_node *new_sibling = &new_sibling_con->node; |
727 | struct sway_node *new_focus = | ||
728 | seat_get_focus_inactive(seat, new_sibling); | ||
729 | // Use the focused child of the tabbed/stacked container, not the | 773 | // Use the focused child of the tabbed/stacked container, not the |
730 | // container the user scrolled on. | 774 | // container the user scrolled on. |
731 | seat_set_focus(seat, new_focus); | 775 | new_focus = seat_get_focus_inactive(seat, new_sibling); |
732 | transaction_commit_dirty(); | 776 | } else { |
733 | handled = true; | 777 | new_focus = seat_get_focus_inactive(seat, &cont->node); |
734 | } | 778 | } |
779 | |||
780 | seat_set_focus(seat, new_focus); | ||
781 | transaction_commit_dirty(); | ||
782 | handled = true; | ||
735 | } | 783 | } |
736 | 784 | ||
737 | // Handle mouse bindings - x11 mouse buttons 4-7 - release event | 785 | // Handle mouse bindings - x11 mouse buttons 4-7 - release event |
@@ -747,8 +795,9 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
747 | 795 | ||
748 | if (!handled) { | 796 | if (!handled) { |
749 | 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, |
750 | event->orientation, scroll_factor * event->delta, | 798 | event->orientation, scroll_factor * event->delta, |
751 | round(scroll_factor * event->delta_discrete), event->source); | 799 | roundf(scroll_factor * event->delta_discrete), event->source, |
800 | event->relative_direction); | ||
752 | } | 801 | } |
753 | } | 802 | } |
754 | 803 | ||
@@ -894,7 +943,7 @@ static void handle_hold_begin(struct sway_seat *seat, | |||
894 | // ... otherwise forward to client | 943 | // ... otherwise forward to client |
895 | struct sway_cursor *cursor = seat->cursor; | 944 | struct sway_cursor *cursor = seat->cursor; |
896 | wlr_pointer_gestures_v1_send_hold_begin( | 945 | wlr_pointer_gestures_v1_send_hold_begin( |
897 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 946 | server.input->pointer_gestures, cursor->seat->wlr_seat, |
898 | event->time_msec, event->fingers); | 947 | event->time_msec, event->fingers); |
899 | } | 948 | } |
900 | } | 949 | } |
@@ -906,7 +955,7 @@ static void handle_hold_end(struct sway_seat *seat, | |||
906 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) { | 955 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) { |
907 | struct sway_cursor *cursor = seat->cursor; | 956 | struct sway_cursor *cursor = seat->cursor; |
908 | wlr_pointer_gestures_v1_send_hold_end( | 957 | wlr_pointer_gestures_v1_send_hold_end( |
909 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 958 | server.input->pointer_gestures, cursor->seat->wlr_seat, |
910 | event->time_msec, event->cancelled); | 959 | event->time_msec, event->cancelled); |
911 | return; | 960 | return; |
912 | } | 961 | } |
@@ -939,7 +988,7 @@ static void handle_pinch_begin(struct sway_seat *seat, | |||
939 | // ... otherwise forward to client | 988 | // ... otherwise forward to client |
940 | struct sway_cursor *cursor = seat->cursor; | 989 | struct sway_cursor *cursor = seat->cursor; |
941 | wlr_pointer_gestures_v1_send_pinch_begin( | 990 | wlr_pointer_gestures_v1_send_pinch_begin( |
942 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 991 | server.input->pointer_gestures, cursor->seat->wlr_seat, |
943 | event->time_msec, event->fingers); | 992 | event->time_msec, event->fingers); |
944 | } | 993 | } |
945 | } | 994 | } |
@@ -955,7 +1004,7 @@ static void handle_pinch_update(struct sway_seat *seat, | |||
955 | // ... otherwise forward to client | 1004 | // ... otherwise forward to client |
956 | struct sway_cursor *cursor = seat->cursor; | 1005 | struct sway_cursor *cursor = seat->cursor; |
957 | wlr_pointer_gestures_v1_send_pinch_update( | 1006 | wlr_pointer_gestures_v1_send_pinch_update( |
958 | cursor->pointer_gestures, | 1007 | server.input->pointer_gestures, |
959 | cursor->seat->wlr_seat, | 1008 | cursor->seat->wlr_seat, |
960 | event->time_msec, event->dx, event->dy, | 1009 | event->time_msec, event->dx, event->dy, |
961 | event->scale, event->rotation); | 1010 | event->scale, event->rotation); |
@@ -969,7 +1018,7 @@ static void handle_pinch_end(struct sway_seat *seat, | |||
969 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { | 1018 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { |
970 | struct sway_cursor *cursor = seat->cursor; | 1019 | struct sway_cursor *cursor = seat->cursor; |
971 | wlr_pointer_gestures_v1_send_pinch_end( | 1020 | wlr_pointer_gestures_v1_send_pinch_end( |
972 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 1021 | server.input->pointer_gestures, cursor->seat->wlr_seat, |
973 | event->time_msec, event->cancelled); | 1022 | event->time_msec, event->cancelled); |
974 | return; | 1023 | return; |
975 | } | 1024 | } |
@@ -1002,7 +1051,7 @@ static void handle_swipe_begin(struct sway_seat *seat, | |||
1002 | // ... otherwise forward to client | 1051 | // ... otherwise forward to client |
1003 | struct sway_cursor *cursor = seat->cursor; | 1052 | struct sway_cursor *cursor = seat->cursor; |
1004 | wlr_pointer_gestures_v1_send_swipe_begin( | 1053 | wlr_pointer_gestures_v1_send_swipe_begin( |
1005 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 1054 | server.input->pointer_gestures, cursor->seat->wlr_seat, |
1006 | event->time_msec, event->fingers); | 1055 | event->time_msec, event->fingers); |
1007 | } | 1056 | } |
1008 | } | 1057 | } |
@@ -1019,7 +1068,7 @@ static void handle_swipe_update(struct sway_seat *seat, | |||
1019 | // ... otherwise forward to client | 1068 | // ... otherwise forward to client |
1020 | struct sway_cursor *cursor = seat->cursor; | 1069 | struct sway_cursor *cursor = seat->cursor; |
1021 | wlr_pointer_gestures_v1_send_swipe_update( | 1070 | wlr_pointer_gestures_v1_send_swipe_update( |
1022 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 1071 | server.input->pointer_gestures, cursor->seat->wlr_seat, |
1023 | event->time_msec, event->dx, event->dy); | 1072 | event->time_msec, event->dx, event->dy); |
1024 | } | 1073 | } |
1025 | } | 1074 | } |
@@ -1030,7 +1079,7 @@ static void handle_swipe_end(struct sway_seat *seat, | |||
1030 | struct seatop_default_event *seatop = seat->seatop_data; | 1079 | struct seatop_default_event *seatop = seat->seatop_data; |
1031 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { | 1080 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { |
1032 | struct sway_cursor *cursor = seat->cursor; | 1081 | struct sway_cursor *cursor = seat->cursor; |
1033 | wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures, | 1082 | wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures, |
1034 | cursor->seat->wlr_seat, event->time_msec, event->cancelled); | 1083 | cursor->seat->wlr_seat, event->time_msec, event->cancelled); |
1035 | return; | 1084 | return; |
1036 | } | 1085 | } |
@@ -1087,6 +1136,7 @@ static const struct sway_seatop_impl seatop_impl = { | |||
1087 | .swipe_begin = handle_swipe_begin, | 1136 | .swipe_begin = handle_swipe_begin, |
1088 | .swipe_update = handle_swipe_update, | 1137 | .swipe_update = handle_swipe_update, |
1089 | .swipe_end = handle_swipe_end, | 1138 | .swipe_end = handle_swipe_end, |
1139 | .touch_down = handle_touch_down, | ||
1090 | .rebase = handle_rebase, | 1140 | .rebase = handle_rebase, |
1091 | .allow_set_cursor = true, | 1141 | .allow_set_cursor = true, |
1092 | }; | 1142 | }; |
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index b40773d0..340e334b 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c | |||
@@ -1,13 +1,20 @@ | |||
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; |
13 | struct sway_seat *seat; | 20 | struct sway_seat *seat; |
@@ -15,8 +22,113 @@ struct seatop_down_event { | |||
15 | struct wlr_surface *surface; | 22 | struct wlr_surface *surface; |
16 | 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 |
17 | 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 | ||
18 | }; | 26 | }; |
19 | 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 | |||
20 | static void handle_pointer_axis(struct sway_seat *seat, | 132 | static void handle_pointer_axis(struct sway_seat *seat, |
21 | struct wlr_pointer_axis_event *event) { | 133 | struct wlr_pointer_axis_event *event) { |
22 | struct sway_input_device *input_device = | 134 | struct sway_input_device *input_device = |
@@ -28,12 +140,13 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
28 | 140 | ||
29 | wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, | 141 | wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, |
30 | event->orientation, scroll_factor * event->delta, | 142 | event->orientation, scroll_factor * event->delta, |
31 | round(scroll_factor * event->delta_discrete), event->source); | 143 | roundf(scroll_factor * event->delta_discrete), event->source, |
144 | event->relative_direction); | ||
32 | } | 145 | } |
33 | 146 | ||
34 | 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, |
35 | struct wlr_input_device *device, uint32_t button, | 148 | struct wlr_input_device *device, uint32_t button, |
36 | enum wlr_button_state state) { | 149 | enum wl_pointer_button_state state) { |
37 | seat_pointer_notify_button(seat, time_msec, button, state); | 150 | seat_pointer_notify_button(seat, time_msec, button, state); |
38 | 151 | ||
39 | if (seat->cursor->pressed_button_count == 0) { | 152 | if (seat->cursor->pressed_button_count == 0) { |
@@ -99,14 +212,18 @@ static const struct sway_seatop_impl seatop_impl = { | |||
99 | .pointer_axis = handle_pointer_axis, | 212 | .pointer_axis = handle_pointer_axis, |
100 | .tablet_tool_tip = handle_tablet_tool_tip, | 213 | .tablet_tool_tip = handle_tablet_tool_tip, |
101 | .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, | ||
102 | .unref = handle_unref, | 219 | .unref = handle_unref, |
103 | .end = handle_end, | 220 | .end = handle_end, |
104 | .allow_set_cursor = true, | 221 | .allow_set_cursor = true, |
105 | }; | 222 | }; |
106 | 223 | ||
107 | 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, |
108 | uint32_t time_msec, double sx, double sy) { | 225 | double sx, double sy) { |
109 | seatop_begin_down_on_surface(seat, con->view->surface, time_msec, sx, sy); | 226 | seatop_begin_down_on_surface(seat, con->view->surface, sx, sy); |
110 | struct seatop_down_event *e = seat->seatop_data; | 227 | struct seatop_down_event *e = seat->seatop_data; |
111 | e->con = con; | 228 | e->con = con; |
112 | 229 | ||
@@ -114,13 +231,20 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, | |||
114 | transaction_commit_dirty(); | 231 | transaction_commit_dirty(); |
115 | } | 232 | } |
116 | 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 | |||
117 | void seatop_begin_down_on_surface(struct sway_seat *seat, | 241 | void seatop_begin_down_on_surface(struct sway_seat *seat, |
118 | struct wlr_surface *surface, uint32_t time_msec, double sx, double sy) { | 242 | struct wlr_surface *surface, double sx, double sy) { |
119 | seatop_end(seat); | 243 | seatop_end(seat); |
120 | 244 | ||
121 | struct seatop_down_event *e = | 245 | struct seatop_down_event *e = |
122 | calloc(1, sizeof(struct seatop_down_event)); | 246 | calloc(1, sizeof(struct seatop_down_event)); |
123 | if (!e) { | 247 | if (!sway_assert(e, "Unable to allocate e")) { |
124 | return; | 248 | return; |
125 | } | 249 | } |
126 | e->con = NULL; | 250 | e->con = NULL; |
@@ -132,6 +256,7 @@ void seatop_begin_down_on_surface(struct sway_seat *seat, | |||
132 | e->ref_ly = seat->cursor->cursor->y; | 256 | e->ref_ly = seat->cursor->cursor->y; |
133 | e->ref_con_lx = sx; | 257 | e->ref_con_lx = sx; |
134 | e->ref_con_ly = sy; | 258 | e->ref_con_ly = sy; |
259 | wl_list_init(&e->point_events); | ||
135 | 260 | ||
136 | seat->seatop_impl = &seatop_impl; | 261 | seat->seatop_impl = &seatop_impl; |
137 | seat->seatop_data = e; | 262 | seat->seatop_data = e; |
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 df683026..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 | ||
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 fc7dfaff..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, |
@@ -34,9 +33,8 @@ static bool sway_switch_trigger_test(enum sway_switch_trigger trigger, | |||
34 | } | 33 | } |
35 | 34 | ||
36 | static void execute_binding(struct sway_switch *sway_switch) { | 35 | static void execute_binding(struct sway_switch *sway_switch) { |
37 | struct sway_seat* seat = sway_switch->seat_device->sway_seat; | 36 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; |
38 | bool input_inhibited = seat->exclusive_client != NULL || | 37 | bool locked = server.session_lock.lock; |
39 | server.session_lock.locked; | ||
40 | 38 | ||
41 | list_t *bindings = config->current_mode->switch_bindings; | 39 | list_t *bindings = config->current_mode->switch_bindings; |
42 | struct sway_switch_binding *matched_binding = NULL; | 40 | struct sway_switch_binding *matched_binding = NULL; |
@@ -53,13 +51,13 @@ static void execute_binding(struct sway_switch *sway_switch) { | |||
53 | continue; | 51 | continue; |
54 | } | 52 | } |
55 | bool binding_locked = binding->flags & BINDING_LOCKED; | 53 | bool binding_locked = binding->flags & BINDING_LOCKED; |
56 | if (!binding_locked && input_inhibited) { | 54 | if (!binding_locked && locked) { |
57 | continue; | 55 | continue; |
58 | } | 56 | } |
59 | 57 | ||
60 | matched_binding = binding; | 58 | matched_binding = binding; |
61 | 59 | ||
62 | if (binding_locked == input_inhibited) { | 60 | if (binding_locked == locked) { |
63 | break; | 61 | break; |
64 | } | 62 | } |
65 | } | 63 | } |
diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 92ede3fa..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) { |
@@ -287,6 +290,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { | |||
287 | tablet_pad->ring.notify = handle_tablet_pad_ring; | 290 | tablet_pad->ring.notify = handle_tablet_pad_ring; |
288 | wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring); | 291 | wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring); |
289 | 292 | ||
293 | #if WLR_HAS_LIBINPUT_BACKEND | ||
290 | /* Search for a sibling tablet */ | 294 | /* Search for a sibling tablet */ |
291 | if (!wlr_input_device_is_libinput(wlr_device)) { | 295 | if (!wlr_input_device_is_libinput(wlr_device)) { |
292 | /* We can only do this on libinput devices */ | 296 | /* We can only do this on libinput devices */ |
@@ -311,6 +315,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { | |||
311 | break; | 315 | break; |
312 | } | 316 | } |
313 | } | 317 | } |
318 | #endif | ||
314 | } | 319 | } |
315 | 320 | ||
316 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { | 321 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { |
@@ -334,14 +339,10 @@ static void handle_pad_tablet_surface_destroy(struct wl_listener *listener, | |||
334 | struct sway_tablet_pad *tablet_pad = | 339 | struct sway_tablet_pad *tablet_pad = |
335 | wl_container_of(listener, tablet_pad, surface_destroy); | 340 | wl_container_of(listener, tablet_pad, surface_destroy); |
336 | 341 | ||
337 | wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, | 342 | sway_tablet_pad_set_focus(tablet_pad, NULL); |
338 | tablet_pad->current_surface); | ||
339 | wl_list_remove(&tablet_pad->surface_destroy.link); | ||
340 | wl_list_init(&tablet_pad->surface_destroy.link); | ||
341 | tablet_pad->current_surface = NULL; | ||
342 | } | 343 | } |
343 | 344 | ||
344 | 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, |
345 | struct wlr_surface *surface) { | 346 | struct wlr_surface *surface) { |
346 | if (!tablet_pad || !tablet_pad->tablet) { | 347 | if (!tablet_pad || !tablet_pad->tablet) { |
347 | return; | 348 | return; |
@@ -360,7 +361,8 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | |||
360 | tablet_pad->current_surface = NULL; | 361 | tablet_pad->current_surface = NULL; |
361 | } | 362 | } |
362 | 363 | ||
363 | 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)) { | ||
364 | return; | 366 | return; |
365 | } | 367 | } |
366 | 368 | ||
@@ -368,7 +370,6 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | |||
368 | tablet_pad->tablet->tablet_v2, surface); | 370 | tablet_pad->tablet->tablet_v2, surface); |
369 | 371 | ||
370 | tablet_pad->current_surface = surface; | 372 | tablet_pad->current_surface = surface; |
371 | wl_list_remove(&tablet_pad->surface_destroy.link); | ||
372 | tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; | 373 | tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; |
373 | wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); | 374 | wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); |
374 | } | 375 | } |
diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 58911c2d..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) { |
@@ -102,6 +109,10 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) { | |||
102 | input_method_destroy); | 109 | input_method_destroy); |
103 | struct wlr_input_method_v2 *context = data; | 110 | struct wlr_input_method_v2 *context = data; |
104 | 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); | ||
105 | relay->input_method = NULL; | 116 | relay->input_method = NULL; |
106 | 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); |
107 | if (text_input) { | 118 | if (text_input) { |
@@ -133,6 +144,11 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, | |||
133 | input->current.content_type.hint, | 144 | input->current.content_type.hint, |
134 | input->current.content_type.purpose); | 145 | input->current.content_type.purpose); |
135 | } | 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 | } | ||
136 | wlr_input_method_v2_send_done(input_method); | 152 | wlr_input_method_v2_send_done(input_method); |
137 | // TODO: pass intent, display popup size | 153 | // TODO: pass intent, display popup size |
138 | } | 154 | } |
@@ -255,6 +271,211 @@ static void relay_handle_text_input(struct wl_listener *listener, | |||
255 | sway_text_input_create(relay, wlr_text_input); | 271 | sway_text_input_create(relay, wlr_text_input); |
256 | } | 272 | } |
257 | 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 | |||
258 | static void relay_handle_input_method(struct wl_listener *listener, | 479 | static void relay_handle_input_method(struct wl_listener *listener, |
259 | void *data) { | 480 | void *data) { |
260 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | 481 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, |
@@ -280,10 +501,13 @@ static void relay_handle_input_method(struct wl_listener *listener, | |||
280 | wl_signal_add(&relay->input_method->events.destroy, | 501 | wl_signal_add(&relay->input_method->events.destroy, |
281 | &relay->input_method_destroy); | 502 | &relay->input_method_destroy); |
282 | 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; | ||
283 | 507 | ||
284 | 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); |
285 | if (text_input) { | 509 | if (text_input) { |
286 | wlr_text_input_v3_send_enter(text_input->input, | 510 | text_input_send_enter(text_input, |
287 | text_input->pending_focused_surface); | 511 | text_input->pending_focused_surface); |
288 | text_input_set_pending_focused_surface(text_input, NULL); | 512 | text_input_set_pending_focused_surface(text_input, NULL); |
289 | } | 513 | } |
@@ -293,6 +517,7 @@ void sway_input_method_relay_init(struct sway_seat *seat, | |||
293 | struct sway_input_method_relay *relay) { | 517 | struct sway_input_method_relay *relay) { |
294 | relay->seat = seat; | 518 | relay->seat = seat; |
295 | wl_list_init(&relay->text_inputs); | 519 | wl_list_init(&relay->text_inputs); |
520 | wl_list_init(&relay->input_popups); | ||
296 | 521 | ||
297 | relay->text_input_new.notify = relay_handle_text_input; | 522 | relay->text_input_new.notify = relay_handle_text_input; |
298 | 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 e422b24d..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,14 +288,13 @@ 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 | ||
245 | static void ipc_json_describe_output(struct sway_output *output, | 297 | static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) { |
246 | json_object *object) { | ||
247 | struct wlr_output *wlr_output = output->wlr_output; | ||
248 | |||
249 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | 298 | json_object_object_add(object, "primary", json_object_new_boolean(false)); |
250 | json_object_object_add(object, "make", | 299 | json_object_object_add(object, "make", |
251 | json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); | 300 | json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); |
@@ -269,11 +318,17 @@ static void ipc_json_describe_output(struct sway_output *output, | |||
269 | json_object_object_add(object, "modes", modes_array); | 318 | json_object_object_add(object, "modes", modes_array); |
270 | } | 319 | } |
271 | 320 | ||
321 | static void ipc_json_describe_output(struct sway_output *output, | ||
322 | json_object *object) { | ||
323 | ipc_json_describe_wlr_output(output->wlr_output, object); | ||
324 | } | ||
325 | |||
272 | static void ipc_json_describe_enabled_output(struct sway_output *output, | 326 | static void ipc_json_describe_enabled_output(struct sway_output *output, |
273 | json_object *object) { | 327 | json_object *object) { |
274 | ipc_json_describe_output(output, object); | 328 | ipc_json_describe_output(output, object); |
275 | 329 | ||
276 | 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)); | ||
277 | json_object_object_add(object, "active", json_object_new_boolean(true)); | 332 | json_object_object_add(object, "active", json_object_new_boolean(true)); |
278 | json_object_object_add(object, "dpms", | 333 | json_object_object_add(object, "dpms", |
279 | json_object_new_boolean(wlr_output->enabled)); | 334 | json_object_new_boolean(wlr_output->enabled)); |
@@ -307,25 +362,26 @@ static void ipc_json_describe_enabled_output(struct sway_output *output, | |||
307 | json_object *modes_array = json_object_new_array(); | 362 | json_object *modes_array = json_object_new_array(); |
308 | struct wlr_output_mode *mode; | 363 | struct wlr_output_mode *mode; |
309 | wl_list_for_each(mode, &wlr_output->modes, link) { | 364 | wl_list_for_each(mode, &wlr_output->modes, link) { |
310 | json_object *mode_object = json_object_new_object(); | 365 | json_object *mode_object = |
311 | json_object_object_add(mode_object, "width", | 366 | ipc_json_output_mode_description(mode); |
312 | json_object_new_int(mode->width)); | ||
313 | json_object_object_add(mode_object, "height", | ||
314 | json_object_new_int(mode->height)); | ||
315 | json_object_object_add(mode_object, "refresh", | ||
316 | json_object_new_int(mode->refresh)); | ||
317 | json_object_array_add(modes_array, mode_object); | 367 | json_object_array_add(modes_array, mode_object); |
318 | } | 368 | } |
319 | 369 | ||
320 | json_object_object_add(object, "modes", modes_array); | 370 | json_object_object_add(object, "modes", modes_array); |
321 | 371 | ||
322 | json_object *current_mode_object = json_object_new_object(); | 372 | json_object *current_mode_object; |
323 | json_object_object_add(current_mode_object, "width", | 373 | if (wlr_output->current_mode != NULL) { |
324 | json_object_new_int(wlr_output->width)); | 374 | current_mode_object = |
325 | json_object_object_add(current_mode_object, "height", | 375 | ipc_json_output_mode_description(wlr_output->current_mode); |
326 | json_object_new_int(wlr_output->height)); | 376 | } else { |
327 | json_object_object_add(current_mode_object, "refresh", | 377 | current_mode_object = json_object_new_object(); |
328 | 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 | } | ||
329 | json_object_object_add(object, "current_mode", current_mode_object); | 385 | json_object_object_add(object, "current_mode", current_mode_object); |
330 | 386 | ||
331 | struct sway_node *parent = node_get_parent(&output->node); | 387 | struct sway_node *parent = node_get_parent(&output->node); |
@@ -351,6 +407,7 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { | |||
351 | 407 | ||
352 | ipc_json_describe_output(output, object); | 408 | ipc_json_describe_output(output, object); |
353 | 409 | ||
410 | json_object_object_add(object, "non_desktop", json_object_new_boolean(false)); | ||
354 | json_object_object_add(object, "type", json_object_new_string("output")); | 411 | json_object_object_add(object, "type", json_object_new_string("output")); |
355 | json_object_object_add(object, "name", | 412 | json_object_object_add(object, "name", |
356 | json_object_new_string(wlr_output->name)); | 413 | json_object_new_string(wlr_output->name)); |
@@ -372,6 +429,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { | |||
372 | return object; | 429 | return object; |
373 | } | 430 | } |
374 | 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 | |||
375 | static json_object *ipc_json_describe_scratchpad_output(void) { | 447 | static json_object *ipc_json_describe_scratchpad_output(void) { |
376 | struct wlr_box box; | 448 | struct wlr_box box; |
377 | root_get_box(root, &box); | 449 | root_get_box(root, &box); |
@@ -507,7 +579,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
507 | 579 | ||
508 | struct wlr_box window_box = { | 580 | struct wlr_box window_box = { |
509 | c->pending.content_x - c->pending.x, | 581 | c->pending.content_x - c->pending.x, |
510 | (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, | 582 | (c->current.border == B_PIXEL) ? c->pending.content_y - c->pending.y : 0, |
511 | c->pending.content_width, | 583 | c->pending.content_width, |
512 | c->pending.content_height | 584 | c->pending.content_height |
513 | }; | 585 | }; |
@@ -551,6 +623,16 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
551 | 623 | ||
552 | json_object_object_add(object, "idle_inhibitors", idle_inhibitors); | 624 | json_object_object_add(object, "idle_inhibitors", idle_inhibitors); |
553 | 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 | |||
554 | #if HAVE_XWAYLAND | 636 | #if HAVE_XWAYLAND |
555 | if (c->view->type == SWAY_VIEW_XWAYLAND) { | 637 | if (c->view->type == SWAY_VIEW_XWAYLAND) { |
556 | json_object_object_add(object, "window", | 638 | json_object_object_add(object, "window", |
@@ -595,7 +677,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
595 | 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) { |
596 | json_object_object_add(object, "name", | 678 | json_object_object_add(object, "name", |
597 | c->title ? json_object_new_string(c->title) : NULL); | 679 | c->title ? json_object_new_string(c->title) : NULL); |
598 | if (container_is_floating(c)) { | 680 | bool floating = container_is_floating(c); |
681 | if (floating) { | ||
599 | json_object_object_add(object, "type", | 682 | json_object_object_add(object, "type", |
600 | json_object_new_string("floating_con")); | 683 | json_object_new_string("floating_con")); |
601 | } | 684 | } |
@@ -613,9 +696,17 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o | |||
613 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); | 696 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); |
614 | 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)); |
615 | 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 | |||
616 | json_object_object_add(object, "fullscreen_mode", | 703 | json_object_object_add(object, "fullscreen_mode", |
617 | json_object_new_int(c->pending.fullscreen_mode)); | 704 | json_object_new_int(c->pending.fullscreen_mode)); |
618 | 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 | |||
619 | struct sway_node *parent = node_get_parent(&c->node); | 710 | struct sway_node *parent = node_get_parent(&c->node); |
620 | struct wlr_box parent_box = {0, 0, 0, 0}; | 711 | struct wlr_box parent_box = {0, 0, 0, 0}; |
621 | 712 | ||
@@ -771,6 +862,7 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { | |||
771 | return object; | 862 | return object; |
772 | } | 863 | } |
773 | 864 | ||
865 | #if WLR_HAS_LIBINPUT_BACKEND | ||
774 | static json_object *describe_libinput_device(struct libinput_device *device) { | 866 | static json_object *describe_libinput_device(struct libinput_device *device) { |
775 | json_object *object = json_object_new_object(); | 867 | json_object *object = json_object_new_object(); |
776 | 868 | ||
@@ -854,6 +946,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
854 | case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: | 946 | case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: |
855 | accel_profile = "adaptive"; | 947 | accel_profile = "adaptive"; |
856 | 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 | ||
857 | } | 954 | } |
858 | json_object_object_add(object, "accel_profile", | 955 | json_object_object_add(object, "accel_profile", |
859 | json_object_new_string(accel_profile)); | 956 | json_object_new_string(accel_profile)); |
@@ -933,6 +1030,17 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
933 | uint32_t button = libinput_device_config_scroll_get_button(device); | 1030 | uint32_t button = libinput_device_config_scroll_get_button(device); |
934 | json_object_object_add(object, "scroll_button", | 1031 | json_object_object_add(object, "scroll_button", |
935 | 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)); | ||
936 | } | 1044 | } |
937 | } | 1045 | } |
938 | 1046 | ||
@@ -949,6 +1057,19 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
949 | json_object_object_add(object, "dwt", json_object_new_string(dwt)); | 1057 | json_object_object_add(object, "dwt", json_object_new_string(dwt)); |
950 | } | 1058 | } |
951 | 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 | |||
952 | if (libinput_device_config_calibration_has_matrix(device)) { | 1073 | if (libinput_device_config_calibration_has_matrix(device)) { |
953 | float matrix[6]; | 1074 | float matrix[6]; |
954 | libinput_device_config_calibration_get_matrix(device, matrix); | 1075 | libinput_device_config_calibration_get_matrix(device, matrix); |
@@ -963,6 +1084,7 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
963 | 1084 | ||
964 | return object; | 1085 | return object; |
965 | } | 1086 | } |
1087 | #endif | ||
966 | 1088 | ||
967 | json_object *ipc_json_describe_input(struct sway_input_device *device) { | 1089 | json_object *ipc_json_describe_input(struct sway_input_device *device) { |
968 | if (!(sway_assert(device, "Device must not be null"))) { | 1090 | if (!(sway_assert(device, "Device must not be null"))) { |
@@ -975,10 +1097,6 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { | |||
975 | json_object_new_string(device->identifier)); | 1097 | json_object_new_string(device->identifier)); |
976 | json_object_object_add(object, "name", | 1098 | json_object_object_add(object, "name", |
977 | json_object_new_string(device->wlr_device->name)); | 1099 | json_object_new_string(device->wlr_device->name)); |
978 | json_object_object_add(object, "vendor", | ||
979 | json_object_new_int(device->wlr_device->vendor)); | ||
980 | json_object_object_add(object, "product", | ||
981 | json_object_new_int(device->wlr_device->product)); | ||
982 | json_object_object_add(object, "type", | 1100 | json_object_object_add(object, "type", |
983 | json_object_new_string( | 1101 | json_object_new_string( |
984 | input_device_get_type(device))); | 1102 | input_device_get_type(device))); |
@@ -1026,12 +1144,18 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { | |||
1026 | json_object_new_double(scroll_factor)); | 1144 | json_object_new_double(scroll_factor)); |
1027 | } | 1145 | } |
1028 | 1146 | ||
1147 | #if WLR_HAS_LIBINPUT_BACKEND | ||
1029 | if (wlr_input_device_is_libinput(device->wlr_device)) { | 1148 | if (wlr_input_device_is_libinput(device->wlr_device)) { |
1030 | struct libinput_device *libinput_dev; | 1149 | struct libinput_device *libinput_dev; |
1031 | libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); | 1150 | libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); |
1032 | json_object_object_add(object, "libinput", | 1151 | json_object_object_add(object, "libinput", |
1033 | 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))); | ||
1034 | } | 1157 | } |
1158 | #endif | ||
1035 | 1159 | ||
1036 | return object; | 1160 | return object; |
1037 | } | 1161 | } |
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 3cbf7889..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); |
@@ -509,6 +507,20 @@ void ipc_event_input(const char *change, struct sway_input_device *device) { | |||
509 | json_object_put(json); | 507 | json_object_put(json); |
510 | } | 508 | } |
511 | 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 | |||
512 | 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) { |
513 | struct ipc_client *client = data; | 525 | struct ipc_client *client = data; |
514 | 526 | ||
@@ -685,6 +697,12 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
685 | ipc_json_describe_disabled_output(output)); | 697 | ipc_json_describe_disabled_output(output)); |
686 | } | 698 | } |
687 | } | 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 | |||
688 | const char *json_string = json_object_to_json_string(outputs); | 706 | const char *json_string = json_object_to_json_string(outputs); |
689 | ipc_send_reply(client, payload_type, json_string, | 707 | ipc_send_reply(client, payload_type, json_string, |
690 | (uint32_t)strlen(json_string)); | 708 | (uint32_t)strlen(json_string)); |
@@ -720,6 +738,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
720 | 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)); |
721 | if (strcmp(event_type, "workspace") == 0) { | 739 | if (strcmp(event_type, "workspace") == 0) { |
722 | 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); | ||
723 | } else if (strcmp(event_type, "barconfig_update") == 0) { | 743 | } else if (strcmp(event_type, "barconfig_update") == 0) { |
724 | client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); | 744 | client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); |
725 | } else if (strcmp(event_type, "bar_state_update") == 0) { | 745 | } else if (strcmp(event_type, "bar_state_update") == 0) { |
@@ -904,7 +924,6 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
904 | 924 | ||
905 | exit_cleanup: | 925 | exit_cleanup: |
906 | free(buf); | 926 | free(buf); |
907 | return; | ||
908 | } | 927 | } |
909 | 928 | ||
910 | 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, |
diff --git a/sway/lock.c b/sway/lock.c index 04f80079..289e8ca4 100644 --- a/sway/lock.c +++ b/sway/lock.c | |||
@@ -1,101 +1,229 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include <wlr/types/wlr_scene.h> | ||
3 | #include <wlr/types/wlr_session_lock_v1.h> | ||
3 | #include "log.h" | 4 | #include "log.h" |
5 | #include "sway/input/cursor.h" | ||
4 | #include "sway/input/keyboard.h" | 6 | #include "sway/input/keyboard.h" |
5 | #include "sway/input/seat.h" | 7 | #include "sway/input/seat.h" |
8 | #include "sway/layers.h" | ||
6 | #include "sway/output.h" | 9 | #include "sway/output.h" |
7 | #include "sway/server.h" | 10 | #include "sway/server.h" |
8 | 11 | ||
9 | struct sway_session_lock_surface { | 12 | struct sway_session_lock_output { |
10 | struct wlr_session_lock_surface_v1 *lock_surface; | 13 | struct wlr_scene_tree *tree; |
14 | struct wlr_scene_rect *background; | ||
15 | struct sway_session_lock *lock; | ||
16 | |||
11 | struct sway_output *output; | 17 | struct sway_output *output; |
12 | struct wlr_surface *surface; | 18 | |
13 | struct wl_listener map; | 19 | struct wl_list link; // sway_session_lock::outputs |
20 | |||
14 | struct wl_listener destroy; | 21 | struct wl_listener destroy; |
15 | struct wl_listener surface_commit; | 22 | struct wl_listener commit; |
16 | struct wl_listener output_mode; | 23 | |
17 | struct wl_listener output_commit; | 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; | ||
18 | }; | 29 | }; |
19 | 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 | |||
20 | static void handle_surface_map(struct wl_listener *listener, void *data) { | 62 | static void handle_surface_map(struct wl_listener *listener, void *data) { |
21 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map); | 63 | struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map); |
22 | sway_force_focus(surf->surface); | 64 | if (surf->lock->focused == NULL) { |
23 | output_damage_whole(surf->output); | 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); | ||
24 | } | 119 | } |
25 | 120 | ||
26 | static void handle_surface_commit(struct wl_listener *listener, void *data) { | 121 | static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) { |
27 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit); | 122 | if (output->surface) { |
28 | output_damage_surface(surf->output, 0, 0, surf->surface, false); | 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); | ||
29 | } | 133 | } |
30 | 134 | ||
31 | static void handle_output_mode(struct wl_listener *listener, void *data) { | 135 | static void lock_node_handle_destroy(struct wl_listener *listener, void *data) { |
32 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_mode); | 136 | struct sway_session_lock_output *output = |
33 | wlr_session_lock_surface_v1_configure(surf->lock_surface, | 137 | wl_container_of(listener, output, destroy); |
34 | surf->output->width, surf->output->height); | 138 | sway_session_lock_output_destroy(output); |
35 | } | 139 | } |
36 | 140 | ||
37 | static void handle_output_commit(struct wl_listener *listener, void *data) { | 141 | static void lock_output_handle_commit(struct wl_listener *listener, void *data) { |
38 | struct wlr_output_event_commit *event = data; | 142 | struct wlr_output_event_commit *event = data; |
39 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit); | 143 | struct sway_session_lock_output *output = |
40 | if (event->committed & ( | 144 | wl_container_of(listener, output, commit); |
145 | if (event->state->committed & ( | ||
41 | WLR_OUTPUT_STATE_MODE | | 146 | WLR_OUTPUT_STATE_MODE | |
42 | WLR_OUTPUT_STATE_SCALE | | 147 | WLR_OUTPUT_STATE_SCALE | |
43 | WLR_OUTPUT_STATE_TRANSFORM)) { | 148 | WLR_OUTPUT_STATE_TRANSFORM)) { |
44 | wlr_session_lock_surface_v1_configure(surf->lock_surface, | 149 | lock_output_reconfigure(output); |
45 | surf->output->width, surf->output->height); | ||
46 | } | 150 | } |
47 | } | 151 | } |
48 | 152 | ||
49 | static void handle_surface_destroy(struct wl_listener *listener, void *data) { | 153 | static struct sway_session_lock_output *session_lock_output_create( |
50 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy); | 154 | struct sway_session_lock *lock, struct sway_output *output) { |
51 | wl_list_remove(&surf->map.link); | 155 | struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output)); |
52 | wl_list_remove(&surf->destroy.link); | 156 | if (!lock_output) { |
53 | wl_list_remove(&surf->surface_commit.link); | 157 | sway_log(SWAY_ERROR, "failed to allocate a session lock output"); |
54 | wl_list_remove(&surf->output_mode.link); | 158 | return NULL; |
55 | wl_list_remove(&surf->output_commit.link); | 159 | } |
56 | output_damage_whole(surf->output); | 160 | |
57 | free(surf); | 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; | ||
58 | } | 197 | } |
59 | 198 | ||
60 | static void handle_new_surface(struct wl_listener *listener, void *data) { | 199 | static void sway_session_lock_destroy(struct sway_session_lock* lock) { |
61 | struct wlr_session_lock_surface_v1 *lock_surface = data; | 200 | struct sway_session_lock_output *lock_output, *tmp_lock_output; |
62 | struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf)); | 201 | wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) { |
63 | if (surf == NULL) { | 202 | // destroying the node will also destroy the whole lock output |
64 | return; | 203 | wlr_scene_node_destroy(&lock_output->tree->node); |
65 | } | 204 | } |
66 | 205 | ||
67 | sway_log(SWAY_DEBUG, "new lock layer surface"); | 206 | if (server.session_lock.lock == lock) { |
207 | server.session_lock.lock = NULL; | ||
208 | } | ||
68 | 209 | ||
69 | struct sway_output *output = lock_surface->output->data; | 210 | if (!lock->abandoned) { |
70 | wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height); | 211 | wl_list_remove(&lock->destroy.link); |
71 | 212 | wl_list_remove(&lock->unlock.link); | |
72 | surf->lock_surface = lock_surface; | 213 | wl_list_remove(&lock->new_surface.link); |
73 | surf->surface = lock_surface->surface; | 214 | } |
74 | surf->output = output; | 215 | |
75 | surf->map.notify = handle_surface_map; | 216 | free(lock); |
76 | wl_signal_add(&lock_surface->events.map, &surf->map); | ||
77 | surf->destroy.notify = handle_surface_destroy; | ||
78 | wl_signal_add(&lock_surface->events.destroy, &surf->destroy); | ||
79 | surf->surface_commit.notify = handle_surface_commit; | ||
80 | wl_signal_add(&surf->surface->events.commit, &surf->surface_commit); | ||
81 | surf->output_mode.notify = handle_output_mode; | ||
82 | wl_signal_add(&output->wlr_output->events.mode, &surf->output_mode); | ||
83 | surf->output_commit.notify = handle_output_commit; | ||
84 | wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit); | ||
85 | } | 217 | } |
86 | 218 | ||
87 | static void handle_unlock(struct wl_listener *listener, void *data) { | 219 | static void handle_unlock(struct wl_listener *listener, void *data) { |
220 | struct sway_session_lock *lock = wl_container_of(listener, lock, unlock); | ||
88 | sway_log(SWAY_DEBUG, "session unlocked"); | 221 | sway_log(SWAY_DEBUG, "session unlocked"); |
89 | server.session_lock.locked = false; | ||
90 | server.session_lock.lock = NULL; | ||
91 | 222 | ||
92 | wl_list_remove(&server.session_lock.lock_new_surface.link); | 223 | sway_session_lock_destroy(lock); |
93 | wl_list_remove(&server.session_lock.lock_unlock.link); | ||
94 | wl_list_remove(&server.session_lock.lock_destroy.link); | ||
95 | 224 | ||
96 | struct sway_seat *seat; | 225 | struct sway_seat *seat; |
97 | wl_list_for_each(seat, &server.input->seats, link) { | 226 | wl_list_for_each(seat, &server.input->seats, link) { |
98 | seat_set_exclusive_client(seat, NULL); | ||
99 | // copied from seat_set_focus_layer -- deduplicate? | 227 | // copied from seat_set_focus_layer -- deduplicate? |
100 | struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); | 228 | struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); |
101 | if (previous) { | 229 | if (previous) { |
@@ -105,31 +233,28 @@ static void handle_unlock(struct wl_listener *listener, void *data) { | |||
105 | } | 233 | } |
106 | } | 234 | } |
107 | 235 | ||
108 | // redraw everything | 236 | // Triggers a refocus of the topmost surface layer if necessary |
237 | // TODO: Make layer surface focus per-output based on cursor position | ||
109 | for (int i = 0; i < root->outputs->length; ++i) { | 238 | for (int i = 0; i < root->outputs->length; ++i) { |
110 | struct sway_output *output = root->outputs->items[i]; | 239 | struct sway_output *output = root->outputs->items[i]; |
111 | output_damage_whole(output); | 240 | arrange_layers(output); |
112 | } | 241 | } |
113 | } | 242 | } |
114 | 243 | ||
115 | static void handle_abandon(struct wl_listener *listener, void *data) { | 244 | static void handle_abandon(struct wl_listener *listener, void *data) { |
245 | struct sway_session_lock *lock = wl_container_of(listener, lock, destroy); | ||
116 | sway_log(SWAY_INFO, "session lock abandoned"); | 246 | sway_log(SWAY_INFO, "session lock abandoned"); |
117 | server.session_lock.lock = NULL; | ||
118 | |||
119 | wl_list_remove(&server.session_lock.lock_new_surface.link); | ||
120 | wl_list_remove(&server.session_lock.lock_unlock.link); | ||
121 | wl_list_remove(&server.session_lock.lock_destroy.link); | ||
122 | 247 | ||
123 | struct sway_seat *seat; | 248 | struct sway_session_lock_output *lock_output; |
124 | wl_list_for_each(seat, &server.input->seats, link) { | 249 | wl_list_for_each(lock_output, &lock->outputs, link) { |
125 | seat->exclusive_client = NULL; | 250 | wlr_scene_rect_set_color(lock_output->background, |
251 | (float[4]){ 1.f, 0.f, 0.f, 1.f }); | ||
126 | } | 252 | } |
127 | 253 | ||
128 | // redraw everything | 254 | lock->abandoned = true; |
129 | for (int i = 0; i < root->outputs->length; ++i) { | 255 | wl_list_remove(&lock->destroy.link); |
130 | struct sway_output *output = root->outputs->items[i]; | 256 | wl_list_remove(&lock->unlock.link); |
131 | output_damage_whole(output); | 257 | wl_list_remove(&lock->new_surface.link); |
132 | } | ||
133 | } | 258 | } |
134 | 259 | ||
135 | static void handle_session_lock(struct wl_listener *listener, void *data) { | 260 | static void handle_session_lock(struct wl_listener *listener, void *data) { |
@@ -137,44 +262,89 @@ static void handle_session_lock(struct wl_listener *listener, void *data) { | |||
137 | struct wl_client *client = wl_resource_get_client(lock->resource); | 262 | struct wl_client *client = wl_resource_get_client(lock->resource); |
138 | 263 | ||
139 | if (server.session_lock.lock) { | 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"); | ||
140 | wlr_session_lock_v1_destroy(lock); | 278 | wlr_session_lock_v1_destroy(lock); |
141 | return; | 279 | return; |
142 | } | 280 | } |
143 | 281 | ||
282 | wl_list_init(&sway_lock->outputs); | ||
283 | |||
144 | sway_log(SWAY_DEBUG, "session locked"); | 284 | sway_log(SWAY_DEBUG, "session locked"); |
145 | server.session_lock.locked = true; | ||
146 | server.session_lock.lock = lock; | ||
147 | 285 | ||
148 | struct sway_seat *seat; | 286 | struct sway_seat *seat; |
149 | wl_list_for_each(seat, &server.input->seats, link) { | 287 | wl_list_for_each(seat, &server.input->seats, link) { |
150 | seat_set_exclusive_client(seat, client); | 288 | seat_unfocus_unless_client(seat, client); |
151 | } | 289 | } |
152 | 290 | ||
153 | wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface); | 291 | struct sway_output *output; |
154 | wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock); | 292 | wl_list_for_each(output, &root->all_outputs, link) { |
155 | wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy); | 293 | sway_session_lock_add_output(sway_lock, output); |
294 | } | ||
156 | 295 | ||
157 | wlr_session_lock_v1_send_locked(lock); | 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); | ||
158 | 302 | ||
159 | // redraw everything | 303 | wlr_session_lock_v1_send_locked(lock); |
160 | for (int i = 0; i < root->outputs->length; ++i) { | 304 | server.session_lock.lock = sway_lock; |
161 | struct sway_output *output = root->outputs->items[i]; | ||
162 | output_damage_whole(output); | ||
163 | } | ||
164 | } | 305 | } |
165 | 306 | ||
166 | static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { | 307 | static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { |
167 | assert(server.session_lock.lock == NULL); | 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 | |||
168 | wl_list_remove(&server.session_lock.new_lock.link); | 313 | wl_list_remove(&server.session_lock.new_lock.link); |
169 | wl_list_remove(&server.session_lock.manager_destroy.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; | ||
170 | } | 343 | } |
171 | 344 | ||
172 | void sway_session_lock_init(void) { | 345 | void sway_session_lock_init(void) { |
173 | server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); | 346 | server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); |
174 | 347 | ||
175 | server.session_lock.lock_new_surface.notify = handle_new_surface; | ||
176 | server.session_lock.lock_unlock.notify = handle_unlock; | ||
177 | server.session_lock.lock_destroy.notify = handle_abandon; | ||
178 | server.session_lock.new_lock.notify = handle_session_lock; | 348 | server.session_lock.new_lock.notify = handle_session_lock; |
179 | server.session_lock.manager_destroy.notify = handle_session_lock_destroy; | 349 | server.session_lock.manager_destroy.notify = handle_session_lock_destroy; |
180 | wl_signal_add(&server.session_lock.manager->events.new_lock, | 350 | wl_signal_add(&server.session_lock.manager->events.new_lock, |
diff --git a/sway/main.c b/sway/main.c index 85bc2f1c..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> |
@@ -49,44 +48,6 @@ void sig_handler(int signal) { | |||
49 | sway_terminate(EXIT_SUCCESS); | 48 | sway_terminate(EXIT_SUCCESS); |
50 | } | 49 | } |
51 | 50 | ||
52 | void detect_proprietary(int allow_unsupported_gpu) { | ||
53 | FILE *f = fopen("/proc/modules", "r"); | ||
54 | if (!f) { | ||
55 | return; | ||
56 | } | ||
57 | char *line = NULL; | ||
58 | size_t line_size = 0; | ||
59 | while (getline(&line, &line_size, f) != -1) { | ||
60 | if (strncmp(line, "nvidia ", 7) == 0) { | ||
61 | if (allow_unsupported_gpu) { | ||
62 | sway_log(SWAY_ERROR, | ||
63 | "!!! Proprietary Nvidia drivers are in use !!!"); | ||
64 | } else { | ||
65 | sway_log(SWAY_ERROR, | ||
66 | "Proprietary Nvidia drivers are NOT supported. " | ||
67 | "Use Nouveau. To launch sway anyway, launch with " | ||
68 | "--unsupported-gpu and DO NOT report issues."); | ||
69 | exit(EXIT_FAILURE); | ||
70 | } | ||
71 | break; | ||
72 | } | ||
73 | if (strstr(line, "fglrx")) { | ||
74 | if (allow_unsupported_gpu) { | ||
75 | sway_log(SWAY_ERROR, | ||
76 | "!!! Proprietary AMD drivers are in use !!!"); | ||
77 | } else { | ||
78 | sway_log(SWAY_ERROR, "Proprietary AMD drivers do NOT support " | ||
79 | "Wayland. Use radeon. To try anyway, launch sway with " | ||
80 | "--unsupported-gpu and DO NOT report issues."); | ||
81 | exit(EXIT_FAILURE); | ||
82 | } | ||
83 | break; | ||
84 | } | ||
85 | } | ||
86 | free(line); | ||
87 | fclose(f); | ||
88 | } | ||
89 | |||
90 | void run_as_ipc_client(char *command, char *socket_path) { | 51 | void run_as_ipc_client(char *command, char *socket_path) { |
91 | int socketfd = ipc_open_socket(socket_path); | 52 | int socketfd = ipc_open_socket(socket_path); |
92 | uint32_t len = strlen(command); | 53 | uint32_t len = strlen(command); |
@@ -192,11 +153,7 @@ void restore_nofile_limit(void) { | |||
192 | } | 153 | } |
193 | 154 | ||
194 | void enable_debug_flag(const char *flag) { | 155 | void enable_debug_flag(const char *flag) { |
195 | if (strcmp(flag, "damage=highlight") == 0) { | 156 | if (strcmp(flag, "noatomic") == 0) { |
196 | debug.damage = DAMAGE_HIGHLIGHT; | ||
197 | } else if (strcmp(flag, "damage=rerender") == 0) { | ||
198 | debug.damage = DAMAGE_RERENDER; | ||
199 | } else if (strcmp(flag, "noatomic") == 0) { | ||
200 | debug.noatomic = true; | 157 | debug.noatomic = true; |
201 | } else if (strcmp(flag, "txn-wait") == 0) { | 158 | } else if (strcmp(flag, "txn-wait") == 0) { |
202 | debug.txn_wait = true; | 159 | debug.txn_wait = true; |
@@ -204,8 +161,8 @@ void enable_debug_flag(const char *flag) { | |||
204 | debug.txn_timings = true; | 161 | debug.txn_timings = true; |
205 | } else if (strncmp(flag, "txn-timeout=", 12) == 0) { | 162 | } else if (strncmp(flag, "txn-timeout=", 12) == 0) { |
206 | server.txn_timeout_ms = atoi(&flag[12]); | 163 | server.txn_timeout_ms = atoi(&flag[12]); |
207 | } else if (strcmp(flag, "noscanout") == 0) { | 164 | } else if (strcmp(flag, "legacy-wl-drm") == 0) { |
208 | debug.noscanout = true; | 165 | debug.legacy_wl_drm = true; |
209 | } else { | 166 | } else { |
210 | sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); | 167 | sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); |
211 | } | 168 | } |
@@ -255,7 +212,7 @@ static const char usage[] = | |||
255 | "\n"; | 212 | "\n"; |
256 | 213 | ||
257 | int main(int argc, char **argv) { | 214 | int main(int argc, char **argv) { |
258 | static bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false; | 215 | static bool verbose = false, debug = false, validate = false; |
259 | 216 | ||
260 | char *config_path = NULL; | 217 | char *config_path = NULL; |
261 | 218 | ||
@@ -363,7 +320,6 @@ int main(int argc, char **argv) { | |||
363 | return 0; | 320 | return 0; |
364 | } | 321 | } |
365 | 322 | ||
366 | detect_proprietary(allow_unsupported_gpu); | ||
367 | increase_nofile_limit(); | 323 | increase_nofile_limit(); |
368 | 324 | ||
369 | // handle SIGTERM signals | 325 | // handle SIGTERM signals |
@@ -375,12 +331,14 @@ int main(int argc, char **argv) { | |||
375 | 331 | ||
376 | sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); | 332 | sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); |
377 | 333 | ||
378 | root = root_create(); | ||
379 | |||
380 | if (!server_init(&server)) { | 334 | if (!server_init(&server)) { |
381 | return 1; | 335 | return 1; |
382 | } | 336 | } |
383 | 337 | ||
338 | if (server.linux_dmabuf_v1) { | ||
339 | wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1); | ||
340 | } | ||
341 | |||
384 | if (validate) { | 342 | if (validate) { |
385 | bool valid = load_main_config(config_path, false, true); | 343 | bool valid = load_main_config(config_path, false, true); |
386 | free(config_path); | 344 | free(config_path); |
diff --git a/sway/meson.build b/sway/meson.build index ced7419c..d937e425 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -8,24 +8,23 @@ sway_sources = files( | |||
8 | 'lock.c', | 8 | 'lock.c', |
9 | 'main.c', | 9 | 'main.c', |
10 | 'realtime.c', | 10 | 'realtime.c', |
11 | 'scene_descriptor.c', | ||
11 | 'server.c', | 12 | 'server.c', |
13 | 'sway_text_node.c', | ||
12 | 'swaynag.c', | 14 | 'swaynag.c', |
13 | 'xdg_activation_v1.c', | 15 | 'xdg_activation_v1.c', |
14 | 'xdg_decoration.c', | 16 | 'xdg_decoration.c', |
15 | 17 | ||
16 | 'desktop/desktop.c', | ||
17 | 'desktop/idle_inhibit_v1.c', | 18 | 'desktop/idle_inhibit_v1.c', |
18 | 'desktop/layer_shell.c', | 19 | 'desktop/layer_shell.c', |
19 | 'desktop/output.c', | 20 | 'desktop/output.c', |
20 | 'desktop/render.c', | ||
21 | 'desktop/surface.c', | ||
22 | 'desktop/transaction.c', | 21 | 'desktop/transaction.c', |
23 | 'desktop/xdg_shell.c', | 22 | 'desktop/xdg_shell.c', |
23 | 'desktop/launcher.c', | ||
24 | 24 | ||
25 | 'input/input-manager.c', | 25 | 'input/input-manager.c', |
26 | 'input/cursor.c', | 26 | 'input/cursor.c', |
27 | 'input/keyboard.c', | 27 | 'input/keyboard.c', |
28 | 'input/libinput.c', | ||
29 | 'input/seat.c', | 28 | 'input/seat.c', |
30 | 'input/seatop_default.c', | 29 | 'input/seatop_default.c', |
31 | 'input/seatop_down.c', | 30 | 'input/seatop_down.c', |
@@ -86,6 +85,7 @@ sway_sources = files( | |||
86 | 'commands/nop.c', | 85 | 'commands/nop.c', |
87 | 'commands/output.c', | 86 | 'commands/output.c', |
88 | 'commands/popup_during_fullscreen.c', | 87 | 'commands/popup_during_fullscreen.c', |
88 | 'commands/primary_selection.c', | ||
89 | 'commands/reload.c', | 89 | 'commands/reload.c', |
90 | 'commands/rename.c', | 90 | 'commands/rename.c', |
91 | 'commands/resize.c', | 91 | 'commands/resize.c', |
@@ -157,6 +157,7 @@ sway_sources = files( | |||
157 | 'commands/input/drag.c', | 157 | 'commands/input/drag.c', |
158 | 'commands/input/drag_lock.c', | 158 | 'commands/input/drag_lock.c', |
159 | 'commands/input/dwt.c', | 159 | 'commands/input/dwt.c', |
160 | 'commands/input/dwtp.c', | ||
160 | 'commands/input/events.c', | 161 | 'commands/input/events.c', |
161 | 'commands/input/left_handed.c', | 162 | 'commands/input/left_handed.c', |
162 | 'commands/input/map_from_region.c', | 163 | 'commands/input/map_from_region.c', |
@@ -165,9 +166,11 @@ sway_sources = files( | |||
165 | 'commands/input/middle_emulation.c', | 166 | 'commands/input/middle_emulation.c', |
166 | 'commands/input/natural_scroll.c', | 167 | 'commands/input/natural_scroll.c', |
167 | 'commands/input/pointer_accel.c', | 168 | 'commands/input/pointer_accel.c', |
169 | 'commands/input/rotation_angle.c', | ||
168 | 'commands/input/repeat_delay.c', | 170 | 'commands/input/repeat_delay.c', |
169 | 'commands/input/repeat_rate.c', | 171 | 'commands/input/repeat_rate.c', |
170 | 'commands/input/scroll_button.c', | 172 | 'commands/input/scroll_button.c', |
173 | 'commands/input/scroll_button_lock.c', | ||
171 | 'commands/input/scroll_factor.c', | 174 | 'commands/input/scroll_factor.c', |
172 | 'commands/input/scroll_method.c', | 175 | 'commands/input/scroll_method.c', |
173 | 'commands/input/tap.c', | 176 | 'commands/input/tap.c', |
@@ -198,6 +201,7 @@ sway_sources = files( | |||
198 | 'commands/output/subpixel.c', | 201 | 'commands/output/subpixel.c', |
199 | 'commands/output/toggle.c', | 202 | 'commands/output/toggle.c', |
200 | 'commands/output/transform.c', | 203 | 'commands/output/transform.c', |
204 | 'commands/output/unplug.c', | ||
201 | 205 | ||
202 | 'tree/arrange.c', | 206 | 'tree/arrange.c', |
203 | 'tree/container.c', | 207 | 'tree/container.c', |
@@ -218,24 +222,26 @@ sway_deps = [ | |||
218 | math, | 222 | math, |
219 | pango, | 223 | pango, |
220 | pcre2, | 224 | pcre2, |
221 | glesv2, | ||
222 | pixman, | 225 | pixman, |
223 | server_protos, | ||
224 | threads, | 226 | threads, |
225 | wayland_server, | 227 | wayland_server, |
226 | wlroots, | 228 | wlroots, |
227 | xkbcommon, | 229 | xkbcommon, |
230 | xcb, | ||
228 | xcb_icccm, | 231 | xcb_icccm, |
229 | ] | 232 | ] |
230 | 233 | ||
231 | if have_xwayland | 234 | if have_xwayland |
232 | sway_sources += 'desktop/xwayland.c' | 235 | sway_sources += 'desktop/xwayland.c' |
233 | sway_deps += xcb | 236 | endif |
237 | |||
238 | if wlroots_features['libinput_backend'] | ||
239 | sway_sources += 'input/libinput.c' | ||
234 | endif | 240 | endif |
235 | 241 | ||
236 | executable( | 242 | executable( |
237 | 'sway', | 243 | 'sway', |
238 | sway_sources, | 244 | sway_sources + wl_protos_src, |
239 | include_directories: [sway_inc], | 245 | include_directories: [sway_inc], |
240 | dependencies: sway_deps, | 246 | dependencies: sway_deps, |
241 | link_with: [lib_sway_common], | 247 | link_with: [lib_sway_common], |
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 a87fc7cf..d159dc9b 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,24 +6,34 @@ | |||
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/session.h> | ||
11 | #include <wlr/config.h> | 9 | #include <wlr/config.h> |
10 | #include <wlr/render/allocator.h> | ||
12 | #include <wlr/render/wlr_renderer.h> | 11 | #include <wlr/render/wlr_renderer.h> |
13 | #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> | ||
14 | #include <wlr/types/wlr_data_control_v1.h> | 15 | #include <wlr/types/wlr_data_control_v1.h> |
15 | #include <wlr/types/wlr_drm_lease_v1.h> | 16 | #include <wlr/types/wlr_data_device.h> |
16 | #include <wlr/types/wlr_drm.h> | 17 | #include <wlr/types/wlr_drm.h> |
17 | #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> | ||
18 | #include <wlr/types/wlr_gamma_control_v1.h> | 22 | #include <wlr/types/wlr_gamma_control_v1.h> |
19 | #include <wlr/types/wlr_idle.h> | 23 | #include <wlr/types/wlr_idle_notify_v1.h> |
20 | #include <wlr/types/wlr_layer_shell_v1.h> | 24 | #include <wlr/types/wlr_layer_shell_v1.h> |
21 | #include <wlr/types/wlr_linux_dmabuf_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> | ||
22 | #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> | ||
23 | #include <wlr/types/wlr_primary_selection_v1.h> | 30 | #include <wlr/types/wlr_primary_selection_v1.h> |
24 | #include <wlr/types/wlr_relative_pointer_v1.h> | 31 | #include <wlr/types/wlr_relative_pointer_v1.h> |
25 | #include <wlr/types/wlr_screencopy_v1.h> | 32 | #include <wlr/types/wlr_screencopy_v1.h> |
26 | #include <wlr/types/wlr_single_pixel_buffer_v1.h> | 33 | #include <wlr/types/wlr_security_context_v1.h> |
27 | #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> | ||
28 | #include <wlr/types/wlr_subcompositor.h> | 37 | #include <wlr/types/wlr_subcompositor.h> |
29 | #include <wlr/types/wlr_tablet_v2.h> | 38 | #include <wlr/types/wlr_tablet_v2.h> |
30 | #include <wlr/types/wlr_viewporter.h> | 39 | #include <wlr/types/wlr_viewporter.h> |
@@ -35,6 +44,7 @@ | |||
35 | #include <wlr/types/wlr_xdg_foreign_v1.h> | 44 | #include <wlr/types/wlr_xdg_foreign_v1.h> |
36 | #include <wlr/types/wlr_xdg_foreign_v2.h> | 45 | #include <wlr/types/wlr_xdg_foreign_v2.h> |
37 | #include <wlr/types/wlr_xdg_output_v1.h> | 46 | #include <wlr/types/wlr_xdg_output_v1.h> |
47 | #include <xf86drm.h> | ||
38 | #include "config.h" | 48 | #include "config.h" |
39 | #include "list.h" | 49 | #include "list.h" |
40 | #include "log.h" | 50 | #include "log.h" |
@@ -43,11 +53,25 @@ | |||
43 | #include "sway/input/input-manager.h" | 53 | #include "sway/input/input-manager.h" |
44 | #include "sway/output.h" | 54 | #include "sway/output.h" |
45 | #include "sway/server.h" | 55 | #include "sway/server.h" |
56 | #include "sway/input/cursor.h" | ||
46 | #include "sway/tree/root.h" | 57 | #include "sway/tree/root.h" |
58 | |||
47 | #if HAVE_XWAYLAND | 59 | #if HAVE_XWAYLAND |
60 | #include <wlr/xwayland/shell.h> | ||
48 | #include "sway/xwayland.h" | 61 | #include "sway/xwayland.h" |
49 | #endif | 62 | #endif |
50 | 63 | ||
64 | #if WLR_HAS_DRM_BACKEND | ||
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 | ||
51 | static void handle_drm_lease_request(struct wl_listener *listener, void *data) { | 75 | static void handle_drm_lease_request(struct wl_listener *listener, void *data) { |
52 | /* We only offer non-desktop outputs, but in the future we might want to do | 76 | /* We only offer non-desktop outputs, but in the future we might want to do |
53 | * more logic here. */ | 77 | * more logic here. */ |
@@ -59,32 +83,170 @@ static void handle_drm_lease_request(struct wl_listener *listener, void *data) { | |||
59 | wlr_drm_lease_request_v1_reject(req); | 83 | wlr_drm_lease_request_v1_reject(req); |
60 | } | 84 | } |
61 | } | 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; | ||
62 | 199 | ||
63 | #define SWAY_XDG_SHELL_VERSION 2 | 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 | } | ||
64 | 214 | ||
65 | bool server_init(struct sway_server *server) { | 215 | bool server_init(struct sway_server *server) { |
66 | sway_log(SWAY_DEBUG, "Initializing Wayland server"); | 216 | sway_log(SWAY_DEBUG, "Initializing Wayland server"); |
67 | server->wl_display = wl_display_create(); | 217 | server->wl_display = wl_display_create(); |
68 | 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); |
69 | server->backend = wlr_backend_autocreate(server->wl_display); | ||
70 | 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); | ||
71 | if (!server->backend) { | 225 | if (!server->backend) { |
72 | sway_log(SWAY_ERROR, "Unable to create backend"); | 226 | sway_log(SWAY_ERROR, "Unable to create backend"); |
73 | return false; | 227 | return false; |
74 | } | 228 | } |
75 | 229 | ||
230 | wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL); | ||
231 | |||
76 | server->renderer = wlr_renderer_autocreate(server->backend); | 232 | server->renderer = wlr_renderer_autocreate(server->backend); |
77 | if (!server->renderer) { | 233 | if (!server->renderer) { |
78 | sway_log(SWAY_ERROR, "Failed to create renderer"); | 234 | sway_log(SWAY_ERROR, "Failed to create renderer"); |
79 | return false; | 235 | return false; |
80 | } | 236 | } |
81 | 237 | ||
238 | server->renderer_lost.notify = handle_renderer_lost; | ||
239 | wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); | ||
240 | |||
82 | wlr_renderer_init_wl_shm(server->renderer, server->wl_display); | 241 | wlr_renderer_init_wl_shm(server->renderer, server->wl_display); |
83 | 242 | ||
84 | if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { | 243 | if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { |
244 | server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( | ||
245 | server->wl_display, 4, server->renderer); | ||
246 | } | ||
247 | if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL && | ||
248 | debug.legacy_wl_drm) { | ||
85 | wlr_drm_create(server->wl_display, server->renderer); | 249 | wlr_drm_create(server->wl_display, server->renderer); |
86 | server->linux_dmabuf_v1 = | ||
87 | wlr_linux_dmabuf_v1_create(server->wl_display, server->renderer); | ||
88 | } | 250 | } |
89 | 251 | ||
90 | server->allocator = wlr_allocator_autocreate(server->backend, | 252 | server->allocator = wlr_allocator_autocreate(server->backend, |
@@ -94,18 +256,19 @@ bool server_init(struct sway_server *server) { | |||
94 | return false; | 256 | return false; |
95 | } | 257 | } |
96 | 258 | ||
97 | server->compositor = wlr_compositor_create(server->wl_display, | 259 | server->compositor = wlr_compositor_create(server->wl_display, 6, |
98 | server->renderer); | 260 | server->renderer); |
99 | server->compositor_new_surface.notify = handle_compositor_new_surface; | ||
100 | wl_signal_add(&server->compositor->events.new_surface, | ||
101 | &server->compositor_new_surface); | ||
102 | 261 | ||
103 | wlr_subcompositor_create(server->wl_display); | 262 | wlr_subcompositor_create(server->wl_display); |
104 | 263 | ||
105 | server->data_device_manager = | 264 | server->data_device_manager = |
106 | wlr_data_device_manager_create(server->wl_display); | 265 | wlr_data_device_manager_create(server->wl_display); |
107 | 266 | ||
108 | wlr_gamma_control_manager_v1_create(server->wl_display); | 267 | server->gamma_control_manager_v1 = |
268 | wlr_gamma_control_manager_v1_create(server->wl_display); | ||
269 | server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma; | ||
270 | wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma, | ||
271 | &server->gamma_control_set_gamma); | ||
109 | 272 | ||
110 | server->new_output.notify = handle_new_output; | 273 | server->new_output.notify = handle_new_output; |
111 | wl_signal_add(&server->backend->events.new_output, &server->new_output); | 274 | wl_signal_add(&server->backend->events.new_output, &server->new_output); |
@@ -115,20 +278,20 @@ bool server_init(struct sway_server *server) { | |||
115 | 278 | ||
116 | wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); | 279 | wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); |
117 | 280 | ||
118 | server->idle = wlr_idle_create(server->wl_display); | 281 | server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); |
119 | server->idle_inhibit_manager_v1 = | 282 | sway_idle_inhibit_manager_v1_init(); |
120 | sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle); | ||
121 | 283 | ||
122 | server->layer_shell = wlr_layer_shell_v1_create(server->wl_display); | 284 | server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, |
285 | SWAY_LAYER_SHELL_VERSION); | ||
123 | wl_signal_add(&server->layer_shell->events.new_surface, | 286 | wl_signal_add(&server->layer_shell->events.new_surface, |
124 | &server->layer_shell_surface); | 287 | &server->layer_shell_surface); |
125 | server->layer_shell_surface.notify = handle_layer_shell_surface; | 288 | server->layer_shell_surface.notify = handle_layer_shell_surface; |
126 | 289 | ||
127 | server->xdg_shell = wlr_xdg_shell_create(server->wl_display, | 290 | server->xdg_shell = wlr_xdg_shell_create(server->wl_display, |
128 | SWAY_XDG_SHELL_VERSION); | 291 | SWAY_XDG_SHELL_VERSION); |
129 | wl_signal_add(&server->xdg_shell->events.new_surface, | 292 | wl_signal_add(&server->xdg_shell->events.new_toplevel, |
130 | &server->xdg_shell_surface); | 293 | &server->xdg_shell_toplevel); |
131 | server->xdg_shell_surface.notify = handle_xdg_shell_surface; | 294 | server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel; |
132 | 295 | ||
133 | server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); | 296 | server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); |
134 | 297 | ||
@@ -159,8 +322,7 @@ bool server_init(struct sway_server *server) { | |||
159 | wl_signal_add(&server->pointer_constraints->events.new_constraint, | 322 | wl_signal_add(&server->pointer_constraints->events.new_constraint, |
160 | &server->pointer_constraint); | 323 | &server->pointer_constraint); |
161 | 324 | ||
162 | server->presentation = | 325 | wlr_presentation_create(server->wl_display, server->backend); |
163 | wlr_presentation_create(server->wl_display, server->backend); | ||
164 | 326 | ||
165 | server->output_manager_v1 = | 327 | server->output_manager_v1 = |
166 | wlr_output_manager_v1_create(server->wl_display); | 328 | wlr_output_manager_v1_create(server->wl_display); |
@@ -179,11 +341,14 @@ bool server_init(struct sway_server *server) { | |||
179 | &server->output_power_manager_set_mode); | 341 | &server->output_power_manager_set_mode); |
180 | server->input_method = wlr_input_method_manager_v2_create(server->wl_display); | 342 | server->input_method = wlr_input_method_manager_v2_create(server->wl_display); |
181 | server->text_input = wlr_text_input_manager_v3_create(server->wl_display); | 343 | server->text_input = wlr_text_input_manager_v3_create(server->wl_display); |
344 | server->foreign_toplevel_list = | ||
345 | wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION); | ||
182 | server->foreign_toplevel_manager = | 346 | server->foreign_toplevel_manager = |
183 | wlr_foreign_toplevel_manager_v1_create(server->wl_display); | 347 | wlr_foreign_toplevel_manager_v1_create(server->wl_display); |
184 | 348 | ||
185 | sway_session_lock_init(); | 349 | sway_session_lock_init(); |
186 | 350 | ||
351 | #if WLR_HAS_DRM_BACKEND | ||
187 | server->drm_lease_manager= | 352 | server->drm_lease_manager= |
188 | wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); | 353 | wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); |
189 | if (server->drm_lease_manager) { | 354 | if (server->drm_lease_manager) { |
@@ -194,13 +359,17 @@ bool server_init(struct sway_server *server) { | |||
194 | sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); | 359 | sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); |
195 | sway_log(SWAY_INFO, "VR will not be available"); | 360 | sway_log(SWAY_INFO, "VR will not be available"); |
196 | } | 361 | } |
362 | #endif | ||
197 | 363 | ||
198 | wlr_export_dmabuf_manager_v1_create(server->wl_display); | 364 | server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display); |
199 | wlr_screencopy_manager_v1_create(server->wl_display); | 365 | server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); |
200 | wlr_data_control_manager_v1_create(server->wl_display); | 366 | server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); |
201 | wlr_primary_selection_v1_device_manager_create(server->wl_display); | 367 | server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); |
202 | wlr_viewporter_create(server->wl_display); | 368 | wlr_viewporter_create(server->wl_display); |
203 | wlr_single_pixel_buffer_manager_v1_create(server->wl_display); | 369 | wlr_single_pixel_buffer_manager_v1_create(server->wl_display); |
370 | server->content_type_manager_v1 = | ||
371 | wlr_content_type_manager_v1_create(server->wl_display, 1); | ||
372 | wlr_fractional_scale_manager_v1_create(server->wl_display, 1); | ||
204 | 373 | ||
205 | struct wlr_xdg_foreign_registry *foreign_registry = | 374 | struct wlr_xdg_foreign_registry *foreign_registry = |
206 | wlr_xdg_foreign_registry_create(server->wl_display); | 375 | wlr_xdg_foreign_registry_create(server->wl_display); |
@@ -212,6 +381,17 @@ bool server_init(struct sway_server *server) { | |||
212 | xdg_activation_v1_handle_request_activate; | 381 | xdg_activation_v1_handle_request_activate; |
213 | wl_signal_add(&server->xdg_activation_v1->events.request_activate, | 382 | wl_signal_add(&server->xdg_activation_v1->events.request_activate, |
214 | &server->xdg_activation_v1_request_activate); | 383 | &server->xdg_activation_v1_request_activate); |
384 | server->xdg_activation_v1_new_token.notify = | ||
385 | xdg_activation_v1_handle_new_token; | ||
386 | wl_signal_add(&server->xdg_activation_v1->events.new_token, | ||
387 | &server->xdg_activation_v1_new_token); | ||
388 | |||
389 | struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = | ||
390 | wlr_cursor_shape_manager_v1_create(server->wl_display, 1); | ||
391 | server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; | ||
392 | wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape); | ||
393 | |||
394 | wl_list_init(&server->pending_launcher_ctxs); | ||
215 | 395 | ||
216 | // Avoid using "wayland-0" as display socket | 396 | // Avoid using "wayland-0" as display socket |
217 | char name_candidate[16]; | 397 | char name_candidate[16]; |
@@ -229,7 +409,7 @@ bool server_init(struct sway_server *server) { | |||
229 | return false; | 409 | return false; |
230 | } | 410 | } |
231 | 411 | ||
232 | server->headless_backend = wlr_headless_backend_create(server->wl_display); | 412 | server->headless_backend = wlr_headless_backend_create(server->wl_event_loop); |
233 | if (!server->headless_backend) { | 413 | if (!server->headless_backend) { |
234 | sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); | 414 | sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); |
235 | wlr_backend_destroy(server->backend); | 415 | wlr_backend_destroy(server->backend); |
@@ -262,6 +442,7 @@ void server_fini(struct sway_server *server) { | |||
262 | wlr_xwayland_destroy(server->xwayland.wlr_xwayland); | 442 | wlr_xwayland_destroy(server->xwayland.wlr_xwayland); |
263 | #endif | 443 | #endif |
264 | wl_display_destroy_clients(server->wl_display); | 444 | wl_display_destroy_clients(server->wl_display); |
445 | wlr_backend_destroy(server->backend); | ||
265 | wl_display_destroy(server->wl_display); | 446 | wl_display_destroy(server->wl_display); |
266 | list_free(server->dirty_nodes); | 447 | list_free(server->dirty_nodes); |
267 | } | 448 | } |
@@ -292,6 +473,10 @@ bool server_start(struct sway_server *server) { | |||
292 | } | 473 | } |
293 | #endif | 474 | #endif |
294 | 475 | ||
476 | if (config->primary_selection) { | ||
477 | wlr_primary_selection_v1_device_manager_create(server->wl_display); | ||
478 | } | ||
479 | |||
295 | sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", | 480 | sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", |
296 | server->socket); | 481 | server->socket); |
297 | if (!wlr_backend_start(server->backend)) { | 482 | if (!wlr_backend_start(server->backend)) { |
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 8b702b77..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,6 +246,8 @@ 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). |
@@ -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 |
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index c7a2c473..2f697248 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd | |||
@@ -337,8 +337,9 @@ node and will have the following properties: | |||
337 | this, but borders are included. | 337 | this, but borders are included. |
338 | |- window_rect | 338 | |- window_rect |
339 | : object | 339 | : object |
340 | : 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 |
341 | excluded from this calculation, but borders are included. | 341 | to the node itself. Window decorations and borders are outside the |
342 | _window_rect_ | ||
342 | |- deco_rect | 343 | |- deco_rect |
343 | : object | 344 | : object |
344 | : 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 |
@@ -375,6 +376,12 @@ node and will have the following properties: | |||
375 | : integer | 376 | : integer |
376 | : (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 |
377 | 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" | ||
378 | |- app_id | 385 | |- app_id |
379 | : string | 386 | : string |
380 | : (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. |
@@ -1039,7 +1046,7 @@ An object with a single string property containing the contents of the config | |||
1039 | *Example Reply:* | 1046 | *Example Reply:* |
1040 | ``` | 1047 | ``` |
1041 | { | 1048 | { |
1042 | "config": "set $mod Mod4\nbindsym $mod+q exit\n" | 1049 | "config": "set $mod Mod4\\nbindsym $mod+q exit\\n" |
1043 | } | 1050 | } |
1044 | ``` | 1051 | ``` |
1045 | 1052 | ||
@@ -1194,9 +1201,16 @@ following properties will be included for devices that support them: | |||
1194 | : int | 1201 | : int |
1195 | : 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 |
1196 | 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_ | ||
1197 | |- dwt | 1207 | |- dwt |
1198 | : string | 1208 | : string |
1199 | : 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_ | ||
1200 | |- calibration_matrix | 1214 | |- calibration_matrix |
1201 | : array | 1215 | : array |
1202 | : An array of 6 floats representing the calibration matrix for absolute | 1216 | : An array of 6 floats representing the calibration matrix for absolute |
@@ -1236,7 +1250,8 @@ following properties will be included for devices that support them: | |||
1236 | "click_method": "button_areas", | 1250 | "click_method": "button_areas", |
1237 | "middle_emulation": "disabled", | 1251 | "middle_emulation": "disabled", |
1238 | "scroll_method": "edge", | 1252 | "scroll_method": "edge", |
1239 | "dwt": "enabled" | 1253 | "dwt": "enabled", |
1254 | "dwtp": "enabled" | ||
1240 | } | 1255 | } |
1241 | }, | 1256 | }, |
1242 | { | 1257 | { |
@@ -1363,7 +1378,8 @@ one seat. Each object has the following properties: | |||
1363 | "click_method": "button_areas", | 1378 | "click_method": "button_areas", |
1364 | "middle_emulation": "disabled", | 1379 | "middle_emulation": "disabled", |
1365 | "scroll_method": "edge", | 1380 | "scroll_method": "edge", |
1366 | "dwt": "enabled" | 1381 | "dwt": "enabled", |
1382 | "dwtp": "enabled" | ||
1367 | } | 1383 | } |
1368 | }, | 1384 | }, |
1369 | { | 1385 | { |
@@ -1439,6 +1455,9 @@ available: | |||
1439 | : workspace | 1455 | : workspace |
1440 | :[ Sent whenever an event involving a workspace occurs such as initialization | 1456 | :[ Sent whenever an event involving a workspace occurs such as initialization |
1441 | 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 | ||
1442 | |- 0x80000002 | 1461 | |- 0x80000002 |
1443 | : mode | 1462 | : mode |
1444 | : Sent whenever the binding mode changes | 1463 | : Sent whenever the binding mode changes |
@@ -1559,6 +1578,20 @@ The following change types are currently available: | |||
1559 | } | 1578 | } |
1560 | ``` | 1579 | ``` |
1561 | 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 | |||
1562 | ## 0x80000002. MODE | 1595 | ## 0x80000002. MODE |
1563 | 1596 | ||
1564 | 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 b7d5e577..7d088d5d 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd | |||
@@ -72,13 +72,11 @@ must be separated by one space. For example: | |||
72 | 72 | ||
73 | *output* <name> scale <factor> | 73 | *output* <name> scale <factor> |
74 | 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 |
75 | recommended, but fractional values are also supported. If a fractional | 75 | recommended, but fractional values are also supported. You may be better |
76 | 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 |
77 | 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 |
78 | 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 |
79 | setting an integer scale factor and adjusting the font size of your | 79 | requirements of the protocol. |
80 | applications to taste. HiDPI isn't supported with Xwayland clients (windows | ||
81 | will blur). | ||
82 | 80 | ||
83 | *output* <name> scale_filter linear|nearest|smart | 81 | *output* <name> scale_filter linear|nearest|smart |
84 | 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 |
@@ -119,7 +117,7 @@ must be separated by one space. For example: | |||
119 | Enables or disables the specified output (all outputs are enabled by | 117 | Enables or disables the specified output (all outputs are enabled by |
120 | default). | 118 | default). |
121 | 119 | ||
122 | As opposed to the _power_ command, the output will loose its current | 120 | As opposed to the _power_ command, the output will lose its current |
123 | workspace and windows. | 121 | workspace and windows. |
124 | 122 | ||
125 | *output* <name> toggle | 123 | *output* <name> toggle |
diff --git a/sway/sway.5.scd b/sway/sway.5.scd index d8a462d3..9f823947 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd | |||
@@ -389,8 +389,8 @@ runtime. | |||
389 | for_window <criteria> move container to output <output> | 389 | for_window <criteria> move container to output <output> |
390 | 390 | ||
391 | *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ | 391 | *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ |
392 | [--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] \ |
393 | <command> | 393 | [Group<1-4>+]<key combo> <command> |
394 | 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 |
395 | 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). |
396 | 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 |
@@ -400,6 +400,12 @@ runtime. | |||
400 | 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, |
401 | 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. |
402 | 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 | |||
403 | 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 |
404 | 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 |
405 | 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 |
@@ -454,7 +460,8 @@ runtime. | |||
454 | ``` | 460 | ``` |
455 | 461 | ||
456 | *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ | 462 | *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ |
457 | [--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> | ||
458 | 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. |
459 | 466 | ||
460 | *bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> | 467 | *bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> |
@@ -648,7 +655,8 @@ The default colors are: | |||
648 | 655 | ||
649 | 656 | ||
650 | *default_border* normal|none|pixel [<n>] | 657 | *default_border* normal|none|pixel [<n>] |
651 | 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. | ||
652 | 660 | ||
653 | *default_floating_border* normal|none|pixel [<n>] | 661 | *default_floating_border* normal|none|pixel [<n>] |
654 | 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 |
@@ -807,6 +815,10 @@ The default colors are: | |||
807 | 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 |
808 | fullscreen mode and the dialog will be rendered. | 816 | fullscreen mode and the dialog will be rendered. |
809 | 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 | |||
810 | *set* $<name> <value> | 822 | *set* $<name> <value> |
811 | 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 |
812 | 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 |
@@ -957,6 +969,9 @@ properties in practice for your applications. | |||
957 | 969 | ||
958 | The following attributes may be matched with: | 970 | The following attributes may be matched with: |
959 | 971 | ||
972 | *all* | ||
973 | Matches all windows. | ||
974 | |||
960 | *app_id* | 975 | *app_id* |
961 | 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 |
962 | \_\_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 |
@@ -965,7 +980,8 @@ The following attributes may be matched with: | |||
965 | *class* | 980 | *class* |
966 | 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 |
967 | 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 |
968 | 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. | ||
969 | 985 | ||
970 | *con_id* | 986 | *con_id* |
971 | 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 |
@@ -979,12 +995,14 @@ The following attributes may be matched with: | |||
979 | Matches floating windows. | 995 | Matches floating windows. |
980 | 996 | ||
981 | *id* | 997 | *id* |
982 | 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. | ||
983 | 1000 | ||
984 | *instance* | 1001 | *instance* |
985 | 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 |
986 | 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 |
987 | of the currently focused window. | 1004 | of the currently focused window. instance is specific to X11 applications and |
1005 | requires XWayland. | ||
988 | 1006 | ||
989 | *pid* | 1007 | *pid* |
990 | Compare value against the window's process ID. Must be numeric. | 1008 | Compare value against the window's process ID. Must be numeric. |
@@ -1009,12 +1027,14 @@ The following attributes may be matched with: | |||
1009 | *window_role* | 1027 | *window_role* |
1010 | 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 |
1011 | 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 |
1012 | 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. | ||
1013 | 1032 | ||
1014 | *window_type* | 1033 | *window_type* |
1015 | Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values | 1034 | Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values |
1016 | are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, | 1035 | are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, |
1017 | popup_menu, tooltip and notification. | 1036 | popup_menu, tooltip and notification. window_type is specific to X11 |
1037 | applications and requires XWayland. | ||
1018 | 1038 | ||
1019 | *workspace* | 1039 | *workspace* |
1020 | 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 4a0a6d30..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> |
@@ -145,22 +144,16 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, | |||
145 | 144 | ||
146 | va_list args; | 145 | va_list args; |
147 | va_start(args, fmt); | 146 | va_start(args, fmt); |
148 | size_t length = vsnprintf(NULL, 0, fmt, args) + 1; | 147 | char *str = vformat_str(fmt, args); |
149 | va_end(args); | 148 | va_end(args); |
150 | 149 | if (!str) { | |
151 | char *temp = malloc(length + 1); | ||
152 | if (!temp) { | ||
153 | 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."); |
154 | return; | 151 | return; |
155 | } | 152 | } |
156 | 153 | ||
157 | va_start(args, fmt); | 154 | write(swaynag->fd[1], str, strlen(str)); |
158 | vsnprintf(temp, length, fmt, args); | ||
159 | va_end(args); | ||
160 | |||
161 | write(swaynag->fd[1], temp, length); | ||
162 | 155 | ||
163 | free(temp); | 156 | free(str); |
164 | } | 157 | } |
165 | 158 | ||
166 | 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 9c1a11e5..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 | } |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 04ef965f..9224b4fb 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -1,25 +1,20 @@ | |||
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 <sys/stat.h> | ||
9 | #include <wayland-server-core.h> | 5 | #include <wayland-server-core.h> |
6 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
10 | #include <wlr/types/wlr_linux_dmabuf_v1.h> | 7 | #include <wlr/types/wlr_linux_dmabuf_v1.h> |
11 | #include <wlr/types/wlr_output_layout.h> | 8 | #include <wlr/types/wlr_output_layout.h> |
12 | #include <wlr/types/wlr_subcompositor.h> | 9 | #include <wlr/types/wlr_subcompositor.h> |
13 | #include <wlr/render/drm_format_set.h> | ||
14 | #include "linux-dmabuf-unstable-v1-protocol.h" | 10 | #include "linux-dmabuf-unstable-v1-protocol.h" |
15 | #include "cairo_util.h" | ||
16 | #include "pango.h" | ||
17 | #include "sway/config.h" | 11 | #include "sway/config.h" |
18 | #include "sway/desktop.h" | ||
19 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
20 | #include "sway/input/input-manager.h" | 13 | #include "sway/input/input-manager.h" |
21 | #include "sway/input/seat.h" | 14 | #include "sway/input/seat.h" |
22 | #include "sway/ipc-server.h" | 15 | #include "sway/ipc-server.h" |
16 | #include "sway/scene_descriptor.h" | ||
17 | #include "sway/sway_text_node.h" | ||
23 | #include "sway/output.h" | 18 | #include "sway/output.h" |
24 | #include "sway/server.h" | 19 | #include "sway/server.h" |
25 | #include "sway/tree/arrange.h" | 20 | #include "sway/tree/arrange.h" |
@@ -30,6 +25,53 @@ | |||
30 | #include "log.h" | 25 | #include "log.h" |
31 | #include "stringop.h" | 26 | #include "stringop.h" |
32 | 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 | |||
33 | struct sway_container *container_create(struct sway_view *view) { | 75 | struct sway_container *container_create(struct sway_view *view) { |
34 | struct sway_container *c = calloc(1, sizeof(struct sway_container)); | 76 | struct sway_container *c = calloc(1, sizeof(struct sway_container)); |
35 | if (!c) { | 77 | if (!c) { |
@@ -37,23 +79,411 @@ struct sway_container *container_create(struct sway_view *view) { | |||
37 | return NULL; | 79 | return NULL; |
38 | } | 80 | } |
39 | node_init(&c->node, N_CONTAINER, c); | 81 | node_init(&c->node, N_CONTAINER, c); |
40 | c->pending.layout = L_NONE; | 82 | |
41 | c->view = view; | 83 | // Container tree structure |
42 | 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 | } | ||
43 | 151 | ||
44 | if (!view) { | 152 | if (!view) { |
45 | c->pending.children = create_list(); | 153 | c->pending.children = create_list(); |
46 | c->current.children = create_list(); | 154 | c->current.children = create_list(); |
47 | } | 155 | } |
156 | |||
157 | c->pending.layout = L_NONE; | ||
158 | c->view = view; | ||
159 | c->alpha = 1.0f; | ||
48 | c->marks = create_list(); | 160 | c->marks = create_list(); |
49 | c->outputs = create_list(); | ||
50 | 161 | ||
51 | wl_signal_init(&c->events.destroy); | 162 | wl_signal_init(&c->events.destroy); |
52 | 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); | ||
53 | 166 | ||
54 | return c; | 167 | return c; |
55 | } | 168 | } |
56 | 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 | |||
57 | void container_destroy(struct sway_container *con) { | 487 | void container_destroy(struct sway_container *con) { |
58 | if (!sway_assert(con->node.destroying, | 488 | if (!sway_assert(con->node.destroying, |
59 | "Tried to free container which wasn't marked as destroying")) { | 489 | "Tried to free container which wasn't marked as destroying")) { |
@@ -65,29 +495,21 @@ void container_destroy(struct sway_container *con) { | |||
65 | } | 495 | } |
66 | free(con->title); | 496 | free(con->title); |
67 | free(con->formatted_title); | 497 | free(con->formatted_title); |
68 | wlr_texture_destroy(con->title_focused); | ||
69 | wlr_texture_destroy(con->title_focused_inactive); | ||
70 | wlr_texture_destroy(con->title_unfocused); | ||
71 | wlr_texture_destroy(con->title_urgent); | ||
72 | wlr_texture_destroy(con->title_focused_tab_title); | ||
73 | list_free(con->pending.children); | 498 | list_free(con->pending.children); |
74 | list_free(con->current.children); | 499 | list_free(con->current.children); |
75 | list_free(con->outputs); | ||
76 | 500 | ||
77 | list_free_items_and_destroy(con->marks); | 501 | list_free_items_and_destroy(con->marks); |
78 | wlr_texture_destroy(con->marks_focused); | ||
79 | wlr_texture_destroy(con->marks_focused_inactive); | ||
80 | wlr_texture_destroy(con->marks_unfocused); | ||
81 | wlr_texture_destroy(con->marks_urgent); | ||
82 | wlr_texture_destroy(con->marks_focused_tab_title); | ||
83 | 502 | ||
84 | if (con->view && con->view->container == con) { | 503 | if (con->view && con->view->container == con) { |
85 | con->view->container = NULL; | 504 | con->view->container = NULL; |
505 | wlr_scene_node_destroy(&con->output_handler->node); | ||
86 | if (con->view->destroying) { | 506 | if (con->view->destroying) { |
87 | view_destroy(con->view); | 507 | view_destroy(con->view); |
88 | } | 508 | } |
89 | } | 509 | } |
90 | 510 | ||
511 | scene_node_disown_children(con->content_tree); | ||
512 | wlr_scene_node_destroy(&con->scene_tree->node); | ||
91 | free(con); | 513 | free(con); |
92 | } | 514 | } |
93 | 515 | ||
@@ -104,7 +526,7 @@ void container_begin_destroy(struct sway_container *con) { | |||
104 | container_fullscreen_disable(con); | 526 | container_fullscreen_disable(con); |
105 | } | 527 | } |
106 | 528 | ||
107 | wl_signal_emit(&con->node.events.destroy, &con->node); | 529 | wl_signal_emit_mutable(&con->node.events.destroy, &con->node); |
108 | 530 | ||
109 | container_end_mouse_operation(con); | 531 | container_end_mouse_operation(con); |
110 | 532 | ||
@@ -174,265 +596,6 @@ struct sway_container *container_find_child(struct sway_container *container, | |||
174 | return NULL; | 596 | return NULL; |
175 | } | 597 | } |
176 | 598 | ||
177 | static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly, | ||
178 | struct wlr_surface **surface, double *sx, double *sy) { | ||
179 | if (!sway_assert(con->view, "Expected a view")) { | ||
180 | return NULL; | ||
181 | } | ||
182 | struct sway_view *view = con->view; | ||
183 | double view_sx = lx - con->surface_x + view->geometry.x; | ||
184 | double view_sy = ly - con->surface_y + view->geometry.y; | ||
185 | |||
186 | double _sx, _sy; | ||
187 | struct wlr_surface *_surface = NULL; | ||
188 | switch (view->type) { | ||
189 | #if HAVE_XWAYLAND | ||
190 | case SWAY_VIEW_XWAYLAND: | ||
191 | _surface = wlr_surface_surface_at(view->surface, | ||
192 | view_sx, view_sy, &_sx, &_sy); | ||
193 | break; | ||
194 | #endif | ||
195 | case SWAY_VIEW_XDG_SHELL: | ||
196 | _surface = wlr_xdg_surface_surface_at( | ||
197 | view->wlr_xdg_toplevel->base, | ||
198 | view_sx, view_sy, &_sx, &_sy); | ||
199 | break; | ||
200 | } | ||
201 | if (_surface) { | ||
202 | *sx = _sx; | ||
203 | *sy = _sy; | ||
204 | *surface = _surface; | ||
205 | return con; | ||
206 | } | ||
207 | return NULL; | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * container_at for a container with layout L_TABBED. | ||
212 | */ | ||
213 | static struct sway_container *container_at_tabbed(struct sway_node *parent, | ||
214 | double lx, double ly, | ||
215 | struct wlr_surface **surface, double *sx, double *sy) { | ||
216 | struct wlr_box box; | ||
217 | node_get_box(parent, &box); | ||
218 | if (lx < box.x || lx > box.x + box.width || | ||
219 | ly < box.y || ly > box.y + box.height) { | ||
220 | return NULL; | ||
221 | } | ||
222 | struct sway_seat *seat = input_manager_current_seat(); | ||
223 | list_t *children = node_get_children(parent); | ||
224 | if (!children->length) { | ||
225 | return NULL; | ||
226 | } | ||
227 | |||
228 | // Tab titles | ||
229 | int title_height = container_titlebar_height(); | ||
230 | if (ly < box.y + title_height) { | ||
231 | int tab_width = box.width / children->length; | ||
232 | int child_index = (lx - box.x) / tab_width; | ||
233 | if (child_index >= children->length) { | ||
234 | child_index = children->length - 1; | ||
235 | } | ||
236 | struct sway_container *child = children->items[child_index]; | ||
237 | return child; | ||
238 | } | ||
239 | |||
240 | // Surfaces | ||
241 | struct sway_node *current = seat_get_active_tiling_child(seat, parent); | ||
242 | return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * container_at for a container with layout L_STACKED. | ||
247 | */ | ||
248 | static struct sway_container *container_at_stacked(struct sway_node *parent, | ||
249 | double lx, double ly, | ||
250 | struct wlr_surface **surface, double *sx, double *sy) { | ||
251 | struct wlr_box box; | ||
252 | node_get_box(parent, &box); | ||
253 | if (lx < box.x || lx > box.x + box.width || | ||
254 | ly < box.y || ly > box.y + box.height) { | ||
255 | return NULL; | ||
256 | } | ||
257 | struct sway_seat *seat = input_manager_current_seat(); | ||
258 | list_t *children = node_get_children(parent); | ||
259 | |||
260 | // Title bars | ||
261 | int title_height = container_titlebar_height(); | ||
262 | if (title_height > 0) { | ||
263 | int child_index = (ly - box.y) / title_height; | ||
264 | if (child_index < children->length) { | ||
265 | struct sway_container *child = children->items[child_index]; | ||
266 | return child; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | // Surfaces | ||
271 | struct sway_node *current = seat_get_active_tiling_child(seat, parent); | ||
272 | return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; | ||
273 | } | ||
274 | |||
275 | /** | ||
276 | * container_at for a container with layout L_HORIZ or L_VERT. | ||
277 | */ | ||
278 | static struct sway_container *container_at_linear(struct sway_node *parent, | ||
279 | double lx, double ly, | ||
280 | struct wlr_surface **surface, double *sx, double *sy) { | ||
281 | list_t *children = node_get_children(parent); | ||
282 | for (int i = 0; i < children->length; ++i) { | ||
283 | struct sway_container *child = children->items[i]; | ||
284 | struct sway_container *container = | ||
285 | tiling_container_at(&child->node, lx, ly, surface, sx, sy); | ||
286 | if (container) { | ||
287 | return container; | ||
288 | } | ||
289 | } | ||
290 | return NULL; | ||
291 | } | ||
292 | |||
293 | static struct sway_container *floating_container_at(double lx, double ly, | ||
294 | struct wlr_surface **surface, double *sx, double *sy) { | ||
295 | // For outputs with floating containers that overhang the output bounds, | ||
296 | // those at the end of the output list appear on top of floating | ||
297 | // containers from other outputs, so iterate the list in reverse. | ||
298 | for (int i = root->outputs->length - 1; i >= 0; --i) { | ||
299 | struct sway_output *output = root->outputs->items[i]; | ||
300 | for (int j = 0; j < output->workspaces->length; ++j) { | ||
301 | struct sway_workspace *ws = output->workspaces->items[j]; | ||
302 | if (!workspace_is_visible(ws)) { | ||
303 | continue; | ||
304 | } | ||
305 | // Items at the end of the list are on top, so iterate the list in | ||
306 | // reverse. | ||
307 | for (int k = ws->floating->length - 1; k >= 0; --k) { | ||
308 | struct sway_container *floater = ws->floating->items[k]; | ||
309 | struct sway_container *container = | ||
310 | tiling_container_at(&floater->node, lx, ly, surface, sx, sy); | ||
311 | if (container) { | ||
312 | return container; | ||
313 | } | ||
314 | } | ||
315 | } | ||
316 | } | ||
317 | return NULL; | ||
318 | } | ||
319 | |||
320 | static struct sway_container *view_container_content_at(struct sway_node *parent, | ||
321 | double lx, double ly, | ||
322 | struct wlr_surface **surface, double *sx, double *sy) { | ||
323 | if (!sway_assert(node_is_view(parent), "Expected a view")) { | ||
324 | return NULL; | ||
325 | } | ||
326 | |||
327 | struct sway_container *container = parent->sway_container; | ||
328 | struct wlr_box box = { | ||
329 | .x = container->pending.content_x, | ||
330 | .y = container->pending.content_y, | ||
331 | .width = container->pending.content_width, | ||
332 | .height = container->pending.content_height, | ||
333 | }; | ||
334 | |||
335 | if (wlr_box_contains_point(&box, lx, ly)) { | ||
336 | surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); | ||
337 | return container; | ||
338 | } | ||
339 | |||
340 | return NULL; | ||
341 | } | ||
342 | |||
343 | static struct sway_container *view_container_at(struct sway_node *parent, | ||
344 | double lx, double ly, | ||
345 | struct wlr_surface **surface, double *sx, double *sy) { | ||
346 | if (!sway_assert(node_is_view(parent), "Expected a view")) { | ||
347 | return NULL; | ||
348 | } | ||
349 | |||
350 | struct sway_container *container = parent->sway_container; | ||
351 | struct wlr_box box = { | ||
352 | .x = container->pending.x, | ||
353 | .y = container->pending.y, | ||
354 | .width = container->pending.width, | ||
355 | .height = container->pending.height, | ||
356 | }; | ||
357 | |||
358 | if (wlr_box_contains_point(&box, lx, ly)) { | ||
359 | surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); | ||
360 | return container; | ||
361 | } | ||
362 | |||
363 | return NULL; | ||
364 | } | ||
365 | |||
366 | struct sway_container *tiling_container_at(struct sway_node *parent, | ||
367 | double lx, double ly, | ||
368 | struct wlr_surface **surface, double *sx, double *sy) { | ||
369 | if (node_is_view(parent)) { | ||
370 | return view_container_at(parent, lx, ly, surface, sx, sy); | ||
371 | } | ||
372 | if (!node_get_children(parent)) { | ||
373 | return NULL; | ||
374 | } | ||
375 | switch (node_get_layout(parent)) { | ||
376 | case L_HORIZ: | ||
377 | case L_VERT: | ||
378 | return container_at_linear(parent, lx, ly, surface, sx, sy); | ||
379 | case L_TABBED: | ||
380 | return container_at_tabbed(parent, lx, ly, surface, sx, sy); | ||
381 | case L_STACKED: | ||
382 | return container_at_stacked(parent, lx, ly, surface, sx, sy); | ||
383 | case L_NONE: | ||
384 | return NULL; | ||
385 | } | ||
386 | return NULL; | ||
387 | } | ||
388 | |||
389 | static bool surface_is_popup(struct wlr_surface *surface) { | ||
390 | while (!wlr_surface_is_xdg_surface(surface)) { | ||
391 | if (!wlr_surface_is_subsurface(surface)) { | ||
392 | return false; | ||
393 | } | ||
394 | struct wlr_subsurface *subsurface = | ||
395 | wlr_subsurface_from_wlr_surface(surface); | ||
396 | surface = subsurface->parent; | ||
397 | } | ||
398 | struct wlr_xdg_surface *xdg_surface = | ||
399 | wlr_xdg_surface_from_wlr_surface(surface); | ||
400 | return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; | ||
401 | } | ||
402 | |||
403 | struct sway_container *container_at(struct sway_workspace *workspace, | ||
404 | double lx, double ly, | ||
405 | struct wlr_surface **surface, double *sx, double *sy) { | ||
406 | struct sway_container *c; | ||
407 | |||
408 | struct sway_seat *seat = input_manager_current_seat(); | ||
409 | struct sway_container *focus = seat_get_focused_container(seat); | ||
410 | bool is_floating = focus && container_is_floating_or_child(focus); | ||
411 | // Focused view's popups | ||
412 | if (focus && focus->view) { | ||
413 | c = surface_at_view(focus, lx, ly, surface, sx, sy); | ||
414 | if (c && surface_is_popup(*surface)) { | ||
415 | return c; | ||
416 | } | ||
417 | *surface = NULL; | ||
418 | } | ||
419 | // Floating | ||
420 | if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) { | ||
421 | return c; | ||
422 | } | ||
423 | // Tiling (focused) | ||
424 | if (focus && focus->view && !is_floating) { | ||
425 | if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) { | ||
426 | return c; | ||
427 | } | ||
428 | } | ||
429 | // Tiling (non-focused) | ||
430 | if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { | ||
431 | return c; | ||
432 | } | ||
433 | return NULL; | ||
434 | } | ||
435 | |||
436 | void container_for_each_child(struct sway_container *container, | 599 | void container_for_each_child(struct sway_container *container, |
437 | void (*f)(struct sway_container *container, void *data), | 600 | void (*f)(struct sway_container *container, void *data), |
438 | void *data) { | 601 | void *data) { |
@@ -478,127 +641,6 @@ bool container_has_ancestor(struct sway_container *descendant, | |||
478 | return false; | 641 | return false; |
479 | } | 642 | } |
480 | 643 | ||
481 | void container_damage_whole(struct sway_container *container) { | ||
482 | for (int i = 0; i < root->outputs->length; ++i) { | ||
483 | struct sway_output *output = root->outputs->items[i]; | ||
484 | output_damage_whole_container(output, container); | ||
485 | } | ||
486 | } | ||
487 | |||
488 | /** | ||
489 | * Return the output which will be used for scale purposes. | ||
490 | * This is the most recently entered output. | ||
491 | */ | ||
492 | struct sway_output *container_get_effective_output(struct sway_container *con) { | ||
493 | if (con->outputs->length == 0) { | ||
494 | return NULL; | ||
495 | } | ||
496 | return con->outputs->items[con->outputs->length - 1]; | ||
497 | } | ||
498 | |||
499 | static void render_titlebar_text_texture(struct sway_output *output, | ||
500 | struct sway_container *con, struct wlr_texture **texture, | ||
501 | struct border_colors *class, bool pango_markup, char *text) { | ||
502 | double scale = output->wlr_output->scale; | ||
503 | int width = 0; | ||
504 | int height = config->font_height * scale; | ||
505 | int baseline; | ||
506 | |||
507 | // We must use a non-nil cairo_t for cairo_set_font_options to work. | ||
508 | // Therefore, we cannot use cairo_create(NULL). | ||
509 | cairo_surface_t *dummy_surface = cairo_image_surface_create( | ||
510 | CAIRO_FORMAT_ARGB32, 0, 0); | ||
511 | cairo_t *c = cairo_create(dummy_surface); | ||
512 | cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); | ||
513 | cairo_font_options_t *fo = cairo_font_options_create(); | ||
514 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
515 | if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { | ||
516 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); | ||
517 | } else { | ||
518 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | ||
519 | cairo_font_options_set_subpixel_order(fo, | ||
520 | to_cairo_subpixel_order(output->wlr_output->subpixel)); | ||
521 | } | ||
522 | cairo_set_font_options(c, fo); | ||
523 | get_text_size(c, config->font_description, &width, NULL, &baseline, scale, | ||
524 | config->pango_markup, "%s", text); | ||
525 | cairo_surface_destroy(dummy_surface); | ||
526 | cairo_destroy(c); | ||
527 | |||
528 | if (width == 0 || height == 0) { | ||
529 | return; | ||
530 | } | ||
531 | |||
532 | if (height > config->font_height * scale) { | ||
533 | height = config->font_height * scale; | ||
534 | } | ||
535 | |||
536 | cairo_surface_t *surface = cairo_image_surface_create( | ||
537 | CAIRO_FORMAT_ARGB32, width, height); | ||
538 | cairo_status_t status = cairo_surface_status(surface); | ||
539 | if (status != CAIRO_STATUS_SUCCESS) { | ||
540 | sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", | ||
541 | cairo_status_to_string(status)); | ||
542 | return; | ||
543 | } | ||
544 | |||
545 | cairo_t *cairo = cairo_create(surface); | ||
546 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
547 | cairo_set_font_options(cairo, fo); | ||
548 | cairo_font_options_destroy(fo); | ||
549 | cairo_set_source_rgba(cairo, class->background[0], class->background[1], | ||
550 | class->background[2], class->background[3]); | ||
551 | cairo_paint(cairo); | ||
552 | PangoContext *pango = pango_cairo_create_context(cairo); | ||
553 | cairo_set_source_rgba(cairo, class->text[0], class->text[1], | ||
554 | class->text[2], class->text[3]); | ||
555 | cairo_move_to(cairo, 0, config->font_baseline * scale - baseline); | ||
556 | |||
557 | render_text(cairo, config->font_description, scale, pango_markup, "%s", text); | ||
558 | |||
559 | cairo_surface_flush(surface); | ||
560 | unsigned char *data = cairo_image_surface_get_data(surface); | ||
561 | int stride = cairo_image_surface_get_stride(surface); | ||
562 | struct wlr_renderer *renderer = output->wlr_output->renderer; | ||
563 | *texture = wlr_texture_from_pixels( | ||
564 | renderer, DRM_FORMAT_ARGB8888, stride, width, height, data); | ||
565 | cairo_surface_destroy(surface); | ||
566 | g_object_unref(pango); | ||
567 | cairo_destroy(cairo); | ||
568 | } | ||
569 | |||
570 | static void update_title_texture(struct sway_container *con, | ||
571 | struct wlr_texture **texture, struct border_colors *class) { | ||
572 | struct sway_output *output = container_get_effective_output(con); | ||
573 | if (!output) { | ||
574 | return; | ||
575 | } | ||
576 | if (*texture) { | ||
577 | wlr_texture_destroy(*texture); | ||
578 | *texture = NULL; | ||
579 | } | ||
580 | if (!con->formatted_title) { | ||
581 | return; | ||
582 | } | ||
583 | |||
584 | render_titlebar_text_texture(output, con, texture, class, | ||
585 | config->pango_markup, con->formatted_title); | ||
586 | } | ||
587 | |||
588 | void container_update_title_textures(struct sway_container *container) { | ||
589 | update_title_texture(container, &container->title_focused, | ||
590 | &config->border_colors.focused); | ||
591 | update_title_texture(container, &container->title_focused_inactive, | ||
592 | &config->border_colors.focused_inactive); | ||
593 | update_title_texture(container, &container->title_unfocused, | ||
594 | &config->border_colors.unfocused); | ||
595 | update_title_texture(container, &container->title_urgent, | ||
596 | &config->border_colors.urgent); | ||
597 | update_title_texture(container, &container->title_focused_tab_title, | ||
598 | &config->border_colors.focused_tab_title); | ||
599 | container_damage_whole(container); | ||
600 | } | ||
601 | |||
602 | /** | 644 | /** |
603 | * Calculate and return the length of the tree representation. | 645 | * Calculate and return the length of the tree representation. |
604 | * An example tree representation is: V[Terminal, Firefox] | 646 | * An example tree representation is: V[Terminal, Firefox] |
@@ -664,7 +706,13 @@ void container_update_representation(struct sway_container *con) { | |||
664 | } | 706 | } |
665 | container_build_representation(con->pending.layout, con->pending.children, | 707 | container_build_representation(con->pending.layout, con->pending.children, |
666 | con->formatted_title); | 708 | con->formatted_title); |
667 | container_update_title_textures(con); | 709 | |
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 | } | ||
668 | } | 716 | } |
669 | if (con->pending.parent) { | 717 | if (con->pending.parent) { |
670 | container_update_representation(con->pending.parent); | 718 | container_update_representation(con->pending.parent); |
@@ -716,6 +764,21 @@ void floating_calculate_constraints(int *min_width, int *max_width, | |||
716 | 764 | ||
717 | } | 765 | } |
718 | 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 | |||
719 | static void floating_natural_resize(struct sway_container *con) { | 782 | static void floating_natural_resize(struct sway_container *con) { |
720 | int min_width, max_width, min_height, max_height; | 783 | int min_width, max_width, min_height, max_height; |
721 | floating_calculate_constraints(&min_width, &max_width, | 784 | floating_calculate_constraints(&min_width, &max_width, |
@@ -787,11 +850,11 @@ void container_floating_set_default_size(struct sway_container *con) { | |||
787 | int min_width, max_width, min_height, max_height; | 850 | int min_width, max_width, min_height, max_height; |
788 | floating_calculate_constraints(&min_width, &max_width, | 851 | floating_calculate_constraints(&min_width, &max_width, |
789 | &min_height, &max_height); | 852 | &min_height, &max_height); |
790 | struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); | 853 | struct wlr_box box; |
791 | workspace_get_box(con->pending.workspace, box); | 854 | workspace_get_box(con->pending.workspace, &box); |
792 | 855 | ||
793 | 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)); |
794 | 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)); |
795 | if (!con->view) { | 858 | if (!con->view) { |
796 | con->pending.width = width; | 859 | con->pending.width = width; |
797 | con->pending.height = height; | 860 | con->pending.height = height; |
@@ -800,8 +863,6 @@ void container_floating_set_default_size(struct sway_container *con) { | |||
800 | con->pending.content_height = height; | 863 | con->pending.content_height = height; |
801 | container_set_geometry_from_content(con); | 864 | container_set_geometry_from_content(con); |
802 | } | 865 | } |
803 | |||
804 | free(box); | ||
805 | } | 866 | } |
806 | 867 | ||
807 | 868 | ||
@@ -937,17 +998,6 @@ bool container_is_floating(struct sway_container *container) { | |||
937 | return false; | 998 | return false; |
938 | } | 999 | } |
939 | 1000 | ||
940 | bool container_is_current_floating(struct sway_container *container) { | ||
941 | if (!container->current.parent && container->current.workspace && | ||
942 | list_find(container->current.workspace->floating, container) != -1) { | ||
943 | return true; | ||
944 | } | ||
945 | if (container->scratchpad) { | ||
946 | return true; | ||
947 | } | ||
948 | return false; | ||
949 | } | ||
950 | |||
951 | 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) { |
952 | box->x = container->pending.x; | 1002 | box->x = container->pending.x; |
953 | box->y = container->pending.y; | 1003 | box->y = container->pending.y; |
@@ -1031,6 +1081,13 @@ void container_floating_move_to(struct sway_container *con, | |||
1031 | workspace_add_floating(new_workspace, con); | 1081 | workspace_add_floating(new_workspace, con); |
1032 | arrange_workspace(old_workspace); | 1082 | arrange_workspace(old_workspace); |
1033 | 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 | } | ||
1034 | workspace_detect_urgent(old_workspace); | 1091 | workspace_detect_urgent(old_workspace); |
1035 | workspace_detect_urgent(new_workspace); | 1092 | workspace_detect_urgent(new_workspace); |
1036 | } | 1093 | } |
@@ -1062,16 +1119,6 @@ void container_end_mouse_operation(struct sway_container *container) { | |||
1062 | } | 1119 | } |
1063 | } | 1120 | } |
1064 | 1121 | ||
1065 | static bool devid_from_fd(int fd, dev_t *devid) { | ||
1066 | struct stat stat; | ||
1067 | if (fstat(fd, &stat) != 0) { | ||
1068 | sway_log_errno(SWAY_ERROR, "fstat failed"); | ||
1069 | return false; | ||
1070 | } | ||
1071 | *devid = stat.st_rdev; | ||
1072 | return true; | ||
1073 | } | ||
1074 | |||
1075 | static void set_fullscreen(struct sway_container *con, bool enable) { | 1122 | static void set_fullscreen(struct sway_container *con, bool enable) { |
1076 | if (!con->view) { | 1123 | if (!con->view) { |
1077 | return; | 1124 | return; |
@@ -1083,75 +1130,6 @@ static void set_fullscreen(struct sway_container *con, bool enable) { | |||
1083 | con->view->foreign_toplevel, enable); | 1130 | con->view->foreign_toplevel, enable); |
1084 | } | 1131 | } |
1085 | } | 1132 | } |
1086 | |||
1087 | if (!server.linux_dmabuf_v1 || !con->view->surface) { | ||
1088 | return; | ||
1089 | } | ||
1090 | if (!enable) { | ||
1091 | wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, | ||
1092 | con->view->surface, NULL); | ||
1093 | return; | ||
1094 | } | ||
1095 | |||
1096 | if (!con->pending.workspace || !con->pending.workspace->output) { | ||
1097 | return; | ||
1098 | } | ||
1099 | |||
1100 | struct sway_output *output = con->pending.workspace->output; | ||
1101 | struct wlr_output *wlr_output = output->wlr_output; | ||
1102 | |||
1103 | // TODO: add wlroots helpers for all of this stuff | ||
1104 | |||
1105 | const struct wlr_drm_format_set *renderer_formats = | ||
1106 | wlr_renderer_get_dmabuf_texture_formats(server.renderer); | ||
1107 | assert(renderer_formats); | ||
1108 | |||
1109 | int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer); | ||
1110 | int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend); | ||
1111 | if (renderer_drm_fd < 0 || backend_drm_fd < 0) { | ||
1112 | return; | ||
1113 | } | ||
1114 | |||
1115 | dev_t render_dev, scanout_dev; | ||
1116 | if (!devid_from_fd(renderer_drm_fd, &render_dev) || | ||
1117 | !devid_from_fd(backend_drm_fd, &scanout_dev)) { | ||
1118 | return; | ||
1119 | } | ||
1120 | |||
1121 | const struct wlr_drm_format_set *output_formats = | ||
1122 | wlr_output_get_primary_formats(output->wlr_output, | ||
1123 | WLR_BUFFER_CAP_DMABUF); | ||
1124 | if (!output_formats) { | ||
1125 | return; | ||
1126 | } | ||
1127 | |||
1128 | struct wlr_drm_format_set scanout_formats = {0}; | ||
1129 | if (!wlr_drm_format_set_intersect(&scanout_formats, | ||
1130 | output_formats, renderer_formats)) { | ||
1131 | return; | ||
1132 | } | ||
1133 | |||
1134 | struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = { | ||
1135 | { | ||
1136 | .target_device = scanout_dev, | ||
1137 | .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT, | ||
1138 | .formats = &scanout_formats, | ||
1139 | }, | ||
1140 | { | ||
1141 | .target_device = render_dev, | ||
1142 | .formats = renderer_formats, | ||
1143 | }, | ||
1144 | }; | ||
1145 | |||
1146 | const struct wlr_linux_dmabuf_feedback_v1 feedback = { | ||
1147 | .main_device = render_dev, | ||
1148 | .tranches = tranches, | ||
1149 | .tranches_len = sizeof(tranches) / sizeof(tranches[0]), | ||
1150 | }; | ||
1151 | wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, | ||
1152 | con->view->surface, &feedback); | ||
1153 | |||
1154 | wlr_drm_format_set_finish(&scanout_formats); | ||
1155 | } | 1133 | } |
1156 | 1134 | ||
1157 | static void container_fullscreen_workspace(struct sway_container *con) { | 1135 | static void container_fullscreen_workspace(struct sway_container *con) { |
@@ -1321,72 +1299,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) { | |||
1321 | return false; | 1299 | return false; |
1322 | } | 1300 | } |
1323 | 1301 | ||
1324 | static void surface_send_enter_iterator(struct wlr_surface *surface, | ||
1325 | int x, int y, void *data) { | ||
1326 | struct wlr_output *wlr_output = data; | ||
1327 | wlr_surface_send_enter(surface, wlr_output); | ||
1328 | } | ||
1329 | |||
1330 | static void surface_send_leave_iterator(struct wlr_surface *surface, | ||
1331 | int x, int y, void *data) { | ||
1332 | struct wlr_output *wlr_output = data; | ||
1333 | wlr_surface_send_leave(surface, wlr_output); | ||
1334 | } | ||
1335 | |||
1336 | void container_discover_outputs(struct sway_container *con) { | ||
1337 | struct wlr_box con_box = { | ||
1338 | .x = con->current.x, | ||
1339 | .y = con->current.y, | ||
1340 | .width = con->current.width, | ||
1341 | .height = con->current.height, | ||
1342 | }; | ||
1343 | struct sway_output *old_output = container_get_effective_output(con); | ||
1344 | |||
1345 | for (int i = 0; i < root->outputs->length; ++i) { | ||
1346 | struct sway_output *output = root->outputs->items[i]; | ||
1347 | struct wlr_box output_box; | ||
1348 | output_get_box(output, &output_box); | ||
1349 | struct wlr_box intersection; | ||
1350 | bool intersects = | ||
1351 | wlr_box_intersection(&intersection, &con_box, &output_box); | ||
1352 | int index = list_find(con->outputs, output); | ||
1353 | |||
1354 | if (intersects && index == -1) { | ||
1355 | // Send enter | ||
1356 | sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output); | ||
1357 | if (con->view) { | ||
1358 | view_for_each_surface(con->view, | ||
1359 | surface_send_enter_iterator, output->wlr_output); | ||
1360 | if (con->view->foreign_toplevel) { | ||
1361 | wlr_foreign_toplevel_handle_v1_output_enter( | ||
1362 | con->view->foreign_toplevel, output->wlr_output); | ||
1363 | } | ||
1364 | } | ||
1365 | list_add(con->outputs, output); | ||
1366 | } else if (!intersects && index != -1) { | ||
1367 | // Send leave | ||
1368 | sway_log(SWAY_DEBUG, "Container %p left output %p", con, output); | ||
1369 | if (con->view) { | ||
1370 | view_for_each_surface(con->view, | ||
1371 | surface_send_leave_iterator, output->wlr_output); | ||
1372 | if (con->view->foreign_toplevel) { | ||
1373 | wlr_foreign_toplevel_handle_v1_output_leave( | ||
1374 | con->view->foreign_toplevel, output->wlr_output); | ||
1375 | } | ||
1376 | } | ||
1377 | list_del(con->outputs, index); | ||
1378 | } | ||
1379 | } | ||
1380 | struct sway_output *new_output = container_get_effective_output(con); | ||
1381 | double old_scale = old_output && old_output->enabled ? | ||
1382 | old_output->wlr_output->scale : -1; | ||
1383 | double new_scale = new_output ? new_output->wlr_output->scale : -1; | ||
1384 | if (old_scale != new_scale) { | ||
1385 | container_update_title_textures(con); | ||
1386 | container_update_marks_textures(con); | ||
1387 | } | ||
1388 | } | ||
1389 | |||
1390 | enum sway_container_layout container_parent_layout(struct sway_container *con) { | 1302 | enum sway_container_layout container_parent_layout(struct sway_container *con) { |
1391 | if (con->pending.parent) { | 1303 | if (con->pending.parent) { |
1392 | return con->pending.parent->pending.layout; | 1304 | return con->pending.parent->pending.layout; |
@@ -1397,19 +1309,11 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) { | |||
1397 | return L_NONE; | 1309 | return L_NONE; |
1398 | } | 1310 | } |
1399 | 1311 | ||
1400 | enum sway_container_layout container_current_parent_layout( | ||
1401 | struct sway_container *con) { | ||
1402 | if (con->current.parent) { | ||
1403 | return con->current.parent->current.layout; | ||
1404 | } | ||
1405 | return con->current.workspace->current.layout; | ||
1406 | } | ||
1407 | |||
1408 | list_t *container_get_siblings(struct sway_container *container) { | 1312 | list_t *container_get_siblings(struct sway_container *container) { |
1409 | if (container->pending.parent) { | 1313 | if (container->pending.parent) { |
1410 | return container->pending.parent->pending.children; | 1314 | return container->pending.parent->pending.children; |
1411 | } | 1315 | } |
1412 | if (container_is_scratchpad_hidden(container)) { | 1316 | if (!container->pending.workspace) { |
1413 | return NULL; | 1317 | return NULL; |
1414 | } | 1318 | } |
1415 | if (list_find(container->pending.workspace->tiling, container) != -1) { | 1319 | if (list_find(container->pending.workspace->tiling, container) != -1) { |
@@ -1422,13 +1326,6 @@ int container_sibling_index(struct sway_container *child) { | |||
1422 | return list_find(container_get_siblings(child), child); | 1326 | return list_find(container_get_siblings(child), child); |
1423 | } | 1327 | } |
1424 | 1328 | ||
1425 | list_t *container_get_current_siblings(struct sway_container *container) { | ||
1426 | if (container->current.parent) { | ||
1427 | return container->current.parent->current.children; | ||
1428 | } | ||
1429 | return container->current.workspace->current.tiling; | ||
1430 | } | ||
1431 | |||
1432 | void container_handle_fullscreen_reparent(struct sway_container *con) { | 1329 | void container_handle_fullscreen_reparent(struct sway_container *con) { |
1433 | if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || | 1330 | if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || |
1434 | con->pending.workspace->fullscreen == con) { | 1331 | con->pending.workspace->fullscreen == con) { |
@@ -1643,7 +1540,7 @@ bool container_find_and_unmark(char *mark) { | |||
1643 | if (strcmp(con_mark, mark) == 0) { | 1540 | if (strcmp(con_mark, mark) == 0) { |
1644 | free(con_mark); | 1541 | free(con_mark); |
1645 | list_del(con->marks, i); | 1542 | list_del(con->marks, i); |
1646 | container_update_marks_textures(con); | 1543 | container_update_marks(con); |
1647 | ipc_event_window(con, "mark"); | 1544 | ipc_event_window(con, "mark"); |
1648 | return true; | 1545 | return true; |
1649 | } | 1546 | } |
@@ -1674,70 +1571,15 @@ void container_add_mark(struct sway_container *con, char *mark) { | |||
1674 | ipc_event_window(con, "mark"); | 1571 | ipc_event_window(con, "mark"); |
1675 | } | 1572 | } |
1676 | 1573 | ||
1677 | static void update_marks_texture(struct sway_container *con, | ||
1678 | struct wlr_texture **texture, struct border_colors *class) { | ||
1679 | struct sway_output *output = container_get_effective_output(con); | ||
1680 | if (!output) { | ||
1681 | return; | ||
1682 | } | ||
1683 | if (*texture) { | ||
1684 | wlr_texture_destroy(*texture); | ||
1685 | *texture = NULL; | ||
1686 | } | ||
1687 | if (!con->marks->length) { | ||
1688 | return; | ||
1689 | } | ||
1690 | |||
1691 | size_t len = 0; | ||
1692 | for (int i = 0; i < con->marks->length; ++i) { | ||
1693 | char *mark = con->marks->items[i]; | ||
1694 | if (mark[0] != '_') { | ||
1695 | len += strlen(mark) + 2; | ||
1696 | } | ||
1697 | } | ||
1698 | char *buffer = calloc(len + 1, 1); | ||
1699 | char *part = malloc(len + 1); | ||
1700 | |||
1701 | if (!sway_assert(buffer && part, "Unable to allocate memory")) { | ||
1702 | free(buffer); | ||
1703 | return; | ||
1704 | } | ||
1705 | |||
1706 | for (int i = 0; i < con->marks->length; ++i) { | ||
1707 | char *mark = con->marks->items[i]; | ||
1708 | if (mark[0] != '_') { | ||
1709 | snprintf(part, len + 1, "[%s]", mark); | ||
1710 | strcat(buffer, part); | ||
1711 | } | ||
1712 | } | ||
1713 | free(part); | ||
1714 | |||
1715 | render_titlebar_text_texture(output, con, texture, class, false, buffer); | ||
1716 | |||
1717 | free(buffer); | ||
1718 | } | ||
1719 | |||
1720 | void container_update_marks_textures(struct sway_container *con) { | ||
1721 | if (!config->show_marks) { | ||
1722 | return; | ||
1723 | } | ||
1724 | update_marks_texture(con, &con->marks_focused, | ||
1725 | &config->border_colors.focused); | ||
1726 | update_marks_texture(con, &con->marks_focused_inactive, | ||
1727 | &config->border_colors.focused_inactive); | ||
1728 | update_marks_texture(con, &con->marks_unfocused, | ||
1729 | &config->border_colors.unfocused); | ||
1730 | update_marks_texture(con, &con->marks_urgent, | ||
1731 | &config->border_colors.urgent); | ||
1732 | update_marks_texture(con, &con->marks_focused_tab_title, | ||
1733 | &config->border_colors.focused_tab_title); | ||
1734 | container_damage_whole(con); | ||
1735 | } | ||
1736 | |||
1737 | void container_raise_floating(struct sway_container *con) { | 1574 | void container_raise_floating(struct sway_container *con) { |
1738 | // 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. |
1739 | struct sway_container *floater = container_toplevel_ancestor(con); | 1576 | struct sway_container *floater = container_toplevel_ancestor(con); |
1740 | 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 | |||
1741 | list_move_to_end(floater->pending.workspace->floating, floater); | 1583 | list_move_to_end(floater->pending.workspace->floating, floater); |
1742 | node_set_dirty(&floater->pending.workspace->node); | 1584 | node_set_dirty(&floater->pending.workspace->node); |
1743 | } | 1585 | } |
@@ -1821,3 +1663,177 @@ int container_squash(struct sway_container *con) { | |||
1821 | } | 1663 | } |
1822 | return change; | 1664 | return change; |
1823 | } | 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 52826c91..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 | ||
@@ -87,9 +86,51 @@ 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; |
@@ -102,11 +143,6 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { | |||
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(); |
@@ -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.disable, 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,7 +313,7 @@ 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); |
289 | wl_signal_emit(&output->node.events.destroy, &output->node); | 316 | wl_signal_emit_mutable(&output->node.events.destroy, &output->node); |
290 | 317 | ||
291 | output->node.destroying = true; | 318 | output->node.destroying = true; |
292 | node_set_dirty(&output->node); | 319 | node_set_dirty(&output->node); |
@@ -390,6 +417,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) { | |||
390 | box->height = output->height; | 417 | box->height = output->height; |
391 | } | 418 | } |
392 | 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 | |||
393 | enum sway_container_layout output_get_default_layout( | 447 | enum sway_container_layout output_get_default_layout( |
394 | struct sway_output *output) { | 448 | struct sway_output *output) { |
395 | 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 8508e9eb..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 | snprintf(file_name, sizeof(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) { |
@@ -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 7d9e038d..35b4b73f 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -1,9 +1,10 @@ | |||
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> |
9 | #include <wlr/types/wlr_subcompositor.h> | 10 | #include <wlr/types/wlr_subcompositor.h> |
@@ -16,14 +17,16 @@ | |||
16 | #include "log.h" | 17 | #include "log.h" |
17 | #include "sway/criteria.h" | 18 | #include "sway/criteria.h" |
18 | #include "sway/commands.h" | 19 | #include "sway/commands.h" |
19 | #include "sway/desktop.h" | ||
20 | #include "sway/desktop/transaction.h" | 20 | #include "sway/desktop/transaction.h" |
21 | #include "sway/desktop/idle_inhibit_v1.h" | 21 | #include "sway/desktop/idle_inhibit_v1.h" |
22 | #include "sway/desktop/launcher.h" | ||
22 | #include "sway/input/cursor.h" | 23 | #include "sway/input/cursor.h" |
23 | #include "sway/ipc-server.h" | 24 | #include "sway/ipc-server.h" |
24 | #include "sway/output.h" | 25 | #include "sway/output.h" |
25 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/scene_descriptor.h" | ||
26 | #include "sway/server.h" | 28 | #include "sway/server.h" |
29 | #include "sway/sway_text_node.h" | ||
27 | #include "sway/tree/arrange.h" | 30 | #include "sway/tree/arrange.h" |
28 | #include "sway/tree/container.h" | 31 | #include "sway/tree/container.h" |
29 | #include "sway/tree/view.h" | 32 | #include "sway/tree/view.h" |
@@ -33,15 +36,29 @@ | |||
33 | #include "pango.h" | 36 | #include "pango.h" |
34 | #include "stringop.h" | 37 | #include "stringop.h" |
35 | 38 | ||
36 | 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, |
37 | 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 | |||
38 | view->type = type; | 55 | view->type = type; |
39 | view->impl = impl; | 56 | view->impl = impl; |
40 | view->executed_criteria = create_list(); | 57 | view->executed_criteria = create_list(); |
41 | wl_list_init(&view->saved_buffers); | ||
42 | view->allow_request_urgent = true; | 58 | view->allow_request_urgent = true; |
43 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; | 59 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; |
44 | wl_signal_init(&view->events.unmap); | 60 | wl_signal_init(&view->events.unmap); |
61 | return true; | ||
45 | } | 62 | } |
46 | 63 | ||
47 | void view_destroy(struct sway_view *view) { | 64 | void view_destroy(struct sway_view *view) { |
@@ -58,11 +75,10 @@ void view_destroy(struct sway_view *view) { | |||
58 | return; | 75 | return; |
59 | } | 76 | } |
60 | wl_list_remove(&view->events.unmap.listener_list); | 77 | wl_list_remove(&view->events.unmap.listener_list); |
61 | if (!wl_list_empty(&view->saved_buffers)) { | ||
62 | view_remove_saved_buffer(view); | ||
63 | } | ||
64 | list_free(view->executed_criteria); | 78 | list_free(view->executed_criteria); |
65 | 79 | ||
80 | view_assign_ctx(view, NULL); | ||
81 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
66 | free(view->title_format); | 82 | free(view->title_format); |
67 | 83 | ||
68 | if (view->impl->destroy) { | 84 | if (view->impl->destroy) { |
@@ -363,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
363 | } | 379 | } |
364 | } | 380 | } |
365 | 381 | ||
366 | void view_request_activate(struct sway_view *view) { | 382 | void view_request_activate(struct sway_view *view, struct sway_seat *seat) { |
367 | struct sway_workspace *ws = view->container->pending.workspace; | 383 | struct sway_workspace *ws = view->container->pending.workspace; |
368 | if (!ws) { // hidden scratchpad container | 384 | if (!seat) { |
369 | return; | 385 | seat = input_manager_current_seat(); |
370 | } | 386 | } |
371 | struct sway_seat *seat = input_manager_current_seat(); | ||
372 | 387 | ||
373 | switch (config->focus_on_window_activation) { | 388 | switch (config->focus_on_window_activation) { |
374 | case FOWA_SMART: | 389 | case FOWA_SMART: |
375 | if (workspace_is_visible(ws)) { | 390 | if (ws && workspace_is_visible(ws)) { |
376 | seat_set_focus_container(seat, view->container); | 391 | seat_set_focus_container(seat, view->container); |
392 | container_raise_floating(view->container); | ||
377 | } else { | 393 | } else { |
378 | view_set_urgent(view, true); | 394 | view_set_urgent(view, true); |
379 | } | 395 | } |
@@ -382,11 +398,23 @@ void view_request_activate(struct sway_view *view) { | |||
382 | view_set_urgent(view, true); | 398 | view_set_urgent(view, true); |
383 | break; | 399 | break; |
384 | case FOWA_FOCUS: | 400 | case FOWA_FOCUS: |
385 | 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 | } | ||
386 | break; | 407 | break; |
387 | case FOWA_NONE: | 408 | case FOWA_NONE: |
388 | break; | 409 | break; |
389 | } | 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 | } | ||
390 | } | 418 | } |
391 | 419 | ||
392 | 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) { |
@@ -433,52 +461,6 @@ void view_close_popups(struct sway_view *view) { | |||
433 | } | 461 | } |
434 | } | 462 | } |
435 | 463 | ||
436 | void view_damage_from(struct sway_view *view) { | ||
437 | for (int i = 0; i < root->outputs->length; ++i) { | ||
438 | struct sway_output *output = root->outputs->items[i]; | ||
439 | output_damage_from_view(output, view); | ||
440 | } | ||
441 | } | ||
442 | |||
443 | void view_for_each_surface(struct sway_view *view, | ||
444 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
445 | if (!view->surface) { | ||
446 | return; | ||
447 | } | ||
448 | if (view->impl->for_each_surface) { | ||
449 | view->impl->for_each_surface(view, iterator, user_data); | ||
450 | } else { | ||
451 | wlr_surface_for_each_surface(view->surface, iterator, user_data); | ||
452 | } | ||
453 | } | ||
454 | |||
455 | void view_for_each_popup_surface(struct sway_view *view, | ||
456 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
457 | if (!view->surface) { | ||
458 | return; | ||
459 | } | ||
460 | if (view->impl->for_each_popup_surface) { | ||
461 | view->impl->for_each_popup_surface(view, iterator, user_data); | ||
462 | } | ||
463 | } | ||
464 | |||
465 | static void view_subsurface_create(struct sway_view *view, | ||
466 | struct wlr_subsurface *subsurface); | ||
467 | |||
468 | static void view_init_subsurfaces(struct sway_view *view, | ||
469 | struct wlr_surface *surface); | ||
470 | |||
471 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
472 | struct wlr_surface *surface); | ||
473 | |||
474 | static void view_handle_surface_new_subsurface(struct wl_listener *listener, | ||
475 | void *data) { | ||
476 | struct sway_view *view = | ||
477 | wl_container_of(listener, view, surface_new_subsurface); | ||
478 | struct wlr_subsurface *subsurface = data; | ||
479 | view_subsurface_create(view, subsurface); | ||
480 | } | ||
481 | |||
482 | static bool view_has_executed_criteria(struct sway_view *view, | 464 | static bool view_has_executed_criteria(struct sway_view *view, |
483 | struct criteria *criteria) { | 465 | struct criteria *criteria) { |
484 | for (int i = 0; i < view->executed_criteria->length; ++i) { | 466 | for (int i = 0; i < view->executed_criteria->length; ++i) { |
@@ -520,7 +502,7 @@ static void view_populate_pid(struct sway_view *view) { | |||
520 | #if HAVE_XWAYLAND | 502 | #if HAVE_XWAYLAND |
521 | case SWAY_VIEW_XWAYLAND:; | 503 | case SWAY_VIEW_XWAYLAND:; |
522 | struct wlr_xwayland_surface *surf = | 504 | struct wlr_xwayland_surface *surf = |
523 | wlr_xwayland_surface_from_wlr_surface(view->surface); | 505 | wlr_xwayland_surface_try_from_wlr_surface(view->surface); |
524 | pid = surf->pid; | 506 | pid = surf->pid; |
525 | break; | 507 | break; |
526 | #endif | 508 | #endif |
@@ -533,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) { | |||
533 | view->pid = pid; | 515 | view->pid = pid; |
534 | } | 516 | } |
535 | 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 | |||
536 | static struct sway_workspace *select_workspace(struct sway_view *view) { | 532 | static struct sway_workspace *select_workspace(struct sway_view *view) { |
537 | struct sway_seat *seat = input_manager_current_seat(); | 533 | struct sway_seat *seat = input_manager_current_seat(); |
538 | 534 | ||
@@ -568,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
568 | } | 564 | } |
569 | list_free(criterias); | 565 | list_free(criterias); |
570 | if (ws) { | 566 | if (ws) { |
571 | root_remove_workspace_pid(view->pid); | 567 | view_assign_ctx(view, NULL); |
572 | return ws; | 568 | return ws; |
573 | } | 569 | } |
574 | 570 | ||
575 | // Check if there's a PID mapping | 571 | // Check if there's a PID mapping |
576 | ws = root_workspace_for_pid(view->pid); | 572 | ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL; |
577 | if (ws) { | 573 | if (ws) { |
574 | view_assign_ctx(view, NULL); | ||
578 | return ws; | 575 | return ws; |
579 | } | 576 | } |
580 | 577 | ||
@@ -592,6 +589,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
592 | return NULL; | 589 | return NULL; |
593 | } | 590 | } |
594 | 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 | |||
595 | static bool should_focus(struct sway_view *view) { | 600 | static bool should_focus(struct sway_view *view) { |
596 | struct sway_seat *seat = input_manager_current_seat(); | 601 | struct sway_seat *seat = input_manager_current_seat(); |
597 | struct sway_container *prev_con = seat_get_focused_container(seat); | 602 | struct sway_container *prev_con = seat_get_focused_container(seat); |
@@ -717,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
717 | view_populate_pid(view); | 722 | view_populate_pid(view); |
718 | view->container = container_create(view); | 723 | view->container = container_create(view); |
719 | 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 | |||
720 | // 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 |
721 | // to honor that request. Otherwise, fallback to assigns, pid mappings, | 733 | // to honor that request. Otherwise, fallback to assigns, pid mappings, |
722 | // focused workspace, etc | 734 | // focused workspace, etc |
@@ -754,6 +766,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
754 | } | 766 | } |
755 | } | 767 | } |
756 | 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); | ||
775 | |||
757 | view->foreign_toplevel = | 776 | view->foreign_toplevel = |
758 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); | 777 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); |
759 | view->foreign_activate_request.notify = handle_foreign_activate_request; | 778 | view->foreign_activate_request.notify = handle_foreign_activate_request; |
@@ -777,11 +796,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
777 | } | 796 | } |
778 | ipc_event_window(view->container, "new"); | 797 | ipc_event_window(view->container, "new"); |
779 | 798 | ||
780 | view_init_subsurfaces(view, wlr_surface); | ||
781 | wl_signal_add(&wlr_surface->events.new_subsurface, | ||
782 | &view->surface_new_subsurface); | ||
783 | view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; | ||
784 | |||
785 | if (decoration) { | 799 | if (decoration) { |
786 | view_update_csd_from_client(view, decoration); | 800 | view_update_csd_from_client(view, decoration); |
787 | } | 801 | } |
@@ -825,9 +839,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
825 | bool set_focus = should_focus(view); | 839 | bool set_focus = should_focus(view); |
826 | 840 | ||
827 | #if HAVE_XWAYLAND | 841 | #if HAVE_XWAYLAND |
828 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 842 | struct wlr_xwayland_surface *xsurface; |
829 | struct wlr_xwayland_surface *xsurface = | 843 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
830 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
831 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != | 844 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != |
832 | WLR_ICCCM_INPUT_MODEL_NONE; | 845 | WLR_ICCCM_INPUT_MODEL_NONE; |
833 | } | 846 | } |
@@ -837,6 +850,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
837 | input_manager_set_focus(&view->container->node); | 850 | input_manager_set_focus(&view->container->node); |
838 | } | 851 | } |
839 | 852 | ||
853 | if (view->ext_foreign_toplevel) { | ||
854 | update_ext_foreign_toplevel(view); | ||
855 | } | ||
856 | |||
840 | const char *app_id; | 857 | const char *app_id; |
841 | const char *class; | 858 | const char *class; |
842 | if ((app_id = view_get_app_id(view)) != NULL) { | 859 | if ((app_id = view_get_app_id(view)) != NULL) { |
@@ -847,15 +864,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
847 | } | 864 | } |
848 | 865 | ||
849 | void view_unmap(struct sway_view *view) { | 866 | void view_unmap(struct sway_view *view) { |
850 | wl_signal_emit(&view->events.unmap, view); | 867 | wl_signal_emit_mutable(&view->events.unmap, view); |
851 | 868 | ||
852 | wl_list_remove(&view->surface_new_subsurface.link); | 869 | view->executed_criteria->length = 0; |
853 | 870 | ||
854 | if (view->urgent_timer) { | 871 | if (view->urgent_timer) { |
855 | wl_event_source_remove(view->urgent_timer); | 872 | wl_event_source_remove(view->urgent_timer); |
856 | view->urgent_timer = NULL; | 873 | view->urgent_timer = NULL; |
857 | } | 874 | } |
858 | 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 | |||
859 | if (view->foreign_toplevel) { | 881 | if (view->foreign_toplevel) { |
860 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); | 882 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); |
861 | view->foreign_toplevel = NULL; | 883 | view->foreign_toplevel = NULL; |
@@ -902,291 +924,47 @@ void view_update_size(struct sway_view *view) { | |||
902 | container_set_geometry_from_content(con); | 924 | container_set_geometry_from_content(con); |
903 | } | 925 | } |
904 | 926 | ||
905 | void view_center_surface(struct sway_view *view) { | 927 | void view_center_and_clip_surface(struct sway_view *view) { |
906 | struct sway_container *con = view->container; | 928 | struct sway_container *con = view->container; |
907 | // We always center the current coordinates rather than the next, as the | ||
908 | // geometry immediately affects the currently active rendering. | ||
909 | con->surface_x = fmax(con->current.content_x, con->current.content_x + | ||
910 | (con->current.content_width - view->geometry.width) / 2); | ||
911 | con->surface_y = fmax(con->current.content_y, con->current.content_y + | ||
912 | (con->current.content_height - view->geometry.height) / 2); | ||
913 | } | ||
914 | 929 | ||
915 | static const struct sway_view_child_impl subsurface_impl; | 930 | if (container_is_floating(con)) { |
931 | // We always center the current coordinates rather than the next, as the | ||
932 | // geometry immediately affects the currently active rendering. | ||
933 | int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); | ||
934 | int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); | ||
916 | 935 | ||
917 | static void subsurface_get_view_coords(struct sway_view_child *child, | 936 | wlr_scene_node_set_position(&view->content_tree->node, x, y); |
918 | int *sx, int *sy) { | ||
919 | struct wlr_surface *surface = child->surface; | ||
920 | if (child->parent && child->parent->impl && | ||
921 | child->parent->impl->get_view_coords) { | ||
922 | child->parent->impl->get_view_coords(child->parent, sx, sy); | ||
923 | } else { | 937 | } else { |
924 | *sx = *sy = 0; | 938 | wlr_scene_node_set_position(&view->content_tree->node, 0, 0); |
925 | } | 939 | } |
926 | struct wlr_subsurface *subsurface = | ||
927 | wlr_subsurface_from_wlr_surface(surface); | ||
928 | *sx += subsurface->current.x; | ||
929 | *sy += subsurface->current.y; | ||
930 | } | ||
931 | 940 | ||
932 | static void subsurface_destroy(struct sway_view_child *child) { | 941 | // only make sure to clip the content if there is content to clip |
933 | if (!sway_assert(child->impl == &subsurface_impl, | 942 | if (!wl_list_empty(&con->view->content_tree->children)) { |
934 | "Expected a subsurface")) { | 943 | wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){ |
935 | return; | 944 | .x = con->view->geometry.x, |
936 | } | 945 | .y = con->view->geometry.y, |
937 | struct sway_subsurface *subsurface = (struct sway_subsurface *)child; | 946 | .width = con->current.content_width, |
938 | wl_list_remove(&subsurface->destroy.link); | 947 | .height = con->current.content_height, |
939 | free(subsurface); | 948 | }); |
940 | } | ||
941 | |||
942 | static const struct sway_view_child_impl subsurface_impl = { | ||
943 | .get_view_coords = subsurface_get_view_coords, | ||
944 | .destroy = subsurface_destroy, | ||
945 | }; | ||
946 | |||
947 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
948 | void *data) { | ||
949 | struct sway_subsurface *subsurface = | ||
950 | wl_container_of(listener, subsurface, destroy); | ||
951 | struct sway_view_child *child = &subsurface->child; | ||
952 | view_child_destroy(child); | ||
953 | } | ||
954 | |||
955 | static void view_child_damage(struct sway_view_child *child, bool whole); | ||
956 | |||
957 | static void view_subsurface_create(struct sway_view *view, | ||
958 | struct wlr_subsurface *wlr_subsurface) { | ||
959 | struct sway_subsurface *subsurface = | ||
960 | calloc(1, sizeof(struct sway_subsurface)); | ||
961 | if (subsurface == NULL) { | ||
962 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
963 | return; | ||
964 | } | ||
965 | view_child_init(&subsurface->child, &subsurface_impl, view, | ||
966 | wlr_subsurface->surface); | ||
967 | |||
968 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
969 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
970 | |||
971 | subsurface->child.mapped = true; | ||
972 | |||
973 | view_child_damage(&subsurface->child, true); | ||
974 | } | ||
975 | |||
976 | static void view_child_subsurface_create(struct sway_view_child *child, | ||
977 | struct wlr_subsurface *wlr_subsurface) { | ||
978 | struct sway_subsurface *subsurface = | ||
979 | calloc(1, sizeof(struct sway_subsurface)); | ||
980 | if (subsurface == NULL) { | ||
981 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
982 | return; | ||
983 | } | ||
984 | subsurface->child.parent = child; | ||
985 | wl_list_insert(&child->children, &subsurface->child.link); | ||
986 | view_child_init(&subsurface->child, &subsurface_impl, child->view, | ||
987 | wlr_subsurface->surface); | ||
988 | |||
989 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
990 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
991 | |||
992 | subsurface->child.mapped = true; | ||
993 | |||
994 | view_child_damage(&subsurface->child, true); | ||
995 | } | ||
996 | |||
997 | static bool view_child_is_mapped(struct sway_view_child *child) { | ||
998 | while (child) { | ||
999 | if (!child->mapped) { | ||
1000 | return false; | ||
1001 | } | ||
1002 | child = child->parent; | ||
1003 | } | ||
1004 | return true; | ||
1005 | } | ||
1006 | |||
1007 | static void view_child_damage(struct sway_view_child *child, bool whole) { | ||
1008 | if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) { | ||
1009 | return; | ||
1010 | } | ||
1011 | int sx, sy; | ||
1012 | child->impl->get_view_coords(child, &sx, &sy); | ||
1013 | desktop_damage_surface(child->surface, | ||
1014 | child->view->container->pending.content_x - | ||
1015 | child->view->geometry.x + sx, | ||
1016 | child->view->container->pending.content_y - | ||
1017 | child->view->geometry.y + sy, whole); | ||
1018 | } | ||
1019 | |||
1020 | static void view_child_handle_surface_commit(struct wl_listener *listener, | ||
1021 | void *data) { | ||
1022 | struct sway_view_child *child = | ||
1023 | wl_container_of(listener, child, surface_commit); | ||
1024 | view_child_damage(child, false); | ||
1025 | } | ||
1026 | |||
1027 | static void view_child_handle_surface_new_subsurface( | ||
1028 | struct wl_listener *listener, void *data) { | ||
1029 | struct sway_view_child *child = | ||
1030 | wl_container_of(listener, child, surface_new_subsurface); | ||
1031 | struct wlr_subsurface *subsurface = data; | ||
1032 | view_child_subsurface_create(child, subsurface); | ||
1033 | } | ||
1034 | |||
1035 | static void view_child_handle_surface_destroy(struct wl_listener *listener, | ||
1036 | void *data) { | ||
1037 | struct sway_view_child *child = | ||
1038 | wl_container_of(listener, child, surface_destroy); | ||
1039 | view_child_destroy(child); | ||
1040 | } | ||
1041 | |||
1042 | static void view_init_subsurfaces(struct sway_view *view, | ||
1043 | struct wlr_surface *surface) { | ||
1044 | struct wlr_subsurface *subsurface; | ||
1045 | wl_list_for_each(subsurface, &surface->current.subsurfaces_below, | ||
1046 | current.link) { | ||
1047 | view_subsurface_create(view, subsurface); | ||
1048 | } | ||
1049 | wl_list_for_each(subsurface, &surface->current.subsurfaces_above, | ||
1050 | current.link) { | ||
1051 | view_subsurface_create(view, subsurface); | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
1056 | struct wlr_surface *surface) { | ||
1057 | struct wlr_subsurface *subsurface; | ||
1058 | wl_list_for_each(subsurface, &surface->current.subsurfaces_below, | ||
1059 | current.link) { | ||
1060 | view_child_subsurface_create(view_child, subsurface); | ||
1061 | } | ||
1062 | wl_list_for_each(subsurface, &surface->current.subsurfaces_above, | ||
1063 | current.link) { | ||
1064 | view_child_subsurface_create(view_child, subsurface); | ||
1065 | } | ||
1066 | } | ||
1067 | |||
1068 | static void view_child_handle_surface_map(struct wl_listener *listener, | ||
1069 | void *data) { | ||
1070 | struct sway_view_child *child = | ||
1071 | wl_container_of(listener, child, surface_map); | ||
1072 | child->mapped = true; | ||
1073 | view_child_damage(child, true); | ||
1074 | } | ||
1075 | |||
1076 | static void view_child_handle_surface_unmap(struct wl_listener *listener, | ||
1077 | void *data) { | ||
1078 | struct sway_view_child *child = | ||
1079 | wl_container_of(listener, child, surface_unmap); | ||
1080 | view_child_damage(child, true); | ||
1081 | child->mapped = false; | ||
1082 | } | ||
1083 | |||
1084 | static void view_child_handle_view_unmap(struct wl_listener *listener, | ||
1085 | void *data) { | ||
1086 | struct sway_view_child *child = | ||
1087 | wl_container_of(listener, child, view_unmap); | ||
1088 | view_child_damage(child, true); | ||
1089 | child->mapped = false; | ||
1090 | } | ||
1091 | |||
1092 | void view_child_init(struct sway_view_child *child, | ||
1093 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
1094 | struct wlr_surface *surface) { | ||
1095 | child->impl = impl; | ||
1096 | child->view = view; | ||
1097 | child->surface = surface; | ||
1098 | wl_list_init(&child->children); | ||
1099 | |||
1100 | wl_signal_add(&surface->events.commit, &child->surface_commit); | ||
1101 | child->surface_commit.notify = view_child_handle_surface_commit; | ||
1102 | wl_signal_add(&surface->events.new_subsurface, | ||
1103 | &child->surface_new_subsurface); | ||
1104 | child->surface_new_subsurface.notify = | ||
1105 | view_child_handle_surface_new_subsurface; | ||
1106 | wl_signal_add(&surface->events.destroy, &child->surface_destroy); | ||
1107 | child->surface_destroy.notify = view_child_handle_surface_destroy; | ||
1108 | |||
1109 | // Not all child views have a map/unmap event | ||
1110 | child->surface_map.notify = view_child_handle_surface_map; | ||
1111 | wl_list_init(&child->surface_map.link); | ||
1112 | child->surface_unmap.notify = view_child_handle_surface_unmap; | ||
1113 | wl_list_init(&child->surface_unmap.link); | ||
1114 | |||
1115 | wl_signal_add(&view->events.unmap, &child->view_unmap); | ||
1116 | child->view_unmap.notify = view_child_handle_view_unmap; | ||
1117 | |||
1118 | struct sway_container *container = child->view->container; | ||
1119 | if (container != NULL) { | ||
1120 | struct sway_workspace *workspace = container->pending.workspace; | ||
1121 | if (workspace) { | ||
1122 | wlr_surface_send_enter(child->surface, workspace->output->wlr_output); | ||
1123 | } | ||
1124 | } | ||
1125 | |||
1126 | view_child_init_subsurfaces(child, surface); | ||
1127 | } | ||
1128 | |||
1129 | void view_child_destroy(struct sway_view_child *child) { | ||
1130 | if (view_child_is_mapped(child) && child->view->container != NULL) { | ||
1131 | view_child_damage(child, true); | ||
1132 | } | ||
1133 | |||
1134 | if (child->parent != NULL) { | ||
1135 | wl_list_remove(&child->link); | ||
1136 | child->parent = NULL; | ||
1137 | } | ||
1138 | |||
1139 | struct sway_view_child *subchild, *tmpchild; | ||
1140 | wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { | ||
1141 | wl_list_remove(&subchild->link); | ||
1142 | subchild->parent = NULL; | ||
1143 | // The subchild lost its parent link, so it cannot see that the parent | ||
1144 | // is unmapped. Unmap it directly. | ||
1145 | subchild->mapped = false; | ||
1146 | } | ||
1147 | |||
1148 | wl_list_remove(&child->surface_commit.link); | ||
1149 | wl_list_remove(&child->surface_destroy.link); | ||
1150 | wl_list_remove(&child->surface_map.link); | ||
1151 | wl_list_remove(&child->surface_unmap.link); | ||
1152 | wl_list_remove(&child->view_unmap.link); | ||
1153 | wl_list_remove(&child->surface_new_subsurface.link); | ||
1154 | |||
1155 | if (child->impl && child->impl->destroy) { | ||
1156 | child->impl->destroy(child); | ||
1157 | } else { | ||
1158 | free(child); | ||
1159 | } | 949 | } |
1160 | } | 950 | } |
1161 | 951 | ||
1162 | 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) { |
1163 | if (wlr_surface_is_xdg_surface(wlr_surface)) { | 953 | struct wlr_xdg_surface *xdg_surface; |
1164 | struct wlr_xdg_surface *xdg_surface = | 954 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { |
1165 | wlr_xdg_surface_from_wlr_surface(wlr_surface); | ||
1166 | if (xdg_surface == NULL) { | ||
1167 | return NULL; | ||
1168 | } | ||
1169 | return view_from_wlr_xdg_surface(xdg_surface); | 955 | return view_from_wlr_xdg_surface(xdg_surface); |
1170 | } | 956 | } |
1171 | #if HAVE_XWAYLAND | 957 | #if HAVE_XWAYLAND |
1172 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 958 | struct wlr_xwayland_surface *xsurface; |
1173 | struct wlr_xwayland_surface *xsurface = | 959 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
1174 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
1175 | if (xsurface == NULL) { | ||
1176 | return NULL; | ||
1177 | } | ||
1178 | return view_from_wlr_xwayland_surface(xsurface); | 960 | return view_from_wlr_xwayland_surface(xsurface); |
1179 | } | 961 | } |
1180 | #endif | 962 | #endif |
1181 | if (wlr_surface_is_subsurface(wlr_surface)) { | 963 | struct wlr_subsurface *subsurface; |
1182 | struct wlr_subsurface *subsurface = | 964 | if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) { |
1183 | wlr_subsurface_from_wlr_surface(wlr_surface); | ||
1184 | if (subsurface == NULL) { | ||
1185 | return NULL; | ||
1186 | } | ||
1187 | return view_from_wlr_surface(subsurface->parent); | 965 | return view_from_wlr_surface(subsurface->parent); |
1188 | } | 966 | } |
1189 | if (wlr_surface_is_layer_surface(wlr_surface)) { | 967 | if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { |
1190 | return NULL; | 968 | return NULL; |
1191 | } | 969 | } |
1192 | 970 | ||
@@ -1267,6 +1045,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { | |||
1267 | return len; | 1045 | return len; |
1268 | } | 1046 | } |
1269 | 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 | |||
1270 | void view_update_title(struct sway_view *view, bool force) { | 1060 | void view_update_title(struct sway_view *view, bool force) { |
1271 | const char *title = view_get_title(view); | 1061 | const char *title = view_get_title(view); |
1272 | 1062 | ||
@@ -1282,29 +1072,41 @@ void view_update_title(struct sway_view *view, bool force) { | |||
1282 | 1072 | ||
1283 | free(view->container->title); | 1073 | free(view->container->title); |
1284 | free(view->container->formatted_title); | 1074 | free(view->container->formatted_title); |
1285 | if (title) { | 1075 | |
1286 | size_t len = parse_title_format(view, NULL); | 1076 | size_t len = parse_title_format(view, NULL); |
1077 | |||
1078 | if (len) { | ||
1287 | char *buffer = calloc(len + 1, sizeof(char)); | 1079 | char *buffer = calloc(len + 1, sizeof(char)); |
1288 | if (!sway_assert(buffer, "Unable to allocate title string")) { | 1080 | if (!sway_assert(buffer, "Unable to allocate title string")) { |
1289 | return; | 1081 | return; |
1290 | } | 1082 | } |
1291 | parse_title_format(view, buffer); | ||
1292 | 1083 | ||
1293 | view->container->title = strdup(title); | 1084 | parse_title_format(view, buffer); |
1294 | view->container->formatted_title = buffer; | 1085 | view->container->formatted_title = buffer; |
1295 | } else { | 1086 | } else { |
1296 | view->container->title = NULL; | ||
1297 | view->container->formatted_title = NULL; | 1087 | view->container->formatted_title = NULL; |
1298 | } | 1088 | } |
1299 | 1089 | ||
1090 | view->container->title = title ? strdup(title) : NULL; | ||
1091 | |||
1300 | // Update title after the global font height is updated | 1092 | // Update title after the global font height is updated |
1301 | 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 | } | ||
1302 | 1100 | ||
1303 | ipc_event_window(view->container, "title"); | 1101 | ipc_event_window(view->container, "title"); |
1304 | 1102 | ||
1305 | if (view->foreign_toplevel && title) { | 1103 | if (view->foreign_toplevel && title) { |
1306 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); | 1104 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); |
1307 | } | 1105 | } |
1106 | |||
1107 | if (view->ext_foreign_toplevel) { | ||
1108 | update_ext_foreign_toplevel(view); | ||
1109 | } | ||
1308 | } | 1110 | } |
1309 | 1111 | ||
1310 | bool view_is_visible(struct sway_view *view) { | 1112 | bool view_is_visible(struct sway_view *view) { |
@@ -1365,6 +1167,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1365 | return; | 1167 | return; |
1366 | } | 1168 | } |
1367 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | 1169 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); |
1170 | container_update_itself_and_parents(view->container); | ||
1368 | } else { | 1171 | } else { |
1369 | view->urgent = (struct timespec){ 0 }; | 1172 | view->urgent = (struct timespec){ 0 }; |
1370 | if (view->urgent_timer) { | 1173 | if (view->urgent_timer) { |
@@ -1372,7 +1175,6 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1372 | view->urgent_timer = NULL; | 1175 | view->urgent_timer = NULL; |
1373 | } | 1176 | } |
1374 | } | 1177 | } |
1375 | container_damage_whole(view->container); | ||
1376 | 1178 | ||
1377 | ipc_event_window(view->container, "urgent"); | 1179 | ipc_event_window(view->container, "urgent"); |
1378 | 1180 | ||
@@ -1386,40 +1188,54 @@ bool view_is_urgent(struct sway_view *view) { | |||
1386 | } | 1188 | } |
1387 | 1189 | ||
1388 | void view_remove_saved_buffer(struct sway_view *view) { | 1190 | void view_remove_saved_buffer(struct sway_view *view) { |
1389 | 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")) { |
1390 | return; | 1192 | return; |
1391 | } | 1193 | } |
1392 | struct sway_saved_buffer *saved_buf, *tmp; | 1194 | |
1393 | wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { | 1195 | wlr_scene_node_destroy(&view->saved_surface_tree->node); |
1394 | wlr_buffer_unlock(&saved_buf->buffer->base); | 1196 | view->saved_surface_tree = NULL; |
1395 | wl_list_remove(&saved_buf->link); | 1197 | wlr_scene_node_set_enabled(&view->content_tree->node, true); |
1396 | free(saved_buf); | ||
1397 | } | ||
1398 | } | 1198 | } |
1399 | 1199 | ||
1400 | static void view_save_buffer_iterator(struct wlr_surface *surface, | 1200 | static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, |
1401 | int sx, int sy, void *data) { | 1201 | int sx, int sy, void *data) { |
1402 | struct sway_view *view = data; | 1202 | struct wlr_scene_tree *tree = data; |
1403 | 1203 | ||
1404 | if (surface && wlr_surface_has_buffer(surface)) { | 1204 | struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); |
1405 | wlr_buffer_lock(&surface->buffer->base); | 1205 | if (!sbuf) { |
1406 | 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"); |
1407 | saved_buffer->buffer = surface->buffer; | 1207 | return; |
1408 | saved_buffer->width = surface->current.width; | ||
1409 | saved_buffer->height = surface->current.height; | ||
1410 | saved_buffer->x = view->container->surface_x + sx; | ||
1411 | saved_buffer->y = view->container->surface_y + sy; | ||
1412 | saved_buffer->transform = surface->current.transform; | ||
1413 | wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); | ||
1414 | wl_list_insert(view->saved_buffers.prev, &saved_buffer->link); | ||
1415 | } | 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); | ||
1416 | } | 1217 | } |
1417 | 1218 | ||
1418 | void view_save_buffer(struct sway_view *view) { | 1219 | void view_save_buffer(struct sway_view *view) { |
1419 | 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")) { |
1420 | view_remove_saved_buffer(view); | 1221 | view_remove_saved_buffer(view); |
1421 | } | 1222 | } |
1422 | 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); | ||
1423 | } | 1239 | } |
1424 | 1240 | ||
1425 | bool view_is_transient_for(struct sway_view *child, | 1241 | bool view_is_transient_for(struct sway_view *child, |
@@ -1427,3 +1243,19 @@ bool view_is_transient_for(struct sway_view *child, | |||
1427 | return child->impl->is_transient_for && | 1243 | return child->impl->is_transient_for && |
1428 | child->impl->is_transient_for(child, ancestor); | 1244 | child->impl->is_transient_for(child, ancestor); |
1429 | } | 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 c84320bd..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> |
@@ -56,6 +55,8 @@ struct sway_output *workspace_get_initial_output(const char *name) { | |||
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 | ||
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 ec9e8d68..fa8c6279 100644 --- a/sway/xdg_decoration.c +++ b/sway/xdg_decoration.c | |||
@@ -23,37 +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 | struct sway_view *view = deco->view; | 26 | set_xdg_decoration_mode(deco); |
27 | enum wlr_xdg_toplevel_decoration_v1_mode mode = | ||
28 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; | ||
29 | enum wlr_xdg_toplevel_decoration_v1_mode client_mode = | ||
30 | deco->wlr_xdg_decoration->requested_mode; | ||
31 | |||
32 | bool floating; | ||
33 | if (view->container) { | ||
34 | floating = container_is_floating(view->container); | ||
35 | bool csd = false; | ||
36 | csd = client_mode == | ||
37 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; | ||
38 | view_update_csd_from_client(view, csd); | ||
39 | arrange_container(view->container); | ||
40 | transaction_commit_dirty(); | ||
41 | } else { | ||
42 | floating = view->impl->wants_floating && | ||
43 | view->impl->wants_floating(view); | ||
44 | } | ||
45 | |||
46 | if (floating && client_mode) { | ||
47 | mode = client_mode; | ||
48 | } | ||
49 | |||
50 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | ||
51 | mode); | ||
52 | } | 27 | } |
53 | 28 | ||
54 | void handle_xdg_decoration(struct wl_listener *listener, void *data) { | 29 | void handle_xdg_decoration(struct wl_listener *listener, void *data) { |
55 | struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; | 30 | struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; |
56 | 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; |
57 | 32 | ||
58 | struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); | 33 | struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); |
59 | if (deco == NULL) { | 34 | if (deco == NULL) { |
@@ -72,16 +47,46 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data) { | |||
72 | 47 | ||
73 | wl_list_insert(&server.xdg_decorations, &deco->link); | 48 | wl_list_insert(&server.xdg_decorations, &deco->link); |
74 | 49 | ||
75 | xdg_decoration_handle_request_mode(&deco->request_mode, wlr_deco); | 50 | set_xdg_decoration_mode(deco); |
76 | } | 51 | } |
77 | 52 | ||
78 | struct sway_xdg_decoration *xdg_decoration_from_surface( | 53 | struct sway_xdg_decoration *xdg_decoration_from_surface( |
79 | struct wlr_surface *surface) { | 54 | struct wlr_surface *surface) { |
80 | struct sway_xdg_decoration *deco; | 55 | struct sway_xdg_decoration *deco; |
81 | wl_list_for_each(deco, &server.xdg_decorations, link) { | 56 | wl_list_for_each(deco, &server.xdg_decorations, link) { |
82 | if (deco->wlr_xdg_decoration->surface->surface == surface) { | 57 | if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) { |
83 | return deco; | 58 | return deco; |
84 | } | 59 | } |
85 | } | 60 | } |
86 | return NULL; | 61 | return NULL; |
87 | } | 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 5e4ebd97..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> |
@@ -362,6 +361,9 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
362 | } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { | 361 | } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { |
363 | bar->xdg_output_manager = wl_registry_bind(registry, name, | 362 | bar->xdg_output_manager = wl_registry_bind(registry, name, |
364 | &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); | ||
365 | } | 367 | } |
366 | } | 368 | } |
367 | 369 | ||
@@ -425,15 +427,17 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) { | |||
425 | // Second roundtrip for xdg-output | 427 | // Second roundtrip for xdg-output |
426 | wl_display_roundtrip(bar->display); | 428 | wl_display_roundtrip(bar->display); |
427 | 429 | ||
428 | struct swaybar_seat *seat; | 430 | if (!bar->cursor_shape_manager) { |
429 | wl_list_for_each(seat, &bar->seats, link) { | 431 | struct swaybar_seat *seat; |
430 | struct swaybar_pointer *pointer = &seat->pointer; | 432 | wl_list_for_each(seat, &bar->seats, link) { |
431 | if (!pointer) { | 433 | struct swaybar_pointer *pointer = &seat->pointer; |
432 | continue; | 434 | if (!pointer) { |
435 | continue; | ||
436 | } | ||
437 | pointer->cursor_surface = | ||
438 | wl_compositor_create_surface(bar->compositor); | ||
439 | assert(pointer->cursor_surface); | ||
433 | } | 440 | } |
434 | pointer->cursor_surface = | ||
435 | wl_compositor_create_surface(bar->compositor); | ||
436 | assert(pointer->cursor_surface); | ||
437 | } | 441 | } |
438 | 442 | ||
439 | if (bar->config->status_command) { | 443 | if (bar->config->status_command) { |
diff --git a/swaybar/config.c b/swaybar/config.c index 5e828773..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" |
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 6d00befb..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> |
@@ -269,11 +268,16 @@ bool i3bar_handle_readable(struct status_line *status) { | |||
269 | 268 | ||
270 | 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, |
271 | 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, |
272 | double w, double h, int scale, uint32_t button) { | 271 | double w, double h, int scale, uint32_t button, bool released) { |
273 | sway_log(SWAY_DEBUG, "block %s clicked", block->name); | 272 | sway_log(SWAY_DEBUG, "block %s clicked", block->name); |
274 | if (!block->name || !status->click_events) { | 273 | if (!block->name || !status->click_events) { |
275 | return HOTSPOT_PROCESS; | 274 | return HOTSPOT_PROCESS; |
276 | } | 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 | } | ||
277 | 281 | ||
278 | struct json_object *event_json = json_object_new_object(); | 282 | struct json_object *event_json = json_object_new_object(); |
279 | 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 c8c8f0d4..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,14 +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) { |
164 | bool released = state == WL_POINTER_BUTTON_STATE_RELEASED; | ||
145 | struct swaybar_hotspot *hotspot; | 165 | struct swaybar_hotspot *hotspot; |
146 | wl_list_for_each(hotspot, &output->hotspots, link) { | 166 | wl_list_for_each(hotspot, &output->hotspots, link) { |
147 | if (x >= hotspot->x && y >= hotspot->y | 167 | if (x >= hotspot->x && y >= hotspot->y |
148 | && x < hotspot->x + hotspot->width | 168 | && x < hotspot->x + hotspot->width |
149 | && y < hotspot->y + hotspot->height) { | 169 | && y < hotspot->y + hotspot->height) { |
150 | if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, | 170 | if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, |
151 | button, hotspot->data)) { | 171 | button, released, hotspot->data)) { |
152 | return true; | 172 | return true; |
153 | } | 173 | } |
154 | } | 174 | } |
@@ -166,14 +186,11 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, | |||
166 | return; | 186 | return; |
167 | } | 187 | } |
168 | 188 | ||
169 | if (check_bindings(seat->bar, button, state)) { | 189 | if (process_hotspots(output, pointer->x, pointer->y, button, state)) { |
170 | return; | 190 | return; |
171 | } | 191 | } |
172 | 192 | ||
173 | if (state != WL_POINTER_BUTTON_STATE_PRESSED) { | 193 | check_bindings(seat->bar, button, state); |
174 | return; | ||
175 | } | ||
176 | process_hotspots(output, pointer->x, pointer->y, button); | ||
177 | } | 194 | } |
178 | 195 | ||
179 | static void workspace_next(struct swaybar *bar, struct swaybar_output *output, | 196 | static void workspace_next(struct swaybar *bar, struct swaybar_output *output, |
@@ -209,7 +226,7 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output, | |||
209 | } | 226 | } |
210 | } | 227 | } |
211 | 228 | ||
212 | if (new) { | 229 | if (new && new != active) { |
213 | ipc_send_workspace_command(bar, new->name); | 230 | ipc_send_workspace_command(bar, new->name); |
214 | 231 | ||
215 | // 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. |
@@ -222,15 +239,15 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output, | |||
222 | static void process_discrete_scroll(struct swaybar_seat *seat, | 239 | static void process_discrete_scroll(struct swaybar_seat *seat, |
223 | struct swaybar_output *output, struct swaybar_pointer *pointer, | 240 | struct swaybar_output *output, struct swaybar_pointer *pointer, |
224 | uint32_t axis, wl_fixed_t value) { | 241 | uint32_t axis, wl_fixed_t value) { |
225 | // If there is a button press binding, execute it, skip default behavior, | ||
226 | // and check button release bindings | ||
227 | uint32_t button = wl_axis_to_button(axis, value); | 242 | uint32_t button = wl_axis_to_button(axis, value); |
228 | 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)) { |
229 | 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) |
230 | return; | 245 | return; |
231 | } | 246 | } |
232 | 247 | ||
233 | 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); | ||
234 | return; | 251 | return; |
235 | } | 252 | } |
236 | 253 | ||
@@ -403,7 +420,8 @@ static void wl_touch_up(void *data, struct wl_touch *wl_touch, | |||
403 | } | 420 | } |
404 | if (time - slot->time < 500) { | 421 | if (time - slot->time < 500) { |
405 | // Tap, treat it like a pointer click | 422 | // Tap, treat it like a pointer click |
406 | 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) | ||
407 | } | 425 | } |
408 | slot->output = NULL; | 426 | slot->output = NULL; |
409 | } | 427 | } |
diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 9d81a9fb..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> |
@@ -426,12 +425,9 @@ bool ipc_initialize(struct swaybar *bar) { | |||
426 | } | 425 | } |
427 | free(res); | 426 | free(res); |
428 | 427 | ||
429 | struct swaybar_config *config = bar->config; | 428 | char *subscribe = |
430 | char subscribe[128]; // suitably large buffer | 429 | "[ \"barconfig_update\", \"bar_state_update\", \"mode\", \"workspace\" ]"; |
431 | len = snprintf(subscribe, 128, | 430 | len = strlen(subscribe); |
432 | "[ \"barconfig_update\" , \"bar_state_update\" %s %s ]", | ||
433 | config->binding_mode_indicator ? ", \"mode\"" : "", | ||
434 | config->workspace_buttons ? ", \"workspace\"" : ""); | ||
435 | free(ipc_single_command(bar->ipc_event_socketfd, | 431 | free(ipc_single_command(bar->ipc_event_socketfd, |
436 | IPC_SUBSCRIBE, subscribe, &len)); | 432 | IPC_SUBSCRIBE, subscribe, &len)); |
437 | return true; | 433 | return true; |
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 a878805e..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> |
@@ -160,7 +159,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, | |||
160 | 159 | ||
161 | static enum hotspot_event_handling block_hotspot_callback( | 160 | static enum hotspot_event_handling block_hotspot_callback( |
162 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, | 161 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, |
163 | double x, double y, uint32_t button, void *data) { | 162 | double x, double y, uint32_t button, bool released, void *data) { |
164 | struct i3bar_block *block = data; | 163 | struct i3bar_block *block = data; |
165 | struct status_line *status = output->bar->status; | 164 | struct status_line *status = output->bar->status; |
166 | return i3bar_block_send_click(status, block, x, y, | 165 | return i3bar_block_send_click(status, block, x, y, |
@@ -168,7 +167,7 @@ static enum hotspot_event_handling block_hotspot_callback( | |||
168 | y - (double)hotspot->y, | 167 | y - (double)hotspot->y, |
169 | (double)hotspot->width, | 168 | (double)hotspot->width, |
170 | (double)hotspot->height, | 169 | (double)hotspot->height, |
171 | output->scale, button); | 170 | output->scale, button, released); |
172 | } | 171 | } |
173 | 172 | ||
174 | static void i3bar_block_unref_callback(void *data) { | 173 | static void i3bar_block_unref_callback(void *data) { |
@@ -292,7 +291,7 @@ static uint32_t render_status_block(struct render_context *ctx, | |||
292 | } | 291 | } |
293 | 292 | ||
294 | double offset = 0; | 293 | double offset = 0; |
295 | if (strncmp(block->align, "left", 5) == 0) { | 294 | if (strncmp(block->align, "left", 4) == 0) { |
296 | offset = x_pos; | 295 | offset = x_pos; |
297 | } else if (strncmp(block->align, "right", 5) == 0) { | 296 | } else if (strncmp(block->align, "right", 5) == 0) { |
298 | offset = x_pos + width - text_width; | 297 | offset = x_pos + width - text_width; |
@@ -599,10 +598,15 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx, | |||
599 | 598 | ||
600 | static enum hotspot_event_handling workspace_hotspot_callback( | 599 | static enum hotspot_event_handling workspace_hotspot_callback( |
601 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, | 600 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, |
602 | double x, double y, uint32_t button, void *data) { | 601 | double x, double y, uint32_t button, bool released, void *data) { |
603 | if (button != BTN_LEFT) { | 602 | if (button != BTN_LEFT) { |
604 | return HOTSPOT_PROCESS; | 603 | return HOTSPOT_PROCESS; |
605 | } | 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 | } | ||
606 | ipc_send_workspace_command(output->bar, (const char *)data); | 610 | ipc_send_workspace_command(output->bar, (const char *)data); |
607 | return HOTSPOT_IGNORE; | 611 | return HOTSPOT_IGNORE; |
608 | } | 612 | } |
@@ -688,15 +692,6 @@ static uint32_t render_to_cairo(struct render_context *ctx) { | |||
688 | struct swaybar_output *output = ctx->output; | 692 | struct swaybar_output *output = ctx->output; |
689 | struct swaybar *bar = output->bar; | 693 | struct swaybar *bar = output->bar; |
690 | struct swaybar_config *config = bar->config; | 694 | struct swaybar_config *config = bar->config; |
691 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | ||
692 | if (output->focused) { | ||
693 | ctx->background_color = config->colors.focused_background; | ||
694 | } else { | ||
695 | ctx->background_color = config->colors.background; | ||
696 | } | ||
697 | |||
698 | cairo_set_source_u32(cairo, ctx->background_color); | ||
699 | cairo_paint(cairo); | ||
700 | 695 | ||
701 | int th; | 696 | int th; |
702 | get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, ""); | 697 | get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, ""); |
@@ -758,8 +753,17 @@ void render_frame(struct swaybar_output *output) { | |||
758 | 753 | ||
759 | free_hotspots(&output->hotspots); | 754 | free_hotspots(&output->hotspots); |
760 | 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 | |||
761 | struct render_context ctx = { 0 }; | 763 | struct render_context ctx = { 0 }; |
762 | 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; | ||
763 | 767 | ||
764 | cairo_surface_t *recorder = cairo_recording_surface_create( | 768 | cairo_surface_t *recorder = cairo_recording_surface_create( |
765 | CAIRO_CONTENT_COLOR_ALPHA, NULL); | 769 | CAIRO_CONTENT_COLOR_ALPHA, NULL); |
@@ -769,24 +773,23 @@ void render_frame(struct swaybar_output *output) { | |||
769 | ctx.cairo = cairo; | 773 | ctx.cairo = cairo; |
770 | 774 | ||
771 | cairo_font_options_t *fo = cairo_font_options_create(); | 775 | cairo_font_options_t *fo = cairo_font_options_create(); |
772 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
773 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); | 776 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); |
774 | ctx.textaa_safe = fo; | 777 | ctx.textaa_safe = fo; |
775 | if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { | 778 | if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { |
776 | ctx.textaa_sharp = ctx.textaa_safe; | 779 | ctx.textaa_sharp = ctx.textaa_safe; |
777 | } else { | 780 | } else { |
778 | fo = cairo_font_options_create(); | 781 | fo = cairo_font_options_create(); |
779 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
780 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | 782 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); |
781 | cairo_font_options_set_subpixel_order(fo, | 783 | cairo_font_options_set_subpixel_order(fo, |
782 | to_cairo_subpixel_order(output->subpixel)); | 784 | to_cairo_subpixel_order(output->subpixel)); |
783 | ctx.textaa_sharp = fo; | 785 | ctx.textaa_sharp = fo; |
784 | } | 786 | } |
785 | 787 | ||
786 | cairo_save(cairo); | 788 | |
787 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | 789 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); |
790 | cairo_set_source_u32(cairo, background_color); | ||
788 | cairo_paint(cairo); | 791 | cairo_paint(cairo); |
789 | cairo_restore(cairo); | 792 | |
790 | uint32_t height = render_to_cairo(&ctx); | 793 | uint32_t height = render_to_cairo(&ctx); |
791 | int config_height = output->bar->config->height; | 794 | int config_height = output->bar->config->height; |
792 | if (config_height > 0) { | 795 | if (config_height > 0) { |
@@ -831,13 +834,15 @@ void render_frame(struct swaybar_output *output) { | |||
831 | wl_surface_damage(output->surface, 0, 0, | 834 | wl_surface_damage(output->surface, 0, 0, |
832 | output->width, output->height); | 835 | output->width, output->height); |
833 | 836 | ||
834 | uint32_t bg_alpha = ctx.background_color & 0xFF; | 837 | uint32_t bg_alpha = background_color & 0xFF; |
835 | if (bg_alpha == 0xFF) { | 838 | if (bg_alpha == 0xFF) { |
836 | struct wl_region *region = | 839 | struct wl_region *region = |
837 | wl_compositor_create_region(output->bar->compositor); | 840 | wl_compositor_create_region(output->bar->compositor); |
838 | wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); | 841 | wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); |
839 | wl_surface_set_opaque_region(output->surface, region); | 842 | wl_surface_set_opaque_region(output->surface, region); |
840 | wl_region_destroy(region); | 843 | wl_region_destroy(region); |
844 | } else { | ||
845 | wl_surface_set_opaque_region(output->surface, NULL); | ||
841 | } | 846 | } |
842 | 847 | ||
843 | struct wl_callback *frame_callback = wl_surface_frame(output->surface); | 848 | struct wl_callback *frame_callback = wl_surface_frame(output->surface); |
diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 2e9bb7f1..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> |
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 0cb5ee9d..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 | } |
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 c08406e2..573a7b16 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | 1 | ||
3 | #include <limits.h> | 2 | #include <limits.h> |
4 | #include <stdio.h> | 3 | #include <stdio.h> |
@@ -60,7 +59,7 @@ static void pretty_print_cmd(json_object *r) { | |||
60 | if (!success_object(r)) { | 59 | if (!success_object(r)) { |
61 | json_object *error; | 60 | json_object *error; |
62 | if (!json_object_object_get_ex(r, "error", &error)) { | 61 | if (!json_object_object_get_ex(r, "error", &error)) { |
63 | printf("An unknkown error occurred"); | 62 | printf("An unknown error occurred"); |
64 | } else { | 63 | } else { |
65 | printf("Error: %s\n", json_object_get_string(error)); | 64 | printf("Error: %s\n", json_object_get_string(error)); |
66 | } | 65 | } |
@@ -185,12 +184,14 @@ static void pretty_print_seat(json_object *i) { | |||
185 | } | 184 | } |
186 | 185 | ||
187 | static void pretty_print_output(json_object *o) { | 186 | static void pretty_print_output(json_object *o) { |
188 | json_object *name, *rect, *focused, *active, *ws, *current_mode; | 187 | json_object *name, *rect, *focused, *active, *power, *ws, *current_mode, *non_desktop; |
189 | json_object_object_get_ex(o, "name", &name); | 188 | json_object_object_get_ex(o, "name", &name); |
190 | json_object_object_get_ex(o, "rect", &rect); | 189 | json_object_object_get_ex(o, "rect", &rect); |
191 | json_object_object_get_ex(o, "focused", &focused); | 190 | json_object_object_get_ex(o, "focused", &focused); |
192 | 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); | ||
193 | 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); | ||
194 | json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, | 195 | json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, |
195 | *transform, *max_render_time, *adaptive_sync_status; | 196 | *transform, *max_render_time, *adaptive_sync_status; |
196 | json_object_object_get_ex(o, "make", &make); | 197 | json_object_object_get_ex(o, "make", &make); |
@@ -213,10 +214,19 @@ static void pretty_print_output(json_object *o) { | |||
213 | json_object_object_get_ex(current_mode, "height", &height); | 214 | json_object_object_get_ex(current_mode, "height", &height); |
214 | json_object_object_get_ex(current_mode, "refresh", &refresh); | 215 | json_object_object_get_ex(current_mode, "refresh", &refresh); |
215 | 216 | ||
216 | 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)) { | ||
217 | printf( | 226 | printf( |
218 | "Output %s '%s %s %s'%s\n" | 227 | "Output %s '%s %s %s'%s\n" |
219 | " Current mode: %dx%d @ %.3f Hz\n" | 228 | " Current mode: %dx%d @ %.3f Hz\n" |
229 | " Power: %s\n" | ||
220 | " Position: %d,%d\n" | 230 | " Position: %d,%d\n" |
221 | " Scale factor: %f\n" | 231 | " Scale factor: %f\n" |
222 | " Scale filter: %s\n" | 232 | " Scale filter: %s\n" |
@@ -231,6 +241,7 @@ static void pretty_print_output(json_object *o) { | |||
231 | json_object_get_int(width), | 241 | json_object_get_int(width), |
232 | json_object_get_int(height), | 242 | json_object_get_int(height), |
233 | (double)json_object_get_int(refresh) / 1000, | 243 | (double)json_object_get_int(refresh) / 1000, |
244 | json_object_get_boolean(power) ? "on" : "off", | ||
234 | json_object_get_int(x), json_object_get_int(y), | 245 | json_object_get_int(x), json_object_get_int(y), |
235 | json_object_get_double(scale), | 246 | json_object_get_double(scale), |
236 | json_object_get_string(scale_filter), | 247 | json_object_get_string(scale_filter), |
@@ -247,7 +258,7 @@ static void pretty_print_output(json_object *o) { | |||
247 | json_object_get_string(adaptive_sync_status)); | 258 | json_object_get_string(adaptive_sync_status)); |
248 | } else { | 259 | } else { |
249 | printf( | 260 | printf( |
250 | "Output %s '%s %s %s' (inactive)\n", | 261 | "Output %s '%s %s %s' (disabled)\n", |
251 | json_object_get_string(name), | 262 | json_object_get_string(name), |
252 | json_object_get_string(make), | 263 | json_object_get_string(make), |
253 | json_object_get_string(model), | 264 | json_object_get_string(model), |
@@ -262,14 +273,22 @@ static void pretty_print_output(json_object *o) { | |||
262 | for (size_t i = 0; i < modes_len; ++i) { | 273 | for (size_t i = 0; i < modes_len; ++i) { |
263 | json_object *mode = json_object_array_get_idx(modes, i); | 274 | json_object *mode = json_object_array_get_idx(modes, i); |
264 | 275 | ||
265 | json_object *mode_width, *mode_height, *mode_refresh; | 276 | json_object *mode_width, *mode_height, *mode_refresh, |
277 | *mode_picture_aspect_ratio; | ||
266 | json_object_object_get_ex(mode, "width", &mode_width); | 278 | json_object_object_get_ex(mode, "width", &mode_width); |
267 | json_object_object_get_ex(mode, "height", &mode_height); | 279 | json_object_object_get_ex(mode, "height", &mode_height); |
268 | 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); | ||
269 | 283 | ||
270 | printf(" %dx%d @ %.3f Hz\n", json_object_get_int(mode_width), | 284 | printf(" %dx%d @ %.3f Hz", json_object_get_int(mode_width), |
271 | json_object_get_int(mode_height), | 285 | json_object_get_int(mode_height), |
272 | (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"); | ||
273 | } | 292 | } |
274 | } | 293 | } |
275 | 294 | ||
diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index 24a9d6c9..abee1bb9 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd | |||
@@ -107,6 +107,8 @@ _swaymsg_ [options...] [message] | |||
107 | 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 |
108 | 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 |
109 | 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). | ||
110 | 112 | ||
111 | # RETURN CODES | 113 | # RETURN CODES |
112 | 114 | ||
diff --git a/swaynag/config.c b/swaynag/config.c index a0bf3197..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> |
@@ -166,7 +165,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
166 | fprintf(stderr, "Missing action for button %s\n", optarg); | 165 | fprintf(stderr, "Missing action for button %s\n", optarg); |
167 | return EXIT_FAILURE; | 166 | return EXIT_FAILURE; |
168 | } | 167 | } |
169 | struct swaynag_button *button = calloc(sizeof(struct swaynag_button), 1); | 168 | struct swaynag_button *button = calloc(1, sizeof(struct swaynag_button)); |
170 | if (!button) { | 169 | if (!button) { |
171 | perror("calloc"); | 170 | perror("calloc"); |
172 | return EXIT_FAILURE; | 171 | return EXIT_FAILURE; |
@@ -226,10 +225,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
226 | break; | 225 | break; |
227 | case 'f': // Font | 226 | case 'f': // Font |
228 | if (type) { | 227 | if (type) { |
229 | free(type->font); | ||
230 | pango_font_description_free(type->font_description); | 228 | pango_font_description_free(type->font_description); |
231 | type->font = strdup(optarg); | 229 | type->font_description = pango_font_description_from_string(optarg); |
232 | type->font_description = pango_font_description_from_string(type->font); | ||
233 | } | 230 | } |
234 | break; | 231 | break; |
235 | case 'l': // Detailed Message | 232 | case 'l': // Detailed Message |
@@ -245,8 +242,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
245 | break; | 242 | break; |
246 | case 'L': // Detailed Button Text | 243 | case 'L': // Detailed Button Text |
247 | if (swaynag) { | 244 | if (swaynag) { |
248 | free(swaynag->details.button_details.text); | 245 | free(swaynag->details.details_text); |
249 | swaynag->details.button_details.text = strdup(optarg); | 246 | swaynag->details.details_text = strdup(optarg); |
250 | } | 247 | } |
251 | break; | 248 | break; |
252 | case 'm': // Message | 249 | case 'm': // Message |
diff --git a/swaynag/main.c b/swaynag/main.c index 2ce37831..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" |
@@ -29,10 +28,12 @@ int main(int argc, char **argv) { | |||
29 | wl_list_init(&swaynag.outputs); | 28 | wl_list_init(&swaynag.outputs); |
30 | wl_list_init(&swaynag.seats); | 29 | wl_list_init(&swaynag.seats); |
31 | 30 | ||
32 | struct swaynag_button button_close = { 0 }; | 31 | struct swaynag_button *button_close = calloc(1, sizeof(struct swaynag_button)); |
33 | button_close.text = strdup("X"); | 32 | button_close->text = strdup("X"); |
34 | button_close.type = SWAYNAG_ACTION_DISMISS; | 33 | button_close->type = SWAYNAG_ACTION_DISMISS; |
35 | list_add(swaynag.buttons, &button_close); | 34 | list_add(swaynag.buttons, button_close); |
35 | |||
36 | swaynag.details.details_text = strdup("Toggle details"); | ||
36 | 37 | ||
37 | char *config_path = NULL; | 38 | char *config_path = NULL; |
38 | bool debug = false; | 39 | bool debug = false; |
@@ -54,8 +55,6 @@ int main(int argc, char **argv) { | |||
54 | } | 55 | } |
55 | } | 56 | } |
56 | 57 | ||
57 | swaynag.details.button_details.text = strdup("Toggle details"); | ||
58 | swaynag.details.button_details.type = SWAYNAG_ACTION_EXPAND; | ||
59 | 58 | ||
60 | if (argc > 1) { | 59 | if (argc > 1) { |
61 | struct swaynag_type *type_args = swaynag_type_new("<args>"); | 60 | struct swaynag_type *type_args = swaynag_type_new("<args>"); |
@@ -88,17 +87,20 @@ int main(int argc, char **argv) { | |||
88 | swaynag_type_merge(type, swaynag_type_get(types, "<args>")); | 87 | swaynag_type_merge(type, swaynag_type_get(types, "<args>")); |
89 | swaynag.type = type; | 88 | swaynag.type = type; |
90 | 89 | ||
91 | swaynag_types_free(types); | ||
92 | |||
93 | if (swaynag.details.message) { | 90 | if (swaynag.details.message) { |
94 | list_add(swaynag.buttons, &swaynag.details.button_details); | 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; | ||
94 | list_add(swaynag.buttons, swaynag.details.button_details); | ||
95 | } | 95 | } |
96 | 96 | ||
97 | sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output); | 97 | sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output); |
98 | sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors); | 98 | sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors); |
99 | sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name); | 99 | sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name); |
100 | sway_log(SWAY_DEBUG, "Message: %s", swaynag.message); | 100 | sway_log(SWAY_DEBUG, "Message: %s", swaynag.message); |
101 | 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); | ||
102 | sway_log(SWAY_DEBUG, "Buttons"); | 104 | sway_log(SWAY_DEBUG, "Buttons"); |
103 | for (int i = 0; i < swaynag.buttons->length; i++) { | 105 | for (int i = 0; i < swaynag.buttons->length; i++) { |
104 | struct swaynag_button *button = swaynag.buttons->items[i]; | 106 | struct swaynag_button *button = swaynag.buttons->items[i]; |
@@ -109,11 +111,9 @@ int main(int argc, char **argv) { | |||
109 | 111 | ||
110 | swaynag_setup(&swaynag); | 112 | swaynag_setup(&swaynag); |
111 | swaynag_run(&swaynag); | 113 | swaynag_run(&swaynag); |
112 | return status; | ||
113 | 114 | ||
114 | cleanup: | 115 | cleanup: |
115 | swaynag_types_free(types); | 116 | swaynag_types_free(types); |
116 | free(swaynag.details.button_details.text); | ||
117 | swaynag_destroy(&swaynag); | 117 | swaynag_destroy(&swaynag); |
118 | return status; | 118 | return status; |
119 | } | 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/swaynag.c b/swaynag/swaynag.c index 5620155d..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> |
@@ -153,8 +152,15 @@ static void update_cursor(struct swaynag_seat *seat) { | |||
153 | } | 152 | } |
154 | pointer->cursor_theme = wl_cursor_theme_load( | 153 | pointer->cursor_theme = wl_cursor_theme_load( |
155 | cursor_theme, cursor_size * swaynag->scale, swaynag->shm); | 154 | cursor_theme, cursor_size * swaynag->scale, swaynag->shm); |
156 | struct wl_cursor *cursor = | 155 | if (!pointer->cursor_theme) { |
157 | 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 | } | ||
158 | pointer->cursor_image = cursor->images[0]; | 164 | pointer->cursor_image = cursor->images[0]; |
159 | wl_surface_set_buffer_scale(pointer->cursor_surface, | 165 | wl_surface_set_buffer_scale(pointer->cursor_surface, |
160 | swaynag->scale); | 166 | swaynag->scale); |
@@ -182,11 +188,22 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | |||
182 | uint32_t serial, struct wl_surface *surface, | 188 | uint32_t serial, struct wl_surface *surface, |
183 | wl_fixed_t surface_x, wl_fixed_t surface_y) { | 189 | wl_fixed_t surface_x, wl_fixed_t surface_y) { |
184 | struct swaynag_seat *seat = data; | 190 | struct swaynag_seat *seat = data; |
191 | |||
185 | struct swaynag_pointer *pointer = &seat->pointer; | 192 | struct swaynag_pointer *pointer = &seat->pointer; |
186 | pointer->x = wl_fixed_to_int(surface_x); | 193 | pointer->x = wl_fixed_to_int(surface_x); |
187 | pointer->y = wl_fixed_to_int(surface_y); | 194 | pointer->y = wl_fixed_to_int(surface_y); |
188 | pointer->serial = serial; | 195 | |
189 | 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 | } | ||
190 | } | 207 | } |
191 | 208 | ||
192 | 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, |
@@ -378,6 +395,9 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
378 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { | 395 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { |
379 | swaynag->layer_shell = wl_registry_bind( | 396 | swaynag->layer_shell = wl_registry_bind( |
380 | registry, name, &zwlr_layer_shell_v1_interface, 1); | 397 | registry, name, &zwlr_layer_shell_v1_interface, 1); |
398 | } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { | ||
399 | swaynag->cursor_shape_manager = wl_registry_bind( | ||
400 | registry, name, &wp_cursor_shape_manager_v1_interface, 1); | ||
381 | } | 401 | } |
382 | } | 402 | } |
383 | 403 | ||
@@ -456,7 +476,9 @@ void swaynag_setup(struct swaynag *swaynag) { | |||
456 | exit(EXIT_FAILURE); | 476 | exit(EXIT_FAILURE); |
457 | } | 477 | } |
458 | 478 | ||
459 | swaynag_setup_cursors(swaynag); | 479 | if (!swaynag->cursor_shape_manager) { |
480 | swaynag_setup_cursors(swaynag); | ||
481 | } | ||
460 | 482 | ||
461 | swaynag->surface = wl_compositor_create_surface(swaynag->compositor); | 483 | swaynag->surface = wl_compositor_create_surface(swaynag->compositor); |
462 | assert(swaynag->surface); | 484 | assert(swaynag->surface); |
@@ -483,10 +505,6 @@ void swaynag_run(struct swaynag *swaynag) { | |||
483 | && wl_display_dispatch(swaynag->display) != -1) { | 505 | && wl_display_dispatch(swaynag->display) != -1) { |
484 | // This is intentionally left blank | 506 | // This is intentionally left blank |
485 | } | 507 | } |
486 | |||
487 | if (swaynag->display) { | ||
488 | wl_display_disconnect(swaynag->display); | ||
489 | } | ||
490 | } | 508 | } |
491 | 509 | ||
492 | void swaynag_destroy(struct swaynag *swaynag) { | 510 | void swaynag_destroy(struct swaynag *swaynag) { |
@@ -501,6 +519,7 @@ void swaynag_destroy(struct swaynag *swaynag) { | |||
501 | } | 519 | } |
502 | list_free(swaynag->buttons); | 520 | list_free(swaynag->buttons); |
503 | free(swaynag->details.message); | 521 | free(swaynag->details.message); |
522 | free(swaynag->details.details_text); | ||
504 | free(swaynag->details.button_up.text); | 523 | free(swaynag->details.button_up.text); |
505 | free(swaynag->details.button_down.text); | 524 | free(swaynag->details.button_down.text); |
506 | 525 | ||
@@ -541,4 +560,8 @@ void swaynag_destroy(struct swaynag *swaynag) { | |||
541 | if (swaynag->shm) { | 560 | if (swaynag->shm) { |
542 | wl_shm_destroy(swaynag->shm); | 561 | wl_shm_destroy(swaynag->shm); |
543 | } | 562 | } |
563 | |||
564 | if (swaynag->display) { | ||
565 | wl_display_disconnect(swaynag->display); | ||
566 | } | ||
544 | } | 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 | } |