diff options
273 files changed, 12607 insertions, 9388 deletions
diff --git a/.builds/alpine.yml b/.builds/alpine.yml index dc5e7c11..59df7737 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml | |||
@@ -4,22 +4,27 @@ 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 |
10 | - libseat-dev | ||
9 | - libxcb-dev | 11 | - libxcb-dev |
10 | - libxkbcommon-dev | 12 | - libxkbcommon-dev |
11 | - mesa-dev | 13 | - mesa-dev |
12 | - meson | 14 | - meson |
13 | - pango-dev | 15 | - pango-dev |
16 | - pcre2-dev | ||
14 | - pixman-dev | 17 | - pixman-dev |
15 | - scdoc | 18 | - scdoc |
16 | - wayland-dev | 19 | - wayland-dev |
17 | - wayland-protocols | 20 | - wayland-protocols |
18 | - xcb-util-image-dev | 21 | - xcb-util-image-dev |
19 | - xorg-server-xwayland | 22 | - xcb-util-wm-dev |
23 | - xwayland-dev | ||
24 | - hwdata-dev | ||
20 | sources: | 25 | sources: |
21 | - https://github.com/swaywm/sway | 26 | - https://github.com/swaywm/sway |
22 | - https://github.com/swaywm/wlroots | 27 | - https://gitlab.freedesktop.org/wlroots/wlroots.git |
23 | tasks: | 28 | tasks: |
24 | - wlroots: | | 29 | - wlroots: | |
25 | cd wlroots | 30 | cd wlroots |
@@ -28,7 +33,7 @@ tasks: | |||
28 | sudo ninja -C build install | 33 | sudo ninja -C build install |
29 | - setup: | | 34 | - setup: | |
30 | cd sway | 35 | cd sway |
31 | meson build -Dauto_features=enabled -Dtray=disabled | 36 | meson build --fatal-meson-warnings -Dauto_features=enabled -Dtray=disabled |
32 | - build: | | 37 | - build: | |
33 | cd sway | 38 | cd sway |
34 | ninja -C build | 39 | ninja -C build |
@@ -36,3 +41,10 @@ tasks: | |||
36 | cd sway | 41 | cd sway |
37 | meson configure build -Dxwayland=disabled | 42 | meson configure build -Dxwayland=disabled |
38 | ninja -C build | 43 | ninja -C build |
44 | - build-static: | | ||
45 | cd sway | ||
46 | mkdir subprojects | ||
47 | ln -s ../../wlroots subprojects/wlroots | ||
48 | rm -rf build | ||
49 | meson build --fatal-meson-warnings --default-library=static --force-fallback-for=wlroots | ||
50 | ninja -C build | ||
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index c0f70186..9972c01a 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml | |||
@@ -3,20 +3,25 @@ packages: | |||
3 | - cairo | 3 | - cairo |
4 | - gdk-pixbuf2 | 4 | - gdk-pixbuf2 |
5 | - json-c | 5 | - json-c |
6 | - libdisplay-info | ||
6 | - libegl | 7 | - libegl |
7 | - libinput | 8 | - libinput |
8 | - libxcb | 9 | - libxcb |
9 | - libxkbcommon | 10 | - libxkbcommon |
10 | - meson | 11 | - meson |
11 | - pango | 12 | - pango |
13 | - pcre2 | ||
12 | - scdoc | 14 | - scdoc |
13 | - wayland | 15 | - wayland |
14 | - wayland-protocols | 16 | - wayland-protocols |
15 | - xcb-util-image | 17 | - xcb-util-image |
18 | - xcb-util-wm | ||
16 | - xorg-xwayland | 19 | - xorg-xwayland |
20 | - seatd | ||
21 | - hwdata | ||
17 | sources: | 22 | sources: |
18 | - https://github.com/swaywm/sway | 23 | - https://github.com/swaywm/sway |
19 | - https://github.com/swaywm/wlroots | 24 | - https://gitlab.freedesktop.org/wlroots/wlroots.git |
20 | tasks: | 25 | tasks: |
21 | - wlroots: | | 26 | - wlroots: | |
22 | cd wlroots | 27 | cd wlroots |
@@ -25,7 +30,7 @@ tasks: | |||
25 | sudo ninja -C build install | 30 | sudo ninja -C build install |
26 | - setup: | | 31 | - setup: | |
27 | cd sway | 32 | cd sway |
28 | meson build -Dauto_features=enabled -Dsd-bus-provider=libsystemd | 33 | meson build --fatal-meson-warnings -Dauto_features=enabled -Dsd-bus-provider=libsystemd |
29 | - build: | | 34 | - build: | |
30 | cd sway | 35 | cd sway |
31 | ninja -C build | 36 | ninja -C build |
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 273badbc..29c6312a 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml | |||
@@ -4,6 +4,7 @@ packages: | |||
4 | - devel/json-c | 4 | - devel/json-c |
5 | - devel/libevdev | 5 | - devel/libevdev |
6 | - devel/meson | 6 | - devel/meson |
7 | - devel/pcre2 | ||
7 | - devel/pkgconf | 8 | - devel/pkgconf |
8 | - graphics/cairo | 9 | - graphics/cairo |
9 | - graphics/gdk-pixbuf2 | 10 | - graphics/gdk-pixbuf2 |
@@ -19,13 +20,17 @@ packages: | |||
19 | - devel/libudev-devd | 20 | - devel/libudev-devd |
20 | - graphics/libdrm | 21 | - graphics/libdrm |
21 | - graphics/mesa-libs | 22 | - graphics/mesa-libs |
23 | - sysutils/libdisplay-info | ||
24 | - sysutils/seatd | ||
22 | - x11/libinput | 25 | - x11/libinput |
23 | - x11/libX11 | 26 | - x11/libX11 |
24 | - x11/pixman | 27 | - x11/pixman |
25 | - x11/xcb-util-wm | 28 | - x11/xcb-util-wm |
29 | - x11-servers/xwayland-devel | ||
30 | - misc/hwdata | ||
26 | sources: | 31 | sources: |
27 | - https://github.com/swaywm/sway | 32 | - https://github.com/swaywm/sway |
28 | - https://github.com/swaywm/wlroots | 33 | - https://gitlab.freedesktop.org/wlroots/wlroots.git |
29 | tasks: | 34 | tasks: |
30 | - setup: | | 35 | - setup: | |
31 | cd sway | 36 | cd sway |
@@ -33,7 +38,7 @@ tasks: | |||
33 | cd subprojects | 38 | cd subprojects |
34 | ln -s ../../wlroots wlroots | 39 | ln -s ../../wlroots wlroots |
35 | cd .. | 40 | cd .. |
36 | meson build -Dtray=enabled -Dsd-bus-provider=basu | 41 | meson build --fatal-meson-warnings -Dtray=enabled -Dsd-bus-provider=basu |
37 | - build: | | 42 | - build: | |
38 | cd sway | 43 | cd sway |
39 | ninja -C build | 44 | ninja -C build |
diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 5818da3c..00000000 --- a/.clang-format +++ /dev/null | |||
@@ -1,16 +0,0 @@ | |||
1 | BasedOnStyle: LLVM | ||
2 | IndentWidth: 4 | ||
3 | TabWidth: 4 | ||
4 | UseTab: Always | ||
5 | BreakBeforeBraces: Attach | ||
6 | AllowShortIfStatementsOnASingleLine: false | ||
7 | IndentCaseLabels: false | ||
8 | SortIncludes: false | ||
9 | ColumnLimit: 80 | ||
10 | AlignAfterOpenBracket: DontAlign | ||
11 | BinPackParameters: false | ||
12 | BinPackArguments: false | ||
13 | ContinuationIndentWidth: 8 | ||
14 | AllowAllParametersOfDeclarationOnNextLine: false | ||
15 | AllowShortLoopsOnASingleLine: true | ||
16 | ReflowComments: false | ||
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 156accde..eba606e6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md | |||
@@ -6,10 +6,9 @@ labels: 'bug' | |||
6 | --- | 6 | --- |
7 | 7 | ||
8 | ### Please read the following before submitting: | 8 | ### Please read the following before submitting: |
9 | - Please do NOT submit bug reports for questions. Ask questions on IRC at #sway on irc.freenode.net. | 9 | - Please do NOT submit bug reports for questions. Ask questions on IRC at #sway on Libera Chat. |
10 | - Proprietary graphics drivers, including nvidia, are not supported. Please use the open source equivalents, such as nouveau, if you would like to use Sway. | 10 | - Proprietary graphics drivers, including nvidia, are not supported. Please use the open source equivalents, such as nouveau, if you would like to use Sway. |
11 | - Problems with the Wayland version of Firefox are likely to be Firefox bugs. Start by submitting your issue to the Firefox Bugzilla and come back here only after they confirm otherwise. | 11 | - Please do NOT submit issues for information from the github wiki. The github wiki is community maintained and therefore may contain outdated information, scripts that don't work or obsolete workarounds. |
12 | - Please do NOT submit issues for information from the github wiki. The github wiki is community maintained and therefore may contain outdated information, scripts that don't work or osbolete workarounds. | ||
13 | If you fix a script or find outdated information, don't hesitate to adjust the wiki page. | 12 | If you fix a script or find outdated information, don't hesitate to adjust the wiki page. |
14 | 13 | ||
15 | ### Please fill out the following: | 14 | ### Please fill out the following: |
@@ -19,6 +18,7 @@ labels: 'bug' | |||
19 | - **Debug Log:** | 18 | - **Debug Log:** |
20 | - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com. | 19 | - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com. |
21 | - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway. | 20 | - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway. |
21 | - Attach the **full** file, do not truncate it. | ||
22 | 22 | ||
23 | - **Configuration File:** | 23 | - **Configuration File:** |
24 | - Please try to produce with the default configuration. | 24 | - Please try to produce with the default configuration. |
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index f09cdf5b..0092609b 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml | |||
@@ -1,5 +1,5 @@ | |||
1 | blank_issues_enabled: false | 1 | blank_issues_enabled: false |
2 | contact_links: | 2 | contact_links: |
3 | - name: Questions | 3 | - name: Questions |
4 | url: "http://webchat.freenode.net/?channels=sway&uio=d4" | 4 | url: "https://libera.chat" |
5 | about: "Please ask questions on IRC in #sway on irc.freenode.net" | 5 | about: "Please ask questions on IRC in #sway on Libera Chat" |
@@ -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/CONTRIBUTING.md b/CONTRIBUTING.md index c17afdda..4f043f7a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md | |||
@@ -1,9 +1,8 @@ | |||
1 | # Contributing to sway | 1 | # Contributing to sway |
2 | 2 | ||
3 | Contributing just involves sending a pull request. You will probably be more | 3 | Contributing just involves sending a pull request. You will probably be more |
4 | successful with your contribution if you visit | 4 | successful with your contribution if you visit #sway-devel on Libera Chat |
5 | [#sway-devel](https://webchat.freenode.net/?channels=sway-devel) on | 5 | upfront and discuss your plans. |
6 | irc.freenode.net upfront and discuss your plans. | ||
7 | 6 | ||
8 | Note: rules are made to be broken. Adjust or ignore any/all of these as you see | 7 | Note: rules are made to be broken. Adjust or ignore any/all of these as you see |
9 | fit, but be prepared to justify it to your peers. | 8 | fit, but be prepared to justify it to your peers. |
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 24e66a60..68b411d9 100644 --- a/README.de.md +++ b/README.de.md | |||
@@ -1,30 +1,30 @@ | |||
1 | # Sway | 1 | # Sway |
2 | Sway ist ein [i3](https://i3wm.org/)-kompatibler [Wayland](http://wayland.freedesktop.org/)-Compositor. Lies die [FAQ](https://github.com/swaywm/sway/wiki). Tritt dem [IRC Channel](http://webchat.freenode.net/?channels=sway&uio=d4) bei (#sway on irc.freenode.net; 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 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) signiert und auf GitHub veröffentlicht. | 5 | Jedes Release wird mit dem PGP-Schlüssel [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) signiert und [auf GitHub](https://github.com/swaywm/sway/releases) veröffentlicht. |
6 | 6 | ||
7 | ## Installation | 7 | ## Installation |
8 | ### Mit der Paketverwaltung | ||
9 | Sway kann in vielen Distributionen direkt durch die Paketverwaltung installiert werden. Das Paket sollte "sway" heißen. Falls es kein solches Paket gibt, kannst du im [Wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) (englisch) nach mehr Informationen bezüglich deiner Distribution suchen. | ||
10 | 8 | ||
11 | Falls du sway für deine eigene Distribution als Paket bereitstellen möchtest, solltest du die Entwickler per IRC oder E-Mail (sir@cmpwn.com) kontaktieren. | 9 | ### Über die Paketverwaltung |
10 | |||
11 | Sway kann in vielen Distributionen direkt durch die Paketverwaltung installiert werden. Versuche einfach das Packet "sway" zu installieren. | ||
12 | 12 | ||
13 | ### Quellcode selbst kompilieren | 13 | ### Quellcode selbst kompilieren |
14 | 14 | ||
15 | sway benötigt die folgenden Pakete: | 15 | sway benötigt die folgenden Pakete: |
16 | 16 | ||
17 | * meson\* | 17 | * meson\* |
18 | * [wlroots](https://github.com/swaywm/wlroots) | 18 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
19 | * wayland | 19 | * wayland |
20 | * wayland-protocols\* | 20 | * wayland-protocols\* |
21 | * pcre | 21 | * pcre2 |
22 | * json-c | 22 | * json-c |
23 | * pango | 23 | * pango |
24 | * cairo | 24 | * cairo |
25 | * gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt) | 25 | * gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt) |
26 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc)\* (Optional, wird für die Dokumentation (Man Pages) benötigt) | 26 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (Optional, wird für die Dokumentation (Man Pages) benötigt)\* |
27 | * git\* | 27 | * git (Optional: Versionsinfo)\* |
28 | 28 | ||
29 | _\*Werden nur während des Kompilierens benötigt_ | 29 | _\*Werden nur während des Kompilierens benötigt_ |
30 | 30 | ||
@@ -34,12 +34,6 @@ Führe die folgenden Befehle aus: | |||
34 | ninja -C build | 34 | ninja -C build |
35 | sudo ninja -C build install | 35 | sudo ninja -C build install |
36 | 36 | ||
37 | Falls dein System nicht logind benutzt, musst du sway noch die passenden Berechtigungen geben: | ||
38 | |||
39 | sudo chmod a+s /usr/local/bin/sway | ||
40 | |||
41 | Sway läuft nur in der Startphase mit Root-Rechten. | ||
42 | |||
43 | ## Konfiguration | 37 | ## Konfiguration |
44 | 38 | ||
45 | Falls du von i3 migrierst, kannst du deine Konfigurationsdatei nach `~/.config/sway/config` kopieren und die Einstellungen sollten ohne Weiteres funktionieren. Ansonsten kannst du die Beispielkonfiguration, die normalerweise in `/etc/sway/config` liegt, nach `~/.config/sway/config` kopieren. Die Dokumentation zur Konfigurationsdatei findest du in `man 5 sway`. | 39 | Falls du von i3 migrierst, kannst du deine Konfigurationsdatei nach `~/.config/sway/config` kopieren und die Einstellungen sollten ohne Weiteres funktionieren. Ansonsten kannst du die Beispielkonfiguration, die normalerweise in `/etc/sway/config` liegt, nach `~/.config/sway/config` kopieren. Die Dokumentation zur Konfigurationsdatei findest du in `man 5 sway`. |
diff --git a/README.dk.md b/README.dk.md index 535000c3..5ce94cde 100644 --- a/README.dk.md +++ b/README.dk.md | |||
@@ -1,41 +1,43 @@ | |||
1 | # Sway | 1 | # Sway |
2 | 2 | ||
3 | Sway er en [i3](https://i3wm.org/)-kompatibel [Wayland](http://wayland.freedesktop.org/) compositor. | 3 | Sway er en [i3]-kompatibel [Wayland] compositor. Læs [Ofte stillede spørgsmål]. |
4 | Læs [Ofte stillede spørgsmål](https://github.com/swaywm/sway/wiki). | 4 | Deltag på [IRC kanalen][IRC kanal] \(#sway på irc.libera.chat). |
5 | Deltag på [IRC kanalen](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway på irc.freenode.net). | ||
6 | 5 | ||
7 | ## Udgivelses Signaturer | 6 | ## Udgivelses Signaturer |
8 | 7 | ||
9 | Udgivelser er signeret med [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) | 8 | Udgivelser er signeret med [E88F5E48] og publiceret [på GitHub][GitHub |
10 | og publiseret på [GitHub](https://github.com/swaywm/sway/releases). | 9 | releases]. |
11 | 10 | ||
12 | ## Installation | 11 | ## Installation |
13 | 12 | ||
14 | ### Fra Pakker | 13 | ### Fra pakker |
15 | 14 | ||
16 | Sway er tilgængelig i mange distributioner. Prøv at installere pakken "svay". Hvis den ikke er tilgængelig, så tjek [denne wiki-side](https://github.com/swaywm/sway/wiki/Unsupported-packages) | 15 | Sway er tilgængelig i mange distributioner. Prøv at installere "sway" pakken |
17 | for information om installation til din(e) distribution(er). | 16 | fra din. |
18 | 17 | ||
19 | Hvis du er interesseret i at lave en Sway pakke til din distribution, burde du besøge IRC | 18 | Hvis du er interesseret i at pakke Sway til din distribution, kan du tage forbi |
20 | kanalen eller sende en e-mail til sir@cmpwn.com for rådgivning. | 19 | IRC kanalen eller sende en email til sir@cmpwn.com for rådgivning. |
21 | 20 | ||
22 | ### Kompilering fra kildekode | 21 | ### Kompilering fra kildekode |
23 | 22 | ||
24 | Installation afhænger af følgende programmer: | 23 | Se [denne wiki-side][Opsætning til udvikling] hvis du vil bygge HEAD af sway og |
24 | wlroots til test eller udvikling. | ||
25 | |||
26 | Installationsafhængigheder: | ||
25 | 27 | ||
26 | * meson \* | 28 | * meson \* |
27 | * [wlroots](https://github.com/swaywm/wlroots) | 29 | * [wlroots] |
28 | * wayland | 30 | * wayland |
29 | * wayland-protocols \* | 31 | * wayland-protocols \* |
30 | * pcre | 32 | * pcre2 |
31 | * json-c | 33 | * json-c |
32 | * pango | 34 | * pango |
33 | * cairo | 35 | * cairo |
34 | * gdk-pixbuf2 (valgfrit tillæg: system tray) | 36 | * gdk-pixbuf2 (valgfrit: system tray) |
35 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (valgfrit tillæg: man pages) \* | 37 | * [scdoc] (valgfrit: man pages) \* |
36 | * git \* | 38 | * git \* |
37 | 39 | ||
38 | _\*Kompiler krav_ | 40 | _\*Kompileringsafhængighed_ |
39 | 41 | ||
40 | Kør følgende kommandoer: | 42 | Kør følgende kommandoer: |
41 | 43 | ||
@@ -43,21 +45,24 @@ Kør følgende kommandoer: | |||
43 | ninja -C build | 45 | ninja -C build |
44 | sudo ninja -C build install | 46 | sudo ninja -C build install |
45 | 47 | ||
46 | På systemer uden 'logind', behøver du at sætte ejerens bruger-id for Sways eksekverbare filer - såkaldt SUID (Set owner User ID): | ||
47 | |||
48 | sudo chmod a+s /usr/local/bin/sway | ||
49 | |||
50 | Sway vil frasige sig 'root' tilladelser kort efter opstart | ||
51 | |||
52 | ## Konfiguration | 48 | ## Konfiguration |
53 | 49 | ||
54 | Hvis du allerede bruger i3, bør du kopiere din i3-konfiguration til `~/.config/sway/config` og | 50 | Hvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til |
55 | det vil bare fungerer. Ellers skal du kopiere eksempel konfigurations filen til | 51 | `~/.config/sway/config`. Ellers skal du kopiere eksempelkonfigurationsfilen til |
56 | `~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`. | 52 | `~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`. Kør |
57 | Kør `man 5 sway` for at få oplysninger om konfigurationen. | 53 | `man 5 sway` for at få oplysninger om konfigurationen. |
58 | 54 | ||
59 | ## Kører | 55 | ## Eksekvering |
60 | 56 | ||
61 | Kør `sway` fra en TTY. Nogle display managers fungerer muligvis, men understøttes ikke af | 57 | Kør `sway` fra en TTY. Nogle display managers kan fungere, men Sway yder ikke |
62 | Sway (gdm er kendt for at fungere temmelig godt). | 58 | support til dem (gdm er kendt for at fungere temmelig godt). |
63 | 59 | ||
60 | [i3]: https://i3wm.org/ | ||
61 | [Wayland]: http://wayland.freedesktop.org/ | ||
62 | [Ofte stillede spørgsmål]: https://github.com/swaywm/sway/wiki | ||
63 | [IRC kanal]: https://web.libera.chat/gamja/?channels=#sway | ||
64 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
65 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
66 | [Opsætning til udvikling]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
67 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
68 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.es.md b/README.es.md index 53f11f68..1f1657df 100644 --- a/README.es.md +++ b/README.es.md | |||
@@ -1,12 +1,12 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con [i3](https://i3wm.org/). | 3 | sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con [i3](https://i3wm.org/). |
4 | Lea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on | 4 | Lea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway on |
5 | irc.freenode.net). | 5 | irc.libera.chat). |
6 | 6 | ||
7 | ## Firmas de las versiones | 7 | ## Firmas de las versiones |
8 | 8 | ||
9 | Las distintas versiones están firmadas con [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) | 9 | Las distintas versiones están firmadas con [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) |
10 | y publicadas [en GitHub](https://github.com/swaywm/sway/releases). | 10 | y publicadas [en GitHub](https://github.com/swaywm/sway/releases). |
11 | 11 | ||
12 | ## Instalación | 12 | ## Instalación |
@@ -25,10 +25,10 @@ escriba un email a sir@cmpwn.com | |||
25 | Instale las dependencias: | 25 | Instale las dependencias: |
26 | 26 | ||
27 | * meson \* | 27 | * meson \* |
28 | * [wlroots](https://github.com/swaywm/wlroots) | 28 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
29 | * wayland | 29 | * wayland |
30 | * wayland-protocols \* | 30 | * wayland-protocols \* |
31 | * pcre | 31 | * pcre2 |
32 | * json-c | 32 | * json-c |
33 | * pango | 33 | * pango |
34 | * cairo | 34 | * cairo |
@@ -44,12 +44,6 @@ Desde su consola, ejecute las órdenes: | |||
44 | ninja -C build | 44 | ninja -C build |
45 | sudo ninja -C build install | 45 | sudo ninja -C build install |
46 | 46 | ||
47 | En sistemas sin `logind`, necesitará cambiar los permisos del archivo compilado de sway: | ||
48 | |||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | |||
51 | Sway abandonará los permisos de super-usuario al poco de arrancar. | ||
52 | |||
53 | ## Configuración | 47 | ## Configuración |
54 | 48 | ||
55 | Si ya utiliza i3, copie su archivo de configuración de i3 a `~/.config/sway/config` y | 49 | Si ya utiliza i3, copie su archivo de configuración de i3 a `~/.config/sway/config` y |
diff --git a/README.fr.md b/README.fr.md index a72696d6..7752fc70 100644 --- a/README.fr.md +++ b/README.fr.md | |||
@@ -1,48 +1,49 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | Sway est un compositeur [Wayland](http://wayland.freedesktop.org/) compatible | 3 | Sway est un compositeur [Wayland] compatible avec [i3]. Lisez la |
4 | avec [i3](https://i3wm.org/), **en cours de développement**. Lisez la | 4 | [FAQ]. Rejoignez le [canal IRC] (#sway sur irc.libera.chat). |
5 | [FAQ](https://github.com/swaywm/sway/wiki). Rejoignez le [canal | ||
6 | IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway sur | ||
7 | irc.freenode.net). | ||
8 | 5 | ||
9 | ## Aide en français | 6 | ## Aide en français |
10 | 7 | ||
11 | [abdelq](//github.com/abdelq) fournit du support en français sur IRC et Github, dans le fuseau horaire UTC-4 (EST). | 8 | [abdelq] fournit du support en français sur IRC et Github, dans le fuseau |
9 | horaire UTC-4 (EST). | ||
12 | 10 | ||
13 | ## Signatures de nouvelles versions | 11 | ## Signatures de nouvelles versions |
14 | 12 | ||
15 | Les nouvelles versions sont signées avec [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) | 13 | Les nouvelles versions sont signées avec [E88F5E48] et publiées |
16 | et publiées [sur GitHub](https://github.com/swaywm/sway/releases). | 14 | [sur GitHub][versions GitHub]. |
17 | 15 | ||
18 | ## Installation | 16 | ## Installation |
19 | 17 | ||
20 | ### À partir de paquets | 18 | ### À partir de paquets |
21 | 19 | ||
22 | Sway est disponible sur plusieurs distributions. Essayez d'installer le paquet "sway" pour | 20 | Sway est disponible sur beaucoup de distributions. Essayez d'installer le |
23 | la vôtre. Si ce n'est pas disponible, consultez [cette page wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) | 21 | paquet "sway" pour la vôtre. |
24 | pour de l'information sur l'installation pour vos distributions. | ||
25 | 22 | ||
26 | Si vous êtes intéressé à maintenir Sway pour votre distribution, passez par le canal | 23 | Si vous êtes intéressé à maintenir Sway pour votre distribution, passez sur le |
27 | IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des conseils. | 24 | canal IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des |
25 | conseils. | ||
28 | 26 | ||
29 | ### Compilation depuis la source | 27 | ### Compilation depuis les sources |
28 | |||
29 | Consultez [cette page wiki][Configuration de développement] si vous souhaitez | ||
30 | compiler la révision HEAD de sway et wlroots pour tester ou développer. | ||
30 | 31 | ||
31 | Installez les dépendances : | 32 | Installez les dépendances : |
32 | 33 | ||
33 | * meson \* | 34 | * meson \* |
34 | * [wlroots](https://github.com/swaywm/wlroots) | 35 | * [wlroots] |
35 | * wayland | 36 | * wayland |
36 | * wayland-protocols \* | 37 | * wayland-protocols \* |
37 | * pcre | 38 | * pcre2 |
38 | * json-c | 39 | * json-c |
39 | * pango | 40 | * pango |
40 | * cairo | 41 | * cairo |
41 | * gdk-pixbuf2 (optionnel: system tray) | 42 | * gdk-pixbuf2 (optionnel : system tray) |
42 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optionnel: requis pour les pages man) \* | 43 | * [scdoc] (optionnel : requis pour les pages man) \* |
43 | * git \* | 44 | * git (optionnel : information de version) \* |
44 | 45 | ||
45 | _\*Requis uniquement pour la compilation_ | 46 | _\* Requis uniquement pour la compilation_ |
46 | 47 | ||
47 | Exécutez ces commandes : | 48 | Exécutez ces commandes : |
48 | 49 | ||
@@ -50,20 +51,27 @@ Exécutez ces commandes : | |||
50 | ninja -C build | 51 | ninja -C build |
51 | sudo ninja -C build install | 52 | sudo ninja -C build install |
52 | 53 | ||
53 | Sur les systèmes sans logind, vous devez suid le binaire de sway : | ||
54 | |||
55 | sudo chmod a+s /usr/local/bin/sway | ||
56 | |||
57 | Sway se débarassera des permissions *root* peu de temps après le démarrage. | ||
58 | |||
59 | ## Configuration | 54 | ## Configuration |
60 | 55 | ||
61 | Si vous utilisez déjà i3, copiez votre configuration i3 à `~/.config/sway/config` et | 56 | Si vous utilisez déjà i3, copiez votre configuration i3 vers |
62 | cela va fonctionner. Sinon, copiez l'exemple de fichier de configuration à | 57 | `~/.config/sway/config` et sway fonctionnera directement. Sinon, copiez |
63 | `~/.config/sway/config`. Il se trouve généralement dans `/etc/sway/config`. | 58 | l'exemple de fichier de configuration vers `~/.config/sway/config`. Il se |
64 | Exécutez `man 5 sway` pour l'information sur la configuration. | 59 | trouve généralement dans `/etc/sway/config`. Exécutez `man 5 sway` pour lire la |
60 | documentation pour la configuration de sway. | ||
65 | 61 | ||
66 | ## Exécution | 62 | ## Exécution |
67 | 63 | ||
68 | Exécutez `sway` à partir d'un TTY. Certains gestionnaires d'affichage peuvent fonctionner, | 64 | Exécutez `sway` à partir d'un TTY. Certains gestionnaires d'affichage peuvent |
69 | mais ne sont pas supportés par Sway (gdm est réputé pour assez bien fonctionner). | 65 | fonctionner, mais ne sont pas supportés par Sway (gdm est réputé pour assez |
66 | bien fonctionner). | ||
67 | |||
68 | [Wayland]: http://wayland.freedesktop.org/ | ||
69 | [i3]: https://i3wm.org/ | ||
70 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
71 | [canal IRC]: https://web.libera.chat/gamja/?channels=#sway | ||
72 | [abdelq]: https://github.com/abdelq | ||
73 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
74 | [versions GitHub]: https://github.com/swaywm/sway/releases | ||
75 | [Configuration de développement]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
76 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
77 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.ge.md b/README.ge.md new file mode 100644 index 00000000..bb8b9a34 --- /dev/null +++ b/README.ge.md | |||
@@ -0,0 +1,61 @@ | |||
1 | # sway | ||
2 | |||
3 | sway áƒáƒ ის [i3]-თáƒáƒ•áƒ¡áƒ”ბáƒáƒ“ი [Wayland]-ის კáƒáƒ›áƒžáƒáƒ–იტáƒáƒ ი. მეტი ინფáƒáƒ მáƒáƒªáƒ˜áƒ˜áƒ¡áƒ—ვის იხილეთ | ||
4 | [FAQ]. დáƒáƒ£áƒ™áƒáƒ•áƒ¨áƒ˜áƒ დით [IRC áƒáƒ ხს][IRC channel] \(#sway irc.libera.chat-ზე). | ||
5 | |||
6 | ## გáƒáƒ›áƒáƒ¨áƒ•áƒ”ბის ხელმáƒáƒ¬áƒ”რები | ||
7 | |||
8 | გáƒáƒ›áƒáƒ¨áƒ•áƒ”ბები ხელმáƒáƒ¬áƒ”რილირ[E88F5E48]-ით დრგáƒáƒ›áƒáƒ¥áƒ•áƒ”ყნებულირ[GitHub-ზე][GitHub releases]. | ||
9 | |||
10 | ## ინსტáƒáƒšáƒáƒªáƒ˜áƒ | ||
11 | |||
12 | ### რეპáƒáƒ–იტáƒáƒ იიდáƒáƒœ | ||
13 | |||
14 | Sway áƒáƒ ის ხელმისáƒáƒ¬áƒ•áƒ“áƒáƒ›áƒ˜ ბევრი დისტრიბუტáƒáƒªáƒ˜áƒ˜áƒ¡áƒ—ვის. ცáƒáƒ“ეთ "sway" პáƒáƒ™áƒ”ტის ინსტáƒáƒšáƒáƒªáƒ˜áƒ თქვენთვის. | ||
15 | |||
16 | ### კáƒáƒ“ის კáƒáƒ›áƒžáƒ˜áƒšáƒáƒªáƒ˜áƒ | ||
17 | |||
18 | იხილეთ [ეს ვიკი გვერდი][Development setup] თუ გინდáƒáƒ— რáƒáƒ› áƒáƒáƒ¬áƒ§áƒáƒ— sway დრwlroots სáƒáƒ¢áƒ”სტáƒáƒ“ áƒáƒœ დეველáƒáƒžáƒ›áƒ”ნტისთვის. | ||
19 | |||
20 | დáƒáƒáƒ˜áƒœáƒ¡áƒ¢áƒáƒšáƒ˜áƒ ეთ დáƒáƒ›áƒáƒ™áƒ˜áƒ“ებულებები: | ||
21 | |||
22 | * meson \* | ||
23 | * [wlroots] | ||
24 | * wayland | ||
25 | * wayland-protocols \* | ||
26 | * pcre2 | ||
27 | * json-c | ||
28 | * pango | ||
29 | * cairo | ||
30 | * gdk-pixbuf2 (áƒáƒ¡áƒ”ვე áƒáƒ ჩევითიáƒ: system tray) | ||
31 | * [scdoc] (áƒáƒ¡áƒ”ვე áƒáƒ ჩევითიáƒ: man pages) \* | ||
32 | * git (áƒáƒ¡áƒ”ვე áƒáƒ ჩევითიáƒ: version info) \* | ||
33 | |||
34 | _\* Compile-time dep_ | ||
35 | |||
36 | გáƒáƒ£áƒ¨áƒ•áƒ˜áƒ— ეს ბრძáƒáƒœáƒ”ბები: | ||
37 | |||
38 | meson build/ | ||
39 | ninja -C build/ | ||
40 | sudo ninja -C build/ install | ||
41 | |||
42 | ## კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ | ||
43 | |||
44 | თუ უკვე იყენებთ i3-ს, მáƒáƒ¨áƒ˜áƒœ დáƒáƒáƒ™áƒáƒžáƒ˜áƒ ე i3 კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ დრჩáƒáƒ¡áƒ•áƒ˜ `~/.config/sway/config` | ||
45 | დრუპრáƒáƒ‘ლემáƒáƒ“ იმუშáƒáƒ•áƒ”ბს პირდáƒáƒžáƒ˜áƒ . წინáƒáƒáƒ¦áƒ›áƒ“ეგ შემთხვევáƒáƒ¨áƒ˜ კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ˜áƒ¡ ნიმუში ჩáƒáƒáƒ™áƒáƒžáƒ˜áƒ ეთ áƒáƒ¥: `~/.config/sway/config`. კáƒáƒ›áƒžáƒ˜áƒ’ურáƒáƒªáƒ˜áƒ˜áƒ¡ ნიმუში ხშირშემთხვევáƒáƒ¨áƒ˜ áƒáƒ ის `/etc/sway/config`. | ||
46 | გáƒáƒ£áƒ¨áƒ•áƒ˜ `man 5 sway` კáƒáƒœáƒžáƒ˜áƒ’ურáƒáƒªáƒ˜áƒáƒ–ე ინფáƒáƒ მáƒáƒªáƒ˜áƒ˜áƒ¡ მისáƒáƒ¦áƒ”ბáƒáƒ“. | ||
47 | |||
48 | ## გáƒáƒ¨áƒ•áƒ”ბრ| ||
49 | |||
50 | გáƒáƒ£áƒ¨áƒ•áƒ˜ `sway` TTY-ისთვის. ზáƒáƒ’იერთმრლáƒáƒ’ინ მენეჯერმრშეიძლებრიმუშáƒáƒ•áƒáƒ¡, მáƒáƒ’რáƒáƒ› áƒáƒ | ||
51 | áƒáƒ ის მხáƒáƒ დáƒáƒáƒ”რილი sway-სგáƒáƒœ (რáƒáƒ’áƒáƒ ც წესი კáƒáƒ გáƒáƒ“ მუშáƒáƒáƒ‘ს gdm). | ||
52 | |||
53 | [i3]: https://i3wm.org/ | ||
54 | [Wayland]: http://wayland.freedesktop.org/ | ||
55 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
56 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway | ||
57 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
58 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
59 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
60 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
61 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.gr.md b/README.gr.md new file mode 100644 index 00000000..d697f78e --- /dev/null +++ b/README.gr.md | |||
@@ -0,0 +1,67 @@ | |||
1 | # Sway | ||
2 | |||
3 | Το sway Îνα [i3]-συμβατό [Wayland] compositor. Διαβάστε το [FAQ]. Μπείτε στο | ||
4 | [IRC channel] \(#sway on irc.libera.chat). | ||
5 | |||
6 | ## ΥπογÏαφÎÏ‚ δημοσιεÏσεων | ||
7 | |||
8 | Οι εκδόσεις είναι υπογεÏαμμÎνες με [E88F5E48] και δημοσιευμÎνες [στο GitHub][GitHub releases]. | ||
9 | |||
10 | ## Εγκατάσταση | ||
11 | |||
12 | ### Από πακÎτα | ||
13 | |||
14 | Το Sway είναι διαθÎσιμο σε πολλά distributions. Δοκιμάστε εγκαταστώντας το "sway" package για | ||
15 | το δικό σας. | ||
16 | |||
17 | Εάν ενδιαφÎÏεστε για packaging του sway για το distribution σας, να πάτε στο IRC | ||
18 | channel ή στείλτε Îνα email στο sir@cmpwn.com για συμβουλÎÏ‚. | ||
19 | |||
20 | ### Compiling από πηγή | ||
21 | |||
22 | ΤσεκάÏετε [αυτό το wiki page][Development setup] εάμα θÎλετε να κάνετε build το HEAD του | ||
23 | sway και wlroots γιά τεστάÏισμα ή development. | ||
24 | |||
25 | Εγκατάσταση των dependencies: | ||
26 | |||
27 | * meson \* | ||
28 | * [wlroots] | ||
29 | * wayland | ||
30 | * wayland-protocols \* | ||
31 | * pcre2 | ||
32 | * json-c | ||
33 | * pango | ||
34 | * cairo | ||
35 | * gdk-pixbuf2 (Ï€ÏοαιÏετικό: system tray) | ||
36 | * [scdoc] (Ï€ÏοαιÏετικό: man pages) \* | ||
37 | * git (Ï€ÏοαιÏετικό: πληÏοφοÏίες εκδώσεων) \* | ||
38 | |||
39 | _\*Compile-time dep_ | ||
40 | |||
41 | ΤÏÎξτε αυτά τα commands: | ||
42 | |||
43 | meson build/ | ||
44 | ninja -C build/ | ||
45 | sudo ninja -C build/ install | ||
46 | |||
47 | ## Configuration | ||
48 | |||
49 | Εάν ήδη χÏησιμοποιήτε το i3, αντιγÏάψτε το i3 config σας στο `~/.config/sway/config` και | ||
50 | θα δουλÎψει out of the box. Αλλιώς, αντιγÏάψτε το sample configuration αÏχείο στο | ||
51 | `~/.config/sway/config`. Το οποίο συνήθως βÏίσκεται στο `/etc/sway/config`. | ||
52 | Κάντε run `man 5 sway` για πληÏοφοÏίες Ï„Î¿Ï configuration. | ||
53 | |||
54 | ## ΤÏÎχοντας | ||
55 | |||
56 | ΤÏÎξτε `sway` από Îνα TTY. ΜεÏίκα display managers μποÏεί να δουλÎψουν αλλά δÎν είναι συμβατά με | ||
57 | το sway (το gdm γνωÏίζεται να δουλÎβει σχετικά καλά). | ||
58 | |||
59 | [i3]: https://i3wm.org/ | ||
60 | [Wayland]: http://wayland.freedesktop.org/ | ||
61 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
62 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway | ||
63 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
64 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
65 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
66 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
67 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc \ No newline at end of file | ||
diff --git a/README.hi.md b/README.hi.md new file mode 100644 index 00000000..eae5e90a --- /dev/null +++ b/README.hi.md | |||
@@ -0,0 +1,63 @@ | |||
1 | # sway | ||
2 | |||
3 | sway à¤à¤• [i3](https://i3wm.org/)-अनà¥à¤•à¥‚ल | ||
4 | [Wayland](https://wayland.freedesktop.org/) Compositor है। | ||
5 | [FAQ](https://github.com/swaywm/sway/wiki) पढिये। [IRC | ||
6 | Channel](https://web.libera.chat/gamja/?channels=#sway) | ||
7 | (irc.libera.chat पर #sway) में à¤à¥€ जà¥à¤¡à¤¿à¤¯à¥‡à¥¤ | ||
8 | |||
9 | ## रिलीज हसà¥à¤¤à¤¾à¤•à¥à¤·à¤° | ||
10 | |||
11 | रिलीजें | ||
12 | [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) | ||
13 | से साइन होतें हैं और [Github पर](https://github.com/swaywm/sway/releases) पà¥à¤°à¤•à¤¾à¤¶à¤¿à¤¤ होते हैं। | ||
14 | |||
15 | ## इंसà¥à¤Ÿà¥Œà¤²à¥‡à¤¶à¤¨ | ||
16 | |||
17 | ### पैकेजों के दà¥à¤µà¤¾à¤°à¤¾ | ||
18 | |||
19 | Sway कई distributions में उपà¥à¤²à¤¬à¥à¤§ है। आप अपने में "sway" नामक पैकेज इंसà¥à¤Ÿà¥Œà¤² करके देख | ||
20 | सकते हैं। | ||
21 | |||
22 | ### Source से compile करके | ||
23 | |||
24 | यदि आप परीकà¥à¤·à¤£ और विकास के लिठsway और wlroots के नवीनतम संसà¥à¤•à¤°à¤£ बनाना | ||
25 | चाहते हैं, तो [यह विकी | ||
26 | पृषà¥à¤ ](https://github.com/swaywm/sway/wiki/Development-Setup) देखें। | ||
27 | |||
28 | निरà¥à¤à¤°à¤¤à¤¾à¤à¤‚: | ||
29 | |||
30 | * meson \* | ||
31 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) | ||
32 | * wayland | ||
33 | * wayland-protocols \* | ||
34 | * pcre2 | ||
35 | * json-c | ||
36 | * pango | ||
37 | * cairo | ||
38 | * gdk-pixbuf (वैकलà¥à¤ªà¤¿à¤•: system tray के लिये) | ||
39 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (वैकलà¥à¤ªà¤¿à¤•: man पृषà¥à¤ ों के लिये) | ||
40 | \* | ||
41 | * git (वैकलà¥à¤ªà¤¿à¤•: संसà¥à¤•à¤°à¤£ जानने के लिये) | ||
42 | |||
43 | _\* Compilation के समय आवशà¥à¤¯à¤•_ | ||
44 | |||
45 | ये commands चलाà¤à¤‚: | ||
46 | |||
47 | meson build/ | ||
48 | ninja -C build/ | ||
49 | sudo ninja -C build/ install | ||
50 | |||
51 | ## Configuration | ||
52 | |||
53 | अगर आप पहले से ही i3 का उपयोग करते हैं तो अपने i3 config को | ||
54 | `~/.config/sway/config` में copy कर लीजिये और वह बिना किसी परिवरà¥à¤¤à¤¨ के काम | ||
55 | करेगा। अनà¥à¤¯à¤¥à¤¾, नमूने configuration file को `~/.config/sway/config` में copy | ||
56 | कर लीजिये। यह सामानà¥à¤¯à¤¤à¤ƒ `/etc/sway/config` में पाया जाता है। `man 5 | ||
57 | sway` से आप configuration के बारे में जानकारी पà¥à¤°à¤¾à¤ªà¥à¤¤ कर सकते हैं। | ||
58 | |||
59 | ## चलाना | ||
60 | |||
61 | आप à¤à¤• tty से `sway` को चला सकते हैं। कà¥à¤› display managers काम करते हैं परनà¥à¤¤à¥ ये | ||
62 | sway के दà¥à¤µà¤¾à¤°à¤¾ समरà¥à¤¥à¤¿à¤¤ नहीं है (gdm के बारे में जाना गया है कि वह सही काम करता | ||
63 | है)। | ||
diff --git a/README.hu.md b/README.hu.md new file mode 100644 index 00000000..82ca6785 --- /dev/null +++ b/README.hu.md | |||
@@ -0,0 +1,70 @@ | |||
1 | # sway | ||
2 | |||
3 | A Sway egy [i3]-kompatibilis [Wayland] kompozitor. Olvasd el a [Gyarkan Ismételt Kérdéseket][FAQ]. Csatlakozz az [IRC csatornához][IRC channel] \(`#sway` az `irc.libera.chat`-en). | ||
4 | |||
5 | ## Csomag aláÃrások | ||
6 | |||
7 | A kiadott csomagok az [E88F5E48] kulccsal vannak aláÃrva és [GitHub-on][GitHub releases] publikálva. | ||
8 | |||
9 | ## TelepÃtés | ||
10 | |||
11 | ### Csomagból | ||
12 | |||
13 | A Sway sok disztribúció csomagkezelőjéből elérhető, próbáld meg a "sway" | ||
14 | csomagot telepÃteni az általad használt eszközzel. | ||
15 | |||
16 | Ha szeretnél csomagot készÃteni a saját disztribúciódhoz, ugorj be az IRC | ||
17 | csatornára, vagy küldj levelet a sir@cmpwn.com cÃmre tanácsokért. | ||
18 | |||
19 | ### FordÃtás forráskódból | ||
20 | |||
21 | Olvasd el [ezt a wiki oldalt][Development setup], ha szeretnéd tesztelési vagy | ||
22 | fejlesztési célokból lefordÃtani az aktuális (HEAD) állapotát a `sway`-nek és a | ||
23 | `wlroots`-nak. | ||
24 | |||
25 | TelepÃtsd a függÅ‘ségeket: | ||
26 | |||
27 | * meson \* | ||
28 | * [wlroots] | ||
29 | * wayland | ||
30 | * wayland-protocols \* | ||
31 | * pcre2 | ||
32 | * json-c | ||
33 | * pango | ||
34 | * cairo | ||
35 | * gdk-pixbuf2 (opcionális: system tray) | ||
36 | * [scdoc] (opcionális: man pages) \* | ||
37 | * git (opcionális: version info) \* | ||
38 | |||
39 | _\*FordÃtásidejű függÅ‘ség_ | ||
40 | |||
41 | Futtasd ezeket a parancsokat: | ||
42 | |||
43 | meson build | ||
44 | ninja -C build | ||
45 | sudo ninja -C build install | ||
46 | |||
47 | ## Konfiguráció | ||
48 | |||
49 | Ha elÅ‘zÅ‘leg i3-mat használtál, akkor átmásolhatod az i3 beállÃtásaidat a | ||
50 | `~/.config/sway/config` file-ba és ugyanúgy működni fognak. Egyéb esetben másold | ||
51 | le kiindulási alapnak a mintát, ami általában az `etc/sway/config` elérési | ||
52 | útvonalon található. | ||
53 | Futtasd a `man 5 sway` parancsot további információért a konfigurációval | ||
54 | kapcsolatban. | ||
55 | |||
56 | ## Futtatás | ||
57 | |||
58 | Futtasd a `sway` parancsot egy TTY felületről. Néhány bejelentkezéskezelő | ||
59 | (display manager) működhet, de alapvetően nem támogatottak a sway által. (A | ||
60 | gdm-ről ismeretes, hogy egész jól működik.) | ||
61 | |||
62 | [i3]: https://i3wm.org/ | ||
63 | [Wayland]: http://wayland.freedesktop.org/ | ||
64 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
65 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway | ||
66 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
67 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
68 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
69 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
70 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.ir.md b/README.ir.md new file mode 100644 index 00000000..a485a405 --- /dev/null +++ b/README.ir.md | |||
@@ -0,0 +1,54 @@ | |||
1 | # sway | ||
2 | |||
3 | ‏sway یک کامپوزیتور الهام گرÙته از [i3](https://i3wm.org/) بر روی [Wayland](http://wayland.freedesktop.org/) است. [سوال‌های متداول](https://github.com/swaywm/sway/wiki) را بخوانید. در [کانال | ||
4 | IRC](http://web.libera.chat/gamja/?channels=sway&uio=d4) عضو شوید (‎#sway‏ در | ||
5 | irc.libera.chat). | ||
6 | |||
7 | برای Øمایت از تیم توسعه sway به [صÙØÙ‡ | ||
8 | Patreon با نام کاربری SirCmpwn](https://patreon.com/sircmpwn) مراجعه کنید. | ||
9 | |||
10 | ## امضای نسخه‌ها | ||
11 | |||
12 | امضای نسخه‌ها با [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) در [GitHub](https://github.com/swaywm/sway/releases) منتشر می‌شود. | ||
13 | |||
14 | ## شیوه نصب | ||
15 | |||
16 | ### از بسته‌های رسمی | ||
17 | |||
18 | ‏sway در بسته‌های رسمی توزیع‌های مختل٠وجود دارد. بسته «sway» را نصب کنید. در صورتی Ú©Ù‡ بسته رسمی وجود نداشت، برای آگاهی بیشتر درباره نصب روی توزیعتان به این [صÙØÙ‡ راهنما](https://github.com/swaywm/sway/wiki/Unsupported-packages) مراجعه کنید. | ||
19 | |||
20 | اگر به ایجاد بسته sway برای توزیعتان علاقه‌مند هستید، از کانال IRC استÙاده کنید یا به sir@cmpwn.com ایمیل بزنید. | ||
21 | |||
22 | ### کامپایل کردن کد | ||
23 | |||
24 | چنانچه می‌خواهید آخرین نسخه کد sway Ùˆ wlroots را برای آزمایش یا توسعه بسازید به این [صÙØÙ‡ راهنما](https://github.com/swaywm/sway/wiki/Development-Setup) مراجعه کنید. | ||
25 | |||
26 | بسته‌های مورد نیاز: | ||
27 | |||
28 | * meson \* | ||
29 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) | ||
30 | * wayland | ||
31 | * wayland-protocols \* | ||
32 | * pcre2 | ||
33 | * json-c | ||
34 | * pango | ||
35 | * cairo | ||
36 | * gdk-pixbuf2 (انتخابی: برای system tray) | ||
37 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (انتخابی: برای صÙØه‌های راهنما) \* | ||
38 | * git (انتخابی: برای اطلاع در خصوص نسخه‌ها) \* | ||
39 | |||
40 | _\*نیازمندی‌های زمان کامپایل برنامه_ | ||
41 | |||
42 | این Ùرمان‌ها را اجرا کنید: | ||
43 | |||
44 | meson build | ||
45 | ninja -C build | ||
46 | sudo ninja -C build install | ||
47 | |||
48 | ### شخصی سازی و تنظیمات | ||
49 | |||
50 | اگر در Øال Øاضر از i3 استÙاده می‌کنید، تنظیمات i3 خودتان را در Ùایل ‪`~/.config/sway/config`‬ Ú©Ù¾ÛŒ کنید Ùˆ بدون نیاز به تغییر کار خواهد کرد. در غیر این‌صورت، Ùایل نمونه تنظیمات را استÙاده کنید. این Ùایل عموما در ‪`/etc/sway/config`‬ قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید. | ||
51 | |||
52 | ## اجرا | ||
53 | |||
54 | در Ù…Øیط TTY کاÙیست `sway` را اجرا کنید. ممکن است ابزارهای مدیریت نمایشگری نیز برای این کار وجود داشته باشند اما از طر٠sway پشتیبانی نمی‌شوند (gdm عملکرد خوبی در این زمینه دارد). | ||
diff --git a/README.it.md b/README.it.md new file mode 100644 index 00000000..82bb5783 --- /dev/null +++ b/README.it.md | |||
@@ -0,0 +1,66 @@ | |||
1 | # sway | ||
2 | |||
3 | sway è un compositore di [Wayland] compatibile con [i3]. Leggi le [FAQ]. | ||
4 | Unisciti al [canale di IRC] \(#sway su irc.libera.chat). | ||
5 | |||
6 | ## Firma delle versioni | ||
7 | |||
8 | Le versioni sono firmate con la chiave [E88F5E48] e sono pubblicate | ||
9 | [su GitHub][GitHub releases]. | ||
10 | |||
11 | ## Installazione | ||
12 | |||
13 | ### Da un pacchetto | ||
14 | |||
15 | Sway è disponibile in molte distribuzioni, prova a installare il pacchetto | ||
16 | "sway" per la tua. | ||
17 | |||
18 | ### Compilazione dei sorgenti | ||
19 | |||
20 | Consulta [questa pagina del wiki][Development setup] se vuoi compilare l'HEAD | ||
21 | di sway e wlroots per testarli o contribuire allo sviluppo. | ||
22 | |||
23 | Installa le dipendenze: | ||
24 | |||
25 | * meson \* | ||
26 | * [wlroots] | ||
27 | * wayland | ||
28 | * wayland-protocols \* | ||
29 | * pcre | ||
30 | * json-c | ||
31 | * pango | ||
32 | * cairo | ||
33 | * gdk-pixbuf2 (opzionale: area di notifica) | ||
34 | * [scdoc] (opzionale: pagine del manuale) \* | ||
35 | * git (opzionale: informazioni sulla versione) \* | ||
36 | |||
37 | _\* Dipendenza necessaria per la compilazione_ | ||
38 | |||
39 | Esegui questi comandi: | ||
40 | |||
41 | meson build/ | ||
42 | ninja -C build/ | ||
43 | sudo ninja -C build/ install | ||
44 | |||
45 | ## Configurazione | ||
46 | |||
47 | Se hai già usato i3, copia il tuo file di configurazione in | ||
48 | `~/.config/sway/config` e sway funzionerà immediatamente. Altrimenti, copia il | ||
49 | file d'esempio in `~/.config/sway/config`, generalmente è situato in | ||
50 | `/etc/sway/config`. Consulta `man 5 sway` per ulteriori informazioni sulla | ||
51 | configurazione. | ||
52 | |||
53 | ## Esecuzione | ||
54 | |||
55 | Lancia `sway` da un TTY. Alcuni gestori d'accesso potrebbero funzionare ma non | ||
56 | sono supportati da sway (gdm funziona abbastanza bene). | ||
57 | |||
58 | [i3]: https://i3wm.org/ | ||
59 | [Wayland]: http://wayland.freedesktop.org/ | ||
60 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
61 | [canale di IRC]: https://web.libera.chat/gamja/?channels=#sway | ||
62 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
63 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
64 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
65 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
66 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.ja.md b/README.ja.md index fa28f3da..4e9a9971 100644 --- a/README.ja.md +++ b/README.ja.md | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | Swayã¯[i3](https://i3wm.org/)互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚ | 3 | Swayã¯[i3](https://i3wm.org/)互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚ |
4 | [FAQ](https://github.com/swaywm/sway/wiki)ã‚‚åˆã‚ã›ã¦ã”覧ãã ã•ã„。 | 4 | [FAQ](https://github.com/swaywm/sway/wiki)ã‚‚åˆã‚ã›ã¦ã”覧ãã ã•ã„。 |
5 | [IRC ãƒãƒ£ãƒ³ãƒãƒ«](http://webchat.freenode.net/?channels=sway&uio=d4) (irc.freenode.netã®#sway)ã‚‚ã‚ã‚Šã¾ã™ã€‚ | 5 | [IRC ãƒãƒ£ãƒ³ãƒãƒ«](https://web.libera.chat/gamja/?channels=#sway) (irc.libera.chatã®#sway)ã‚‚ã‚ã‚Šã¾ã™ã€‚ |
6 | 6 | ||
7 | [![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) | 7 | [![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) |
8 | 8 | ||
@@ -12,7 +12,7 @@ SirCmpwnã¯ã€æ—¥æœ¬èªžã§ã®ã‚µãƒãƒ¼ãƒˆã‚’IRCã¨GitHubã§è¡Œã„ã¾ã™ã€‚タイ | |||
12 | 12 | ||
13 | ## リリースã®ç½²å | 13 | ## リリースã®ç½²å |
14 | 14 | ||
15 | Swayã®ãƒªãƒªãƒ¼ã‚¹ã¯[B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)ã§ç½²åã•ã‚Œã€[GitHub](https://github.com/swaywm/sway/releases)ã§å…¬é–‹ã•ã‚Œã¦ã„ã¾ã™ã€‚ | 15 | Swayã®ãƒªãƒªãƒ¼ã‚¹ã¯[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)ã§ç½²åã•ã‚Œã€[GitHub](https://github.com/swaywm/sway/releases)ã§å…¬é–‹ã•ã‚Œã¦ã„ã¾ã™ã€‚ |
16 | 16 | ||
17 | ## インストール | 17 | ## インストール |
18 | 18 | ||
@@ -27,18 +27,18 @@ Swayã¯æ²¢å±±ã®ãƒ‡ã‚£ã‚¹ãƒˆãƒªãƒ“ューションã§æä¾›ã•ã‚Œã¦ã„ã¾ã™ã€‚" | |||
27 | 次ã®ä¾å˜ãƒ‘ッケージをインストールã—ã¦ãã ã•ã„: | 27 | 次ã®ä¾å˜ãƒ‘ッケージをインストールã—ã¦ãã ã•ã„: |
28 | 28 | ||
29 | * meson \* | 29 | * meson \* |
30 | * [wlroots](https://github.com/swaywm/wlroots) | 30 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
31 | * wayland | 31 | * wayland |
32 | * wayland-protocols \* | 32 | * wayland-protocols \* |
33 | * pcre | 33 | * pcre2 |
34 | * json-c | 34 | * json-c |
35 | * pango | 35 | * pango |
36 | * cairo | 36 | * cairo |
37 | * gdk-pixbuf2 (システムイコンã§å¿…è¦ã§ã™) | 37 | * gdk-pixbuf2 (ä»»æ„: システムイコンã§å¿…è¦ã§ã™) |
38 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (manã§å¿…è¦ã§ã™) \* | 38 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (ä»»æ„: manã§å¿…è¦ã§ã™) \* |
39 | * git \* | 39 | * git (ä»»æ„: ãƒãƒ¼ã‚¸ãƒ§ãƒ³æƒ…å ±ã§å¿…è¦ã§ã™) \* |
40 | 40 | ||
41 | _\*コンパイルã®æ™‚_ | 41 | _\*コンパイル時ã®ä¾å˜_ |
42 | 42 | ||
43 | 次ã®ã‚³ãƒžãƒ³ãƒ‰ã‚’実行ã—ã¦ãã ã•ã„: | 43 | 次ã®ã‚³ãƒžãƒ³ãƒ‰ã‚’実行ã—ã¦ãã ã•ã„: |
44 | 44 | ||
@@ -46,12 +46,6 @@ _\*コンパイルã®æ™‚_ | |||
46 | ninja -C build | 46 | ninja -C build |
47 | sudo ninja -C build install | 47 | sudo ninja -C build install |
48 | 48 | ||
49 | logindを使用ã—ã¦ã„ãªã„システムã§ã¯ã€ãƒã‚¤ãƒŠãƒªã«suidã‚’è¨å®šã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™: | ||
50 | |||
51 | sudo chmod a+s /usr/local/bin/sway | ||
52 | |||
53 | swayã¯èµ·å‹•å¾Œã€ã™ãã«root許å¯ã‚’è½ã¨ã—ã¾ã™ã€‚ | ||
54 | |||
55 | ## è¨å®š | 49 | ## è¨å®š |
56 | 50 | ||
57 | æ—¢ã«i3を使用ã—ã¦ã„ã‚‹å ´åˆã¯ã€i3ã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’`~/.config/sway/config`ã«ã‚³ãƒ”ーã™ã‚Œã°å‹•ãã¾ã™ã€‚ãã†ã§ãªã„å ´åˆã¯ã€ã‚µãƒ³ãƒ—ルã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’`~/.config/sway/config`ã«ã‚³ãƒ”ーã—ã¦ãã ã•ã„。サンプルã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã¯ã€é€šå¸¸`/etc/sway/config`ã«ã‚ã‚Šã¾ã™ã€‚`man 5 sway`を実行ã™ã‚‹ã“ã¨ã§ã€è¨å®šã«é–¢ã™ã‚‹æƒ…å ±ã‚’è¦‹ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ | 51 | æ—¢ã«i3を使用ã—ã¦ã„ã‚‹å ´åˆã¯ã€i3ã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’`~/.config/sway/config`ã«ã‚³ãƒ”ーã™ã‚Œã°å‹•ãã¾ã™ã€‚ãã†ã§ãªã„å ´åˆã¯ã€ã‚µãƒ³ãƒ—ルã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’`~/.config/sway/config`ã«ã‚³ãƒ”ーã—ã¦ãã ã•ã„。サンプルã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã¯ã€é€šå¸¸`/etc/sway/config`ã«ã‚ã‚Šã¾ã™ã€‚`man 5 sway`を実行ã™ã‚‹ã“ã¨ã§ã€è¨å®šã«é–¢ã™ã‚‹æƒ…å ±ã‚’è¦‹ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ |
diff --git a/README.ko.md b/README.ko.md index 9c3dd323..e086c174 100644 --- a/README.ko.md +++ b/README.ko.md | |||
@@ -1,11 +1,11 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | sway는 [i3](https://i3wm.org/)-호환 [Wayland](http://wayland.freedesktop.org/) ì»´í¬ì§€í„°ìž…니다. | 3 | sway는 [i3](https://i3wm.org/)-호환 [Wayland](http://wayland.freedesktop.org/) ì»´í¬ì§€í„°ìž…니다. |
4 | [FAQ](https://github.com/swaywm/sway/wiki)를 ì½ì–´ë³´ì„¸ìš”. [IRC 채ë„](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)ë„ ìžˆìŠµë‹ˆë‹¤. | 4 | [FAQ](https://github.com/swaywm/sway/wiki)를 ì½ì–´ë³´ì„¸ìš”. [IRC 채ë„](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat)ë„ ìžˆìŠµë‹ˆë‹¤. |
5 | 5 | ||
6 | ## 릴리즈 서명 | 6 | ## 릴리즈 서명 |
7 | 7 | ||
8 | 릴리즈는 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A)ì—ì„œ 서명ë˜ê³ , | 8 | 릴리즈는 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)ì—ì„œ 서명ë˜ê³ , |
9 | [GitHubì—ì„œ](https://github.com/swaywm/sway/releases) 공개ë˜ê³ 있습니다. | 9 | [GitHubì—ì„œ](https://github.com/swaywm/sway/releases) 공개ë˜ê³ 있습니다. |
10 | 10 | ||
11 | ## 설치 | 11 | ## 설치 |
@@ -24,10 +24,10 @@ IRC 채ë„ì„ ë°©ë¬¸í•˜ê±°ë‚˜ sir@cmpwn.com으로 ì´ë©”ì¼ì„ ë³´ë‚´ ìƒë‹´ ë°› | |||
24 | ë‹¤ìŒ ì˜ì¡´ íŒ¨í‚¤ì§€ë“¤ì„ ì„¤ì¹˜í•´ 주세요: | 24 | ë‹¤ìŒ ì˜ì¡´ íŒ¨í‚¤ì§€ë“¤ì„ ì„¤ì¹˜í•´ 주세요: |
25 | 25 | ||
26 | * meson \* | 26 | * meson \* |
27 | * [wlroots](https://github.com/swaywm/wlroots) | 27 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
28 | * wayland | 28 | * wayland |
29 | * wayland-protocols \* | 29 | * wayland-protocols \* |
30 | * pcre | 30 | * pcre2 |
31 | * json-c | 31 | * json-c |
32 | * pango | 32 | * pango |
33 | * cairo | 33 | * cairo |
@@ -43,12 +43,6 @@ _\*ì»´íŒŒì¼ ë–„ í•„ìš”_ | |||
43 | ninja -C build | 43 | ninja -C build |
44 | sudo ninja -C build install | 44 | sudo ninja -C build install |
45 | 45 | ||
46 | logind를 ì‚¬ìš©í•˜ê³ ìžˆì§€ 않는 시스템ì—서는, ë°”ì´ë„ˆë¦¬ì— suid를 ì„¤ì •í• í•„ìš”ê°€ 있습니다: | ||
47 | |||
48 | sudo chmod a+s /usr/local/bin/sway | ||
49 | |||
50 | Sway는 시작 í›„ì— root ê¶Œí•œì„ dropí• ê²ƒ 입니다. | ||
51 | |||
52 | ## ì„¤ì • | 46 | ## ì„¤ì • |
53 | 47 | ||
54 | i3를 ì´ë¯¸ 사용 중ì´ë¼ë©´, i3 configì„ `~/.config/sway/config`ë¡œ 복사하세요. | 48 | i3를 ì´ë¯¸ 사용 중ì´ë¼ë©´, i3 configì„ `~/.config/sway/config`ë¡œ 복사하세요. |
@@ -1,9 +1,9 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | **[English][en]** - [日本語][ja] - [Français][fr] - [УкраїнÑька][uk] - [Español][es] - [Polski][pl] - [ä¸æ–‡-简体][zh-CN] - [Deutsch][de] - [Nederlands][nl] - [РуÑÑкий][ru] - [ä¸æ–‡-ç¹é«”][zh-TW] - [Português][pt] - [Danish][dk] - [í•œêµì–´][ko] - [Română][ro] | 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.freenode.net). | 6 | [IRC channel] \(#sway on irc.libera.chat). |
7 | 7 | ||
8 | ## Release Signatures | 8 | ## Release Signatures |
9 | 9 | ||
@@ -16,9 +16,6 @@ Releases are signed with [E88F5E48] and published [on GitHub][GitHub releases]. | |||
16 | Sway is available in many distributions. Try installing the "sway" package for | 16 | Sway is available in many distributions. Try installing the "sway" package for |
17 | yours. | 17 | yours. |
18 | 18 | ||
19 | If you're interested in packaging sway for your distribution, stop by the IRC | ||
20 | channel or shoot an email to sir@cmpwn.com for advice. | ||
21 | |||
22 | ### Compiling from Source | 19 | ### Compiling from Source |
23 | 20 | ||
24 | Check out [this wiki page][Development setup] if you want to build the HEAD of | 21 | Check out [this wiki page][Development setup] if you want to build the HEAD of |
@@ -30,27 +27,22 @@ Install dependencies: | |||
30 | * [wlroots] | 27 | * [wlroots] |
31 | * wayland | 28 | * wayland |
32 | * wayland-protocols \* | 29 | * wayland-protocols \* |
33 | * pcre | 30 | * pcre2 |
34 | * json-c | 31 | * json-c |
35 | * pango | 32 | * pango |
36 | * cairo | 33 | * cairo |
37 | * gdk-pixbuf2 (optional: system tray) | 34 | * gdk-pixbuf2 (optional: additional image formats for system tray) |
35 | * [swaybg] (optional: wallpaper) | ||
38 | * [scdoc] (optional: man pages) \* | 36 | * [scdoc] (optional: man pages) \* |
39 | * git (optional: version info) \* | 37 | * git (optional: version info) \* |
40 | 38 | ||
41 | _\*Compile-time dep_ | 39 | _\* Compile-time dep_ |
42 | 40 | ||
43 | Run these commands: | 41 | Run these commands: |
44 | 42 | ||
45 | meson build | 43 | meson build/ |
46 | ninja -C build | 44 | ninja -C build/ |
47 | sudo ninja -C build install | 45 | sudo ninja -C build/ install |
48 | |||
49 | On systems without logind, you need to suid the sway binary: | ||
50 | |||
51 | sudo chmod a+s /usr/local/bin/sway | ||
52 | |||
53 | Sway will drop root permissions shortly after startup. | ||
54 | 46 | ||
55 | ## Configuration | 47 | ## Configuration |
56 | 48 | ||
@@ -65,26 +57,38 @@ Run `sway` from a TTY. Some display managers may work but are not supported by | |||
65 | sway (gdm is known to work fairly well). | 57 | sway (gdm is known to work fairly well). |
66 | 58 | ||
67 | [en]: https://github.com/swaywm/sway#readme | 59 | [en]: https://github.com/swaywm/sway#readme |
68 | [ja]: https://github.com/swaywm/sway/blob/master/README.ja.md | 60 | [ar]: README.ar.md |
69 | [fr]: https://github.com/swaywm/sway/blob/master/README.fr.md | 61 | [cs]: README.cs.md |
70 | [uk]: https://github.com/swaywm/sway/blob/master/README.uk.md | 62 | [de]: README.de.md |
71 | [es]: https://github.com/swaywm/sway/blob/master/README.es.md | 63 | [dk]: README.dk.md |
72 | [pl]: https://github.com/swaywm/sway/blob/master/README.pl.md | 64 | [es]: README.es.md |
73 | [zh-CN]: https://github.com/swaywm/sway/blob/master/README.zh-CN.md | 65 | [fr]: README.fr.md |
74 | [de]: https://github.com/swaywm/sway/blob/master/README.de.md | 66 | [ge]: README.ge.md |
75 | [nl]: https://github.com/swaywm/sway/blob/master/README.nl.md | 67 | [gr]: README.gr.md |
76 | [ru]: https://github.com/swaywm/sway/blob/master/README.ru.md | 68 | [hi]: README.hi.md |
77 | [zh-TW]: https://github.com/swaywm/sway/blob/master/README.zh-TW.md | 69 | [hu]: README.hu.md |
78 | [pt]: https://github.com/swaywm/sway/blob/master/README.pt.md | 70 | [ir]: README.ir.md |
79 | [dk]: https://github.com/swaywm/sway/blob/master/README.dk.md | 71 | [it]: README.it.md |
80 | [ko]: https://github.com/swaywm/sway/blob/master/README.ko.md | 72 | [ja]: README.ja.md |
81 | [ro]: https://github.com/swaywm/sway/blob/master/README.ro.md | 73 | [ko]: README.ko.md |
74 | [nl]: README.nl.md | ||
75 | [no]: README.no.md | ||
76 | [pl]: README.pl.md | ||
77 | [pt]: README.pt.md | ||
78 | [ro]: README.ro.md | ||
79 | [ru]: README.ru.md | ||
80 | [sv]: README.sv.md | ||
81 | [tr]: README.tr.md | ||
82 | [uk]: README.uk.md | ||
83 | [zh-CN]: README.zh-CN.md | ||
84 | [zh-TW]: README.zh-TW.md | ||
82 | [i3]: https://i3wm.org/ | 85 | [i3]: https://i3wm.org/ |
83 | [Wayland]: http://wayland.freedesktop.org/ | 86 | [Wayland]: http://wayland.freedesktop.org/ |
84 | [FAQ]: https://github.com/swaywm/sway/wiki | 87 | [FAQ]: https://github.com/swaywm/sway/wiki |
85 | [IRC channel]: http://webchat.freenode.net/?channels=sway&uio=d4 | 88 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway |
86 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | 89 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 |
87 | [GitHub releases]: https://github.com/swaywm/sway/releases | 90 | [GitHub releases]: https://github.com/swaywm/sway/releases |
88 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | 91 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup |
89 | [wlroots]: https://github.com/swaywm/wlroots | 92 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots |
93 | [swaybg]: https://github.com/swaywm/swaybg/ | ||
90 | [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 86ebe398..bf1ea975 100644 --- a/README.nl.md +++ b/README.nl.md | |||
@@ -2,12 +2,12 @@ | |||
2 | 2 | ||
3 | Sway is een [i3](https://i3wm.org/)-compatibele [Wayland](http://wayland.freedesktop.org/) compositor. | 3 | Sway is een [i3](https://i3wm.org/)-compatibele [Wayland](http://wayland.freedesktop.org/) compositor. |
4 | Lees de [FAQ](https://github.com/swaywm/sway/wiki). Word lid van het [IRC | 4 | Lees de [FAQ](https://github.com/swaywm/sway/wiki). Word lid van het [IRC |
5 | kanaal](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway op | 5 | kanaal](https://web.libera.chat/gamja/?channels=#sway) (#sway op |
6 | irc.freenode.net). | 6 | irc.libera.chat). |
7 | 7 | ||
8 | ## Releasehandtekeningen | 8 | ## Releasehandtekeningen |
9 | 9 | ||
10 | Releases worden ondertekend met [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) | 10 | Releases worden ondertekend met [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) |
11 | en gepubliceerd [op GitHub](https://github.com/swaywm/sway/releases). | 11 | en gepubliceerd [op GitHub](https://github.com/swaywm/sway/releases). |
12 | 12 | ||
13 | ## Installatie | 13 | ## Installatie |
@@ -25,10 +25,10 @@ kanaal of stuur een e-mail naar sir@cmpwn.com voor advies. | |||
25 | Afhankelijkheden installeren: | 25 | Afhankelijkheden installeren: |
26 | 26 | ||
27 | * meson \* | 27 | * meson \* |
28 | * [wlroots](https://github.com/swaywm/wlroots) | 28 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
29 | * wayland | 29 | * wayland |
30 | * wayland-protocols \* | 30 | * wayland-protocols \* |
31 | * pcre | 31 | * pcre2 |
32 | * json-c | 32 | * json-c |
33 | * pango | 33 | * pango |
34 | * cairo | 34 | * cairo |
@@ -44,12 +44,6 @@ Voer deze opdrachten uit: | |||
44 | ninja -C build | 44 | ninja -C build |
45 | sudo ninja -C build install | 45 | sudo ninja -C build install |
46 | 46 | ||
47 | Op systemen zonder logind, moet je bij het binaire bestand het suid bit instellen: | ||
48 | |||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | |||
51 | Sway zal root-rechten kort na het opstarten loslaten. | ||
52 | |||
53 | ## Configuratie | 47 | ## Configuratie |
54 | 48 | ||
55 | Als je al i3 gebruikt, kopieer dan je i3-configuratie naar `~/.config/sway/config` en | 49 | Als je al i3 gebruikt, kopieer dan je i3-configuratie naar `~/.config/sway/config` en |
diff --git a/README.no.md b/README.no.md new file mode 100644 index 00000000..35d6e320 --- /dev/null +++ b/README.no.md | |||
@@ -0,0 +1,68 @@ | |||
1 | # Sway | ||
2 | |||
3 | Sway er en [i3]-kompatibel [Wayland] compositor. Les [Ofte stilte spørsmål]. | ||
4 | Delta på [IRC kanalen][IRC kanal] \(#sway på irc.libera.chat). | ||
5 | |||
6 | ## Utgivelses Signaturer | ||
7 | |||
8 | Utgivelser er signert med [E88F5E48] og publisert [på GitHub][GitHub | ||
9 | releases]. | ||
10 | |||
11 | ## Installasjon | ||
12 | |||
13 | ### Fra system pakker | ||
14 | |||
15 | Sway er tilgjengelig i mange distribusjoner. Prøv å installere "sway" pakken | ||
16 | fra din distro sine repoer. | ||
17 | |||
18 | Er du interessert i å pakke Sway for din distribusjon kan du ta turen innom | ||
19 | IRC-kanalen eller send en e-post til sir@cmpwn.com for råd. | ||
20 | |||
21 | ### Kompilering fra kildekode | ||
22 | |||
23 | Se [denne wiki-siden][Oppsetting for utvikling] hvis du vil bygge fra HEAD grenen av sway og | ||
24 | wlroots for testing eller utvikling. | ||
25 | |||
26 | Installasjonsavhengigheter: | ||
27 | |||
28 | * meson \* | ||
29 | * [wlroots] | ||
30 | * wayland | ||
31 | * wayland-protocols \* | ||
32 | * pcre2 | ||
33 | * json-c | ||
34 | * pango | ||
35 | * cairo | ||
36 | * gdk-pixbuf2 (valgfritt: system tray) | ||
37 | * [scdoc] (valgfritt: man pages) \* | ||
38 | * git \* | ||
39 | |||
40 | _\*Kompileringsavhengigheter_ | ||
41 | |||
42 | Kjør følgende kommandoer: | ||
43 | |||
44 | meson build | ||
45 | ninja -C build | ||
46 | sudo ninja -C build install | ||
47 | |||
48 | ## Konfigurasjon | ||
49 | |||
50 | Hvis du allerede bruker i3 kan du bare kopiere din i3 konfigurasjon til | ||
51 | `~/.config/sway/config`. Ellers skal du kopiere eksempel konfigurasjonsfilen til | ||
52 | `~/.config/sway/config`. Eksempel filen er normalt plasert i `/etc/sway/config`. Kjør | ||
53 | `man 5 sway` for å få oplysninger om konfigurasjonen. | ||
54 | |||
55 | ## Utførelse | ||
56 | |||
57 | Kjør `sway` fra en TTY. Noen display managers kan fungere, men Sway har ikke | ||
58 | støtte for dem (gdm er kjent for å fungere ganske bra). | ||
59 | |||
60 | [i3]: https://i3wm.org/ | ||
61 | [Wayland]: http://wayland.freedesktop.org/ | ||
62 | [Ofte stilte spørsmål]: https://github.com/swaywm/sway/wiki | ||
63 | [IRC kanal]: https://web.libera.chat/gamja/?channels=#sway | ||
64 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
65 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
66 | [Oppsetting for utvikling]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
67 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
68 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.pl.md b/README.pl.md index b63b8567..65b3c3a1 100644 --- a/README.pl.md +++ b/README.pl.md | |||
@@ -1,12 +1,12 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z [i3](https://i3wm.org/). | 3 | sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z [i3](https://i3wm.org/). |
4 | Przeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](http://webchat.freenode.net/?channels=sway&uio=d4) | 4 | Przeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](https://web.libera.chat/gamja/?channels=#sway) |
5 | (#sway na irc.freenode.net). | 5 | (#sway na irc.libera.chat). |
6 | 6 | ||
7 | ## Podpisy cyfrowe wydań | 7 | ## Podpisy cyfrowe wydań |
8 | 8 | ||
9 | Wydania sÄ… podpisywane przy pomocy klucza [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) | 9 | Wydania sÄ… podpisywane przy pomocy klucza [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) |
10 | i publikowane [na GitHubie](https://github.com/swaywm/sway/releases). | 10 | i publikowane [na GitHubie](https://github.com/swaywm/sway/releases). |
11 | 11 | ||
12 | ## Instalacja | 12 | ## Instalacja |
@@ -25,10 +25,10 @@ adres sir@cmpwn.com w celu uzyskania wskazówek. | |||
25 | Zainstaluj zależności: | 25 | Zainstaluj zależności: |
26 | 26 | ||
27 | * meson \* | 27 | * meson \* |
28 | * [wlroots](https://github.com/swaywm/wlroots) | 28 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
29 | * wayland | 29 | * wayland |
30 | * wayland-protocols \* | 30 | * wayland-protocols \* |
31 | * pcre | 31 | * pcre2 |
32 | * json-c | 32 | * json-c |
33 | * pango | 33 | * pango |
34 | * cairo | 34 | * cairo |
@@ -44,12 +44,6 @@ Wykonaj następujące polecenia: | |||
44 | ninja -C build | 44 | ninja -C build |
45 | sudo ninja -C build install | 45 | sudo ninja -C build install |
46 | 46 | ||
47 | Na systemach bez logind należy wykonać polecenie suid na pliku wykonywalnym sway: | ||
48 | |||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | |||
51 | Sway pozbędzie się uprawnień roota tuż po wystartowaniu. | ||
52 | |||
53 | ## Konfiguracja | 47 | ## Konfiguracja |
54 | 48 | ||
55 | Jeśli już korzystasz z i3, skopiuj swoją konfigurację i3 do katalogu `~/.config/sway/config` i | 49 | Jeśli już korzystasz z i3, skopiuj swoją konfigurację i3 do katalogu `~/.config/sway/config` i |
diff --git a/README.pt.md b/README.pt.md index ad7cab65..c1611a31 100644 --- a/README.pt.md +++ b/README.pt.md | |||
@@ -2,12 +2,12 @@ | |||
2 | 2 | ||
3 | O sway é um compositor do [Wayland](http://wayland.freedesktop.org/) compatÃvel com o [i3](https://i3wm.org/). | 3 | O sway é um compositor do [Wayland](http://wayland.freedesktop.org/) compatÃvel com o [i3](https://i3wm.org/). |
4 | Leia o [FAQ](https://github.com/swaywm/sway/wiki). Junte-se ao [canal do | 4 | Leia o [FAQ](https://github.com/swaywm/sway/wiki). Junte-se ao [canal do |
5 | IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway em | 5 | IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway em |
6 | irc.freenode.net). | 6 | irc.libera.chat). |
7 | 7 | ||
8 | ## Assinatura das versões | 8 | ## Assinatura das versões |
9 | 9 | ||
10 | As versões são assinadas com [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) | 10 | As versões são assinadas com [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) |
11 | e publicadas [no GitHub](https://github.com/swaywm/sway/releases). | 11 | e publicadas [no GitHub](https://github.com/swaywm/sway/releases). |
12 | 12 | ||
13 | ## Instalação | 13 | ## Instalação |
@@ -27,10 +27,10 @@ Verifique [essa página da wiki](https://github.com/swaywm/sway/wiki/Development | |||
27 | Instale as dependências: | 27 | Instale as dependências: |
28 | 28 | ||
29 | * meson \* | 29 | * meson \* |
30 | * [wlroots](https://github.com/swaywm/wlroots) | 30 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
31 | * wayland | 31 | * wayland |
32 | * wayland-protocols \* | 32 | * wayland-protocols \* |
33 | * pcre | 33 | * pcre2 |
34 | * json-c | 34 | * json-c |
35 | * pango | 35 | * pango |
36 | * cairo | 36 | * cairo |
@@ -46,12 +46,6 @@ Execute esses comandos: | |||
46 | ninja -C build | 46 | ninja -C build |
47 | sudo ninja -C build install | 47 | sudo ninja -C build install |
48 | 48 | ||
49 | Em sistemas sem logind, você precisa preparar o binário do sway: | ||
50 | |||
51 | sudo chmod a+s /usr/local/bin/sway | ||
52 | |||
53 | O sway perderá as privilégios de de root logo após o inÃcio do sistema. | ||
54 | |||
55 | ## Configuração | 49 | ## Configuração |
56 | 50 | ||
57 | Se você já utiliza o i3, então copie os seus arquivos de configuração para `~/.config/sway/config` e | 51 | Se você já utiliza o i3, então copie os seus arquivos de configuração para `~/.config/sway/config` e |
diff --git a/README.ro.md b/README.ro.md index dd895b56..a3559a8b 100644 --- a/README.ro.md +++ b/README.ro.md | |||
@@ -1,11 +1,11 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | sway este un compositor pentru [Wayland](http://wayland.freedesktop.org/) compatibil cu [i3](https://i3wm.org/). | 3 | sway este un compositor pentru [Wayland](http://wayland.freedesktop.org/) compatibil cu [i3](https://i3wm.org/). |
4 | Citiți [FAQ](https://github.com/swaywm/sway/wiki)-ul. Connectați-vă la canalul nostru [IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway pe irc.freenode.net). | 4 | Citiți [FAQ](https://github.com/swaywm/sway/wiki)-ul. Connectați-vă la canalul nostru [IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway pe irc.libera.chat). |
5 | 5 | ||
6 | ## Semnarea digitală | 6 | ## Semnarea digitală |
7 | 7 | ||
8 | Noile versiuni sunt semnate cu [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) | 8 | Noile versiuni sunt semnate cu [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) |
9 | și postate [pe GitHub](https://github.com/swaywm/sway/releases). | 9 | și postate [pe GitHub](https://github.com/swaywm/sway/releases). |
10 | 10 | ||
11 | ## Instalare | 11 | ## Instalare |
@@ -22,10 +22,10 @@ Dacă sunteți interesați in a crea pachete pentru distribuția voastră, infor | |||
22 | Dependențe pentru instalare: | 22 | Dependențe pentru instalare: |
23 | 23 | ||
24 | * meson \* | 24 | * meson \* |
25 | * [wlroots](https://github.com/swaywm/wlroots) | 25 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
26 | * wayland | 26 | * wayland |
27 | * wayland-protocols \* | 27 | * wayland-protocols \* |
28 | * pcre | 28 | * pcre2 |
29 | * json-c | 29 | * json-c |
30 | * pango | 30 | * pango |
31 | * cairo | 31 | * cairo |
@@ -43,14 +43,6 @@ Rulați aceste comenzi: | |||
43 | sudo ninja -C build install | 43 | sudo ninja -C build install |
44 | ``` | 44 | ``` |
45 | 45 | ||
46 | Pe sisteme fără logind, trebuie să folosiți următoarea comandă pentru a marca binarul de Sway ca suid: | ||
47 | |||
48 | ``` | ||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | ``` | ||
51 | |||
52 | Imediat după pornire, Sway va renunța la permisiunile de root. | ||
53 | |||
54 | ## Configurare | 46 | ## Configurare |
55 | 47 | ||
56 | Dacă folosiți deja i3, copiați fișierul de configurare din i3 în `~/.config/sway/config`, și va funcționa fără a necesita nici o modificare. In caz contrar, copiați exemplul de configurare (disponibil de obicei în `/etc/sway/config`) în `~/.config/sway/config`. | 48 | Dacă folosiți deja i3, copiați fișierul de configurare din i3 în `~/.config/sway/config`, și va funcționa fără a necesita nici o modificare. In caz contrar, copiați exemplul de configurare (disponibil de obicei în `/etc/sway/config`) în `~/.config/sway/config`. |
diff --git a/README.ru.md b/README.ru.md index a870ec89..edc0eda7 100644 --- a/README.ru.md +++ b/README.ru.md | |||
@@ -1,42 +1,41 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | sway - Ñто [i3](https://i3wm.org/)-ÑовмеÑтимый композитор [Wayland](http://wayland.freedesktop.org/). | 3 | sway - Ñто [i3]-ÑовмеÑтимый композитор [Wayland]. |
4 | Больше информации в [FAQ](https://github.com/swaywm/sway/wiki). ПриÑоединÑйтеÑÑŒ к | 4 | Больше информации в [FAQ]. ПриÑоединÑйтеÑÑŒ к |
5 | [IRC-каналу](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на | 5 | [IRC-каналу][IRC channel] (#sway на |
6 | irc.freenode.net). | 6 | irc.libera.chat). |
7 | 7 | ||
8 | ## ПодпиÑи релизов | 8 | ## ПодпиÑи релизов |
9 | 9 | ||
10 | Релизы подпиÑываютÑÑ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) | 10 | Релизы подпиÑываютÑÑ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ [E88F5E48] и публикуютÑÑ [на GitHub][GitHub releases]. |
11 | и публикуютÑÑ [на GitHub](https://github.com/swaywm/sway/releases). | ||
12 | 11 | ||
13 | ## УÑтановка | 12 | ## УÑтановка |
14 | 13 | ||
15 | ### Из репозиториев | 14 | ### Из репозиториев |
16 | 15 | ||
17 | sway доÑтупен во многих диÑтрибутивах. Попробуйте уÑтановить пакет "sway". | 16 | Sway доÑтупен во многих диÑтрибутивах. Попробуйте уÑтановить пакет "sway". |
18 | ЕÑли он вдруг недоÑтупен, проверьте [Ñту Ñтраницу на wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) | ||
19 | Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ о подробноÑÑ‚ÑÑ… уÑтановки Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ | ||
20 | диÑтрибутива. | ||
21 | 17 | ||
22 | ЕÑли вы заинтереÑованы поддерживать sway в вашем диÑтрибутиве, заглÑните в наш IRC-канал | 18 | ЕÑли Ð²Ð°Ñ Ð¸Ð½Ñ‚ÐµÑ€ÐµÑует Ñоздание пакета sway Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ диÑтрибутива, зайдите на [IRC-канал][IRC channel] |
23 | или обратитеÑÑŒ на sir@cmpwn.com за Ñоветом. | 19 | или отправьте пиÑьмо на sir@cmpwn.com за Ñоветом. |
24 | 20 | ||
25 | ### Сборка из иÑходников | 21 | ### Сборка из иÑходников |
26 | 22 | ||
23 | ПоÑетите [Ñту Ñтраницу на вики][Development setup], еÑли вы хотите поÑтроить поÑледнюю верÑию | ||
24 | sway и wlroots Ð´Ð»Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ разработки. | ||
25 | |||
27 | УÑтановите завиÑимоÑти: | 26 | УÑтановите завиÑимоÑти: |
28 | 27 | ||
29 | * meson \* | 28 | * meson \* |
30 | * [wlroots](https://github.com/swaywm/wlroots) | 29 | * [wlroots] |
31 | * wayland | 30 | * wayland |
32 | * wayland-protocols \* | 31 | * wayland-protocols \* |
33 | * pcre | 32 | * pcre2 |
34 | * json-c | 33 | * json-c |
35 | * pango | 34 | * pango |
36 | * cairo | 35 | * cairo |
37 | * gdk-pixbuf2 (необÑзательно: Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ треÑ) | 36 | * gdk-pixbuf2 (опционально: Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ треÑ) |
38 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (необÑзательно: Ð´Ð»Ñ Ñборки man-Ñтраниц) \* | 37 | * [scdoc] (опционально: Ð´Ð»Ñ man-Ñтраниц) \* |
39 | * git \* | 38 | * git (опционально: Ð´Ð»Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ о верÑии) \* |
40 | 39 | ||
41 | _\*ЗавиÑимоÑти Ð´Ð»Ñ Ñборки_ | 40 | _\*ЗавиÑимоÑти Ð´Ð»Ñ Ñборки_ |
42 | 41 | ||
@@ -46,12 +45,6 @@ _\*ЗавиÑимоÑти Ð´Ð»Ñ Ñборки_ | |||
46 | ninja -C build | 45 | ninja -C build |
47 | sudo ninja -C build install | 46 | sudo ninja -C build install |
48 | 47 | ||
49 | Ðа ÑиÑтемах без logind вам понадобитÑÑ Ð´Ð¾Ð±Ð°Ð²Ð¸Ñ‚ÑŒ suid к файлу программы sway: | ||
50 | |||
51 | sudo chmod a+s /usr/local/bin/sway | ||
52 | |||
53 | sway ÑброÑит root-права при запуÑке. | ||
54 | |||
55 | ## ÐаÑтройка | 48 | ## ÐаÑтройка |
56 | 49 | ||
57 | ЕÑли вы уже иÑпользуете i3, Ñкопируйте ваш конфигурационный файл i3 в `~/.config/sway/config`, и | 50 | ЕÑли вы уже иÑпользуете i3, Ñкопируйте ваш конфигурационный файл i3 в `~/.config/sway/config`, и |
@@ -63,3 +56,13 @@ sway ÑброÑит root-права при запуÑке. | |||
63 | 56 | ||
64 | Выполните команду `sway` прÑмо из TTY. Ðекоторые диÑплейные менеджеры могут работать, но они не поддерживаютÑÑ Ñо Ñтороны | 57 | Выполните команду `sway` прÑмо из TTY. Ðекоторые диÑплейные менеджеры могут работать, но они не поддерживаютÑÑ Ñо Ñтороны |
65 | sway (gdm работает довольно неплохо). | 58 | sway (gdm работает довольно неплохо). |
59 | |||
60 | [i3]: https://i3wm.org/ | ||
61 | [Wayland]: http://wayland.freedesktop.org/ | ||
62 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
63 | [IRC channel]: 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 | [Development setup]: 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.sv.md b/README.sv.md new file mode 100644 index 00000000..c50ca068 --- /dev/null +++ b/README.sv.md | |||
@@ -0,0 +1,83 @@ | |||
1 | # sway | ||
2 | |||
3 | [English][en] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - **[Svenska][sv]** - [Ελληνικά][gr] - [Magyar][hu] - [Ùارسی][ir] - [Italiano][it] - [日本語][ja] - [í•œêµì–´][ko] - [Nederlands][nl] - [Polski][pl] - [Português][pt] - [Română][ro] - [РуÑÑкий][ru] - [Türkçe][tr] - [УкраїнÑька][uk] - [ä¸æ–‡-简体][zh-CN] - [ä¸æ–‡-ç¹é«”][zh-TW] | ||
4 | |||
5 | sway är en [i3]-kompatibel [Wayland] compositor. Läs våran [FAQ]-sida. Gå med i vår | ||
6 | [IRC-kanal] \(#sway på irc.libera.chat). | ||
7 | |||
8 | ## Utgåvosignaturer | ||
9 | |||
10 | Utgåvor är signerade med [E88F5E48] och publicerade på [GitHub][GitHub releases]. | ||
11 | |||
12 | ## Installering | ||
13 | |||
14 | ### Med pakethanterare | ||
15 | |||
16 | Sway är tillgänglig i många distributioner. Prova att installera "sway" med din distributions pakethanterare. | ||
17 | |||
18 | ### Genom att kompilera från källkod | ||
19 | |||
20 | Kolla in [denna wiki-sida][Development setup] om du vill bygga sway och wlroots HEAD för testning eller utveckling. | ||
21 | |||
22 | Installera paket som sway behöver: | ||
23 | |||
24 | * meson \* | ||
25 | * [wlroots] | ||
26 | * wayland | ||
27 | * wayland-protocols \* | ||
28 | * pcre2 | ||
29 | * json-c | ||
30 | * pango | ||
31 | * cairo | ||
32 | * gdk-pixbuf2 (valbar: systembricka) | ||
33 | * [scdoc] (valbar: manualer) \* | ||
34 | * git (valbar: versioninfo) \* | ||
35 | |||
36 | _\* Krav för kompilering_ | ||
37 | |||
38 | Kör dessa kommandon: | ||
39 | |||
40 | meson build/ | ||
41 | ninja -C build/ | ||
42 | sudo ninja -C build/ install | ||
43 | |||
44 | ## Konfiguration | ||
45 | |||
46 | Ifall du redan använder i3 så kan du kopiera din konfigurationsfil till `~/.config/sway/config` och det kommer då att fungera som det ska. | ||
47 | Kopiera annars exemplarkonfigurationsfilen till `~/.config/sway/config`. Den ligger oftast i `/etc/sway/config`. | ||
48 | Kör `man 5 sway` för mer information kring konfigurationen. | ||
49 | |||
50 | ## Att köra sway | ||
51 | |||
52 | Kör `sway` från en TTY. Vissa inloggningahanterare kan fungera men inte vara stödda av sway (gdm ska fungera hyfsat bra). | ||
53 | |||
54 | [en]: https://github.com/swaywm/sway#readme | ||
55 | [de]: README.de.md | ||
56 | [dk]: README.dk.md | ||
57 | [es]: README.es.md | ||
58 | [fr]: README.fr.md | ||
59 | [sv]: README.sv.md | ||
60 | [gr]: README.gr.md | ||
61 | [hu]: README.hu.md | ||
62 | [ir]: README.ir.md | ||
63 | [it]: README.it.md | ||
64 | [ja]: README.ja.md | ||
65 | [ko]: README.ko.md | ||
66 | [nl]: README.nl.md | ||
67 | [pl]: README.pl.md | ||
68 | [pt]: README.pt.md | ||
69 | [ro]: README.ro.md | ||
70 | [ru]: README.ru.md | ||
71 | [tr]: README.tr.md | ||
72 | [uk]: README.uk.md | ||
73 | [zh-CN]: README.zh-CN.md | ||
74 | [zh-TW]: README.zh-TW.md | ||
75 | [i3]: https://i3wm.org/ | ||
76 | [Wayland]: http://wayland.freedesktop.org/ | ||
77 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
78 | [IRC-kanal]: https://web.libera.chat/gamja/?channels=#sway | ||
79 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
80 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
81 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
82 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
83 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.tr.md b/README.tr.md new file mode 100644 index 00000000..40de1474 --- /dev/null +++ b/README.tr.md | |||
@@ -0,0 +1,62 @@ | |||
1 | # sway | ||
2 | |||
3 | |||
4 | Sway, [i3]-uyumlu bir [Wayland] dizgicisidir. [SSS][FAQ]'yi okuyun. | ||
5 | [IRC kanalı][IRC channel]na katılın \(irc.libera.chat'te #sway (İngilizce)). | ||
6 | |||
7 | ## Sürüm imzaları | ||
8 | |||
9 | Sürümler [E88F5E48] ile imzalandı ve [GitHub][GitHub releases]'da yayınlandı. | ||
10 | |||
11 | ## Kurulum | ||
12 | |||
13 | ### Paketler ile | ||
14 | |||
15 | Sway birçok dağıtımda mevcuttur. Sizinki için "sway" paketini yüklemeyi deneyin. | ||
16 | |||
17 | Dağıtımınız için sway'i paketlemekle ilgileniyorsanız, IRC kanalına uğrayın veya tavsiye için sir@cmpwn.com adresine bir e-posta gönderin. | ||
18 | |||
19 | ### Kaynak koddan derleme | ||
20 | |||
21 | Test veya geliştirme için sway ve wlroots'un HEAD'ini oluşturmak istiyorsanız [bu wiki sayfası][Development setup]na göz atın. | ||
22 | |||
23 | Aşağıdaki bağımlılıkları yükleyin: | ||
24 | |||
25 | * meson \* | ||
26 | * [wlroots] | ||
27 | * wayland | ||
28 | * wayland-protocols \* | ||
29 | * pcre2 | ||
30 | * json-c | ||
31 | * pango | ||
32 | * cairo | ||
33 | * gdk-pixbuf2 (isteğe bağlı: system tray) | ||
34 | * [scdoc] (isteğe bağlı: man pages) \* | ||
35 | * git (isteğe bağlı: version info) \* | ||
36 | |||
37 | _\*Derleme-anı bağımlılıkları_ | ||
38 | |||
39 | Şu komutları çalıştırın: | ||
40 | |||
41 | meson build | ||
42 | ninja -C build | ||
43 | sudo ninja -C build install | ||
44 | |||
45 | ## Yapılandırma | ||
46 | |||
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. | ||
48 | Yapılandırma hakkında bilgi almak için `man 5 sway` komutunu çalıştırın. | ||
49 | |||
50 | ## Çalıştırma | ||
51 | |||
52 | TTY'den `sway` çalıştırın. Bazı görüntü yöneticileriyle(display manager) çalışabilir ama Sway tarafından desteklenmez. (gdm'nin oldukça iyi çalıştığı bilinmektedir.) | ||
53 | |||
54 | [i3]: https://i3wm.org/ | ||
55 | [Wayland]: http://wayland.freedesktop.org/ | ||
56 | [FAQ]: https://github.com/swaywm/sway/wiki | ||
57 | [IRC channel]: https://web.libera.chat/gamja/?channels=#sway | ||
58 | [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 | ||
59 | [GitHub releases]: https://github.com/swaywm/sway/releases | ||
60 | [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup | ||
61 | [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots | ||
62 | [scdoc]: https://git.sr.ht/~sircmpwn/scdoc | ||
diff --git a/README.uk.md b/README.uk.md index 95047cb8..33359cff 100644 --- a/README.uk.md +++ b/README.uk.md | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | Sway це ÑуміÑний з [i3](https://i3wm.org/) композитор [Wayland](http://wayland.freedesktop.org/). | 3 | Sway це ÑуміÑний з [i3](https://i3wm.org/) композитор [Wayland](http://wayland.freedesktop.org/). |
4 | ОзнайомтеÑÑŒ з [ЧаПами](https://github.com/swaywm/sway/wiki). ПриєднуйтеÑÑŒ до [Ñпільноти в | 4 | ОзнайомтеÑÑŒ з [ЧаПами](https://github.com/swaywm/sway/wiki). ПриєднуйтеÑÑŒ до [Ñпільноти в |
5 | IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на | 5 | IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway на |
6 | irc.freenode.net). | 6 | irc.libera.chat). |
7 | 7 | ||
8 | ## Підтримка українÑькою мовою | 8 | ## Підтримка українÑькою мовою |
9 | 9 | ||
@@ -15,7 +15,7 @@ Hummer12007 у IRC-Ñпільноті. Будьте терплÑчі, вам оР| |||
15 | 15 | ||
16 | ## ПідпиÑи випуÑків | 16 | ## ПідпиÑи випуÑків |
17 | 17 | ||
18 | ВипуÑки підпиÑані ключем [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) | 18 | ВипуÑки підпиÑані ключем [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) |
19 | та публікуютьÑÑ Ð½Ð° Ñторінці [GitHub](https://github.com/swaywm/sway/releases). | 19 | та публікуютьÑÑ Ð½Ð° Ñторінці [GitHub](https://github.com/swaywm/sway/releases). |
20 | 20 | ||
21 | ## Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ | 21 | ## Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ |
@@ -36,10 +36,10 @@ Sway доÑтупний у багатьох диÑтрибутивах Linux (а | |||
36 | Ð’Ñтановіть залежноÑÑ‚Ñ–: | 36 | Ð’Ñтановіть залежноÑÑ‚Ñ–: |
37 | 37 | ||
38 | * meson \* | 38 | * meson \* |
39 | * [wlroots](https://github.com/swaywm/wlroots) | 39 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
40 | * wayland | 40 | * wayland |
41 | * wayland-protocols \* | 41 | * wayland-protocols \* |
42 | * pcre | 42 | * pcre2 |
43 | * json-c | 43 | * json-c |
44 | * pango | 44 | * pango |
45 | * cairo | 45 | * cairo |
@@ -55,12 +55,6 @@ _\*Лише Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¿Ñ–Ð»Ñції_ | |||
55 | ninja -C build | 55 | ninja -C build |
56 | sudo ninja -C build install | 56 | sudo ninja -C build install |
57 | 57 | ||
58 | Ðа ÑиÑтемах без logind, необхідно вÑтановити біт SUID на виконуваний файл sway: | ||
59 | |||
60 | sudo chmod a+s /usr/local/bin/sway | ||
61 | |||
62 | Sway втратить права доÑтупу root незабаром піÑÐ»Ñ Ð·Ð°Ð¿ÑƒÑку. | ||
63 | |||
64 | ## ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ | 58 | ## ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ |
65 | 59 | ||
66 | Якщо ви вже викориÑтовуєте i3, Ñкопіюйте Ñвій файл налаштувань | 60 | Якщо ви вже викориÑтовуєте i3, Ñкопіюйте Ñвій файл налаштувань |
diff --git a/README.zh-CN.md b/README.zh-CN.md index 9a3337ce..a6f4518a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md | |||
@@ -1,63 +1,50 @@ | |||
1 | # sway | 1 | # sway |
2 | 2 | ||
3 | sway 是和 [i3](https://i3wm.org/) 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor. | 3 | sway 是和 [i3](https://i3wm.org/) 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor。 |
4 | 阅读 [FAQ](https://github.com/swaywm/sway/wiki). åŠ å…¥ [IRC | 4 | [查看FAQ](https://github.com/swaywm/sway/wiki)/ [åŠ å…¥IRC频é“](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat) |
5 | 频é“](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on | ||
6 | irc.freenode.net). | ||
7 | 5 | ||
8 | ## å‘布ç¾å | 6 | ## å‘è¡Œç¾å |
9 | 7 | ||
10 | å‘布是以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) ç¾å | 8 | æ¯ä¸ªå‘行版都以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 的密钥ç¾å并å‘布在 [GitHub](https://github.com/swaywm/sway/releases)上。 |
11 | 并å‘布在 [GitHub](https://github.com/swaywm/sway/releases). | ||
12 | 9 | ||
13 | ## 安装 | 10 | ## 安装 |
14 | 11 | ||
15 | ### ä»Žè½¯ä»¶åŒ…ä¸ | 12 | ### 从包管ç†å™¨å®‰è£… |
16 | 13 | ||
17 | Sway 在很多å‘行版ä¸å¯ç”¨. å°è¯•åœ¨ä½ çš„å‘行版ä¸å®‰è£… "sway" 包. | 14 | Sway 在很多å‘行版ä¸å¯ç”¨ã€‚请å°è¯•åœ¨ä½ çš„å‘行版ä¸å®‰è£… `sway` 。 |
18 | 如果这ä¸å¯ç”¨, 请到 [æ¤ wiki 页](https://github.com/swaywm/sway/wiki/Unsupported-packages) | ||
19 | æ£€æŸ¥é’ˆå¯¹ä½ çš„å‘行版关于安装的信æ¯. | ||
20 | 15 | ||
21 | å¦‚æžœä½ æœ‰å…´è¶£ç»™ä½ çš„å‘行版打包 sway, åœä¸‹æ¥åˆ° IRC 频é“或者å‘邮件至 sir@cmpwn.com 获å–建议. | 16 | ### 从æºç 编译 |
22 | 17 | ||
23 | ### 从æºä»£ç 编译 | 18 | 如果想è¦æž„建最新版swayå’Œwlroots用以测试和开å‘,请查看 [æ¤wiki页é¢](https://github.com/swaywm/sway/wiki/Development-Setup) |
24 | 19 | ||
25 | 安装ä¾èµ–: | 20 | 安装如下ä¾èµ–: |
26 | 21 | ||
27 | * meson \* | 22 | * meson \* |
28 | * [wlroots](https://github.com/swaywm/wlroots) | 23 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
29 | * wayland | 24 | * wayland |
30 | * wayland-protocols \* | 25 | * wayland-protocols \* |
31 | * pcre | 26 | * pcre2 |
32 | * json-c | 27 | * json-c |
33 | * pango | 28 | * pango |
34 | * cairo | 29 | * cairo |
35 | * gdk-pixbuf2 (å¯é€‰çš„: system tray) | 30 | * gdk-pixbuf2 (å¯é€‰çš„: system tray) |
36 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (å¯é€‰çš„: man pages) \* | 31 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (å¯é€‰: man pages) \* |
37 | * git \* | 32 | * git \* |
38 | 33 | ||
39 | _\*编译时ä¾èµ–_ | 34 | _\*编译时ä¾èµ–_ |
40 | 35 | ||
41 | è¿è¡Œè¿™äº›å‘½ä»¤: | 36 | è¿è¡Œå¦‚下命令: |
42 | 37 | ||
43 | meson build | 38 | meson build/ |
44 | ninja -C build | 39 | ninja -C build/ |
45 | sudo ninja -C build install | 40 | sudo ninja -C build/ install |
46 | |||
47 | 在没有 logind 的系统上, ä½ éœ€è¦ç»™ sway 二进制设置 suid: | ||
48 | |||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | |||
51 | Sway 将会在å¯åŠ¨åŽå°½å¿«ä¸¢æŽ‰ root æƒé™. | ||
52 | 41 | ||
53 | ## é…ç½® | 42 | ## é…ç½® |
54 | 43 | ||
55 | å¦‚æžœä½ å·²ç»åœ¨ä½¿ç”¨ i3, 接下æ¥å¤åˆ¶ä½ çš„ i3 é…置到 `~/.config/sway/config` | 44 | å¦‚æžœä½ å·²ç»åœ¨ä½¿ç”¨i3,直接å¤åˆ¶i3é…置文件到 `~/.config/sway/config`,这是开箱å³ç”¨çš„ã€‚æˆ–è€…ï¼Œä½ å¯ä»¥å¤åˆ¶é…ç½®æ ·ä¾‹åˆ°`~/.config/sway/config`。它通常ä½äºŽ `/etc/sway/config`。 |
56 | 它å¯ä»¥ç›´æŽ¥å·¥ä½œ. 或者, å¤åˆ¶æ ·æœ¬é…置文件到 | 45 | è¿è¡Œ `man 5 sway` 获å–关于é…置的更多信æ¯ã€‚ |
57 | `~/.config/sway/config`. 它通常ä½äºŽ `/etc/sway/config`. | ||
58 | è¿è¡Œ `man 5 sway` 获å–关于é…置的信æ¯. | ||
59 | 46 | ||
60 | ## è¿è¡Œ | 47 | ## è¿è¡Œ |
61 | 48 | ||
62 | 从 TTY ä¸è¿è¡Œ `sway` . æŸäº›æ˜¾ç¤ºç®¡ç†å™¨å¯èƒ½ä¼šå·¥ä½œä½†å¹¶ä¸è¢« sway æ”¯æŒ | 49 | 从 TTY ä¸è¿è¡Œ `sway`。 æŸäº›æ˜¾ç¤ºç®¡ç†å™¨ï¼ˆDisplay Manager)也许å¯ä»¥å·¥ä½œä½†ä¸è¢« sway 支æŒã€‚ |
63 | (已知的 gdm 工作得éžå¸¸å¥½). | 50 | (已知 gdm 工作得éžå¸¸å¥½)。 |
diff --git a/README.zh-TW.md b/README.zh-TW.md index 13a9d2f4..2de2f63f 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md | |||
@@ -2,12 +2,12 @@ | |||
2 | 2 | ||
3 | sway 是一個與 [i3](https://i3wm.org/) 相容的 [Wayland](http://wayland.freedesktop.org/) compositor。 | 3 | sway 是一個與 [i3](https://i3wm.org/) 相容的 [Wayland](http://wayland.freedesktop.org/) compositor。 |
4 | 閱讀 [FAQ](https://github.com/swaywm/sway/wiki)。 åŠ å…¥ [IRC | 4 | 閱讀 [FAQ](https://github.com/swaywm/sway/wiki)。 åŠ å…¥ [IRC |
5 | é »é“](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on | 5 | é »é“](https://web.libera.chat/gamja/?channels=#sway) (#sway on |
6 | irc.freenode.net) | 6 | irc.libera.chat) |
7 | 7 | ||
8 | ## ç™¼è¡Œç°½ç« | 8 | ## ç™¼è¡Œç°½ç« |
9 | 9 | ||
10 | 所有發行的版本都會以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 簽署 | 10 | 所有發行的版本都會以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 簽署 |
11 | 並發佈於 [GitHub](https://github.com/swaywm/sway/releases) | 11 | 並發佈於 [GitHub](https://github.com/swaywm/sway/releases) |
12 | 12 | ||
13 | ## å®‰è£ | 13 | ## å®‰è£ |
@@ -25,10 +25,10 @@ Sway 在許多發行版都有æä¾›ã€‚è«‹è‡ªå·±å˜—è©¦æ–¼ä½ çš„ç™¼è¡Œç‰ˆå®‰è£ ã€ | |||
25 | 相ä¾å¥—件: | 25 | 相ä¾å¥—件: |
26 | 26 | ||
27 | * meson \* | 27 | * meson \* |
28 | * [wlroots](https://github.com/swaywm/wlroots) | 28 | * [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) |
29 | * wayland | 29 | * wayland |
30 | * wayland-protocols \* | 30 | * wayland-protocols \* |
31 | * pcre | 31 | * pcre2 |
32 | * json-c | 32 | * json-c |
33 | * pango | 33 | * pango |
34 | * cairo | 34 | * cairo |
@@ -44,12 +44,6 @@ _\*ç·¨è¯æ™‚相ä¾_ | |||
44 | ninja -C build | 44 | ninja -C build |
45 | sudo ninja -C build install | 45 | sudo ninja -C build install |
46 | 46 | ||
47 | 在沒有 logind çš„ç³»çµ±ä¸Šï¼Œä½ éœ€è¦ç‚º sway çš„åŸ·è¡Œæª”åŠ ä¸Š suid。 | ||
48 | |||
49 | sudo chmod a+s /usr/local/bin/sway | ||
50 | |||
51 | Sway 在啟動ä¸ä¹…後就會放棄 root 權é™ã€‚ | ||
52 | |||
53 | ## è¨å®šæª” | 47 | ## è¨å®šæª” |
54 | 48 | ||
55 | å¦‚æžœä½ å·²ç¶“åœ¨ä½¿ç”¨ i3ï¼Œä½ å¯ä»¥ç›´æŽ¥å°‡ä½ çš„ i3 è¨å®šæª”複製到 `~/.config/sway/config` 然後就能直接使用。 | 49 | å¦‚æžœä½ å·²ç¶“åœ¨ä½¿ç”¨ i3ï¼Œä½ å¯ä»¥ç›´æŽ¥å°‡ä½ çš„ i3 è¨å®šæª”複製到 `~/.config/sway/config` 然後就能直接使用。 |
diff --git a/client/pool-buffer.c b/client/pool-buffer.c index fd500c49..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/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/cairo.c b/common/cairo.c index 403dcf49..7c59d48c 100644 --- a/common/cairo.c +++ b/common/cairo.c | |||
@@ -1,6 +1,6 @@ | |||
1 | #include <stdint.h> | 1 | #include <stdint.h> |
2 | #include <cairo/cairo.h> | 2 | #include <cairo.h> |
3 | #include "cairo.h" | 3 | #include "cairo_util.h" |
4 | 4 | ||
5 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { | 5 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { |
6 | cairo_set_source_rgba(cairo, | 6 | cairo_set_source_rgba(cairo, |
diff --git a/common/gesture.c b/common/gesture.c new file mode 100644 index 00000000..272aa837 --- /dev/null +++ b/common/gesture.c | |||
@@ -0,0 +1,332 @@ | |||
1 | #include "gesture.h" | ||
2 | |||
3 | #include <math.h> | ||
4 | #include <stdarg.h> | ||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <string.h> | ||
8 | #include "list.h" | ||
9 | #include "log.h" | ||
10 | #include "stringop.h" | ||
11 | |||
12 | const uint8_t GESTURE_FINGERS_ANY = 0; | ||
13 | |||
14 | char *gesture_parse(const char *input, struct gesture *output) { | ||
15 | // Clear output in case of failure | ||
16 | output->type = GESTURE_TYPE_NONE; | ||
17 | output->fingers = GESTURE_FINGERS_ANY; | ||
18 | output->directions = GESTURE_DIRECTION_NONE; | ||
19 | |||
20 | // Split input type, fingers and directions | ||
21 | list_t *split = split_string(input, ":"); | ||
22 | if (split->length < 1 || split->length > 3) { | ||
23 | return format_str( | ||
24 | "expected <gesture>[:<fingers>][:direction], got %s", | ||
25 | input); | ||
26 | } | ||
27 | |||
28 | // Parse gesture type | ||
29 | if (strcmp(split->items[0], "hold") == 0) { | ||
30 | output->type = GESTURE_TYPE_HOLD; | ||
31 | } else if (strcmp(split->items[0], "pinch") == 0) { | ||
32 | output->type = GESTURE_TYPE_PINCH; | ||
33 | } else if (strcmp(split->items[0], "swipe") == 0) { | ||
34 | output->type = GESTURE_TYPE_SWIPE; | ||
35 | } else { | ||
36 | return format_str("expected hold|pinch|swipe, got %s", | ||
37 | (const char *)split->items[0]); | ||
38 | } | ||
39 | |||
40 | // Parse optional arguments | ||
41 | if (split->length > 1) { | ||
42 | char *next = split->items[1]; | ||
43 | |||
44 | // Try to parse as finger count (1-9) | ||
45 | if (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') { | ||
46 | output->fingers = atoi(next); | ||
47 | |||
48 | // Move to next if available | ||
49 | next = split->length == 3 ? split->items[2] : NULL; | ||
50 | } else if (split->length == 3) { | ||
51 | // Fail here if argument can only be finger count | ||
52 | return format_str("expected 1-9, got %s", next); | ||
53 | } | ||
54 | |||
55 | // If there is an argument left, try to parse as direction | ||
56 | if (next) { | ||
57 | list_t *directions = split_string(next, "+"); | ||
58 | |||
59 | for (int i = 0; i < directions->length; ++i) { | ||
60 | const char *item = directions->items[i]; | ||
61 | if (strcmp(item, "any") == 0) { | ||
62 | continue; | ||
63 | } else if (strcmp(item, "up") == 0) { | ||
64 | output->directions |= GESTURE_DIRECTION_UP; | ||
65 | } else if (strcmp(item, "down") == 0) { | ||
66 | output->directions |= GESTURE_DIRECTION_DOWN; | ||
67 | } else if (strcmp(item, "left") == 0) { | ||
68 | output->directions |= GESTURE_DIRECTION_LEFT; | ||
69 | } else if (strcmp(item, "right") == 0) { | ||
70 | output->directions |= GESTURE_DIRECTION_RIGHT; | ||
71 | } else if (strcmp(item, "inward") == 0) { | ||
72 | output->directions |= GESTURE_DIRECTION_INWARD; | ||
73 | } else if (strcmp(item, "outward") == 0) { | ||
74 | output->directions |= GESTURE_DIRECTION_OUTWARD; | ||
75 | } else if (strcmp(item, "clockwise") == 0) { | ||
76 | output->directions |= GESTURE_DIRECTION_CLOCKWISE; | ||
77 | } else if (strcmp(item, "counterclockwise") == 0) { | ||
78 | output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; | ||
79 | } else { | ||
80 | return format_str("expected direction, got %s", item); | ||
81 | } | ||
82 | } | ||
83 | list_free_items_and_destroy(directions); | ||
84 | } | ||
85 | } // if optional args | ||
86 | |||
87 | list_free_items_and_destroy(split); | ||
88 | |||
89 | return NULL; | ||
90 | } | ||
91 | |||
92 | const char *gesture_type_string(enum gesture_type type) { | ||
93 | switch (type) { | ||
94 | case GESTURE_TYPE_NONE: | ||
95 | return "none"; | ||
96 | case GESTURE_TYPE_HOLD: | ||
97 | return "hold"; | ||
98 | case GESTURE_TYPE_PINCH: | ||
99 | return "pinch"; | ||
100 | case GESTURE_TYPE_SWIPE: | ||
101 | return "swipe"; | ||
102 | } | ||
103 | |||
104 | return NULL; | ||
105 | } | ||
106 | |||
107 | const char *gesture_direction_string(enum gesture_direction direction) { | ||
108 | switch (direction) { | ||
109 | case GESTURE_DIRECTION_NONE: | ||
110 | return "none"; | ||
111 | case GESTURE_DIRECTION_UP: | ||
112 | return "up"; | ||
113 | case GESTURE_DIRECTION_DOWN: | ||
114 | return "down"; | ||
115 | case GESTURE_DIRECTION_LEFT: | ||
116 | return "left"; | ||
117 | case GESTURE_DIRECTION_RIGHT: | ||
118 | return "right"; | ||
119 | case GESTURE_DIRECTION_INWARD: | ||
120 | return "inward"; | ||
121 | case GESTURE_DIRECTION_OUTWARD: | ||
122 | return "outward"; | ||
123 | case GESTURE_DIRECTION_CLOCKWISE: | ||
124 | return "clockwise"; | ||
125 | case GESTURE_DIRECTION_COUNTERCLOCKWISE: | ||
126 | return "counterclockwise"; | ||
127 | } | ||
128 | |||
129 | return NULL; | ||
130 | } | ||
131 | |||
132 | // Helper to turn combination of directions flags into string representation. | ||
133 | static char *gesture_directions_to_string(uint32_t directions) { | ||
134 | char *result = NULL; | ||
135 | |||
136 | for (uint8_t bit = 0; bit < 32; bit++) { | ||
137 | uint32_t masked = directions & (1 << bit); | ||
138 | if (masked) { | ||
139 | const char *name = gesture_direction_string(masked); | ||
140 | |||
141 | if (!name) { | ||
142 | name = "unknown"; | ||
143 | } | ||
144 | |||
145 | if (!result) { | ||
146 | result = strdup(name); | ||
147 | } else { | ||
148 | char *new = format_str("%s+%s", result, name); | ||
149 | free(result); | ||
150 | result = new; | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | if(!result) { | ||
156 | return strdup("any"); | ||
157 | } | ||
158 | |||
159 | return result; | ||
160 | } | ||
161 | |||
162 | char *gesture_to_string(struct gesture *gesture) { | ||
163 | char *directions = gesture_directions_to_string(gesture->directions); | ||
164 | char *result = format_str("%s:%u:%s", | ||
165 | gesture_type_string(gesture->type), | ||
166 | gesture->fingers, directions); | ||
167 | free(directions); | ||
168 | return result; | ||
169 | } | ||
170 | |||
171 | bool gesture_check(struct gesture *target, enum gesture_type type, uint8_t fingers) { | ||
172 | // Check that gesture type matches | ||
173 | if (target->type != type) { | ||
174 | return false; | ||
175 | } | ||
176 | |||
177 | // Check that finger count matches | ||
178 | if (target->fingers != GESTURE_FINGERS_ANY && target->fingers != fingers) { | ||
179 | return false; | ||
180 | } | ||
181 | |||
182 | return true; | ||
183 | } | ||
184 | |||
185 | bool gesture_match(struct gesture *target, struct gesture *to_match, bool exact) { | ||
186 | // Check type and fingers | ||
187 | if (!gesture_check(target, to_match->type, to_match->fingers)) { | ||
188 | return false; | ||
189 | } | ||
190 | |||
191 | // Enforce exact matches ... | ||
192 | if (exact && target->directions != to_match->directions) { | ||
193 | return false; | ||
194 | } | ||
195 | |||
196 | // ... or ensure all target directions are matched | ||
197 | return (target->directions & to_match->directions) == target->directions; | ||
198 | } | ||
199 | |||
200 | bool gesture_equal(struct gesture *a, struct gesture *b) { | ||
201 | return a->type == b->type | ||
202 | && a->fingers == b->fingers | ||
203 | && a->directions == b->directions; | ||
204 | } | ||
205 | |||
206 | // Return count of set bits in directions bit field. | ||
207 | static uint8_t gesture_directions_count(uint32_t directions) { | ||
208 | uint8_t count = 0; | ||
209 | for (; directions; directions >>= 1) { | ||
210 | count += directions & 1; | ||
211 | } | ||
212 | return count; | ||
213 | } | ||
214 | |||
215 | // Compare direction bit count of two direction. | ||
216 | static int8_t gesture_directions_compare(uint32_t a, uint32_t b) { | ||
217 | return gesture_directions_count(a) - gesture_directions_count(b); | ||
218 | } | ||
219 | |||
220 | // Compare two direction based on their direction bit count | ||
221 | int8_t gesture_compare(struct gesture *a, struct gesture *b) { | ||
222 | return gesture_directions_compare(a->directions, b->directions); | ||
223 | } | ||
224 | |||
225 | void gesture_tracker_begin(struct gesture_tracker *tracker, | ||
226 | enum gesture_type type, uint8_t fingers) { | ||
227 | tracker->type = type; | ||
228 | tracker->fingers = fingers; | ||
229 | |||
230 | tracker->dx = 0.0; | ||
231 | tracker->dy = 0.0; | ||
232 | tracker->scale = 1.0; | ||
233 | tracker->rotation = 0.0; | ||
234 | |||
235 | sway_log(SWAY_DEBUG, "begin tracking %s:%u:? gesture", | ||
236 | gesture_type_string(type), fingers); | ||
237 | } | ||
238 | |||
239 | bool gesture_tracker_check(struct gesture_tracker *tracker, enum gesture_type type) { | ||
240 | return tracker->type == type; | ||
241 | } | ||
242 | |||
243 | void gesture_tracker_update(struct gesture_tracker *tracker, | ||
244 | double dx, double dy, double scale, double rotation) { | ||
245 | if (tracker->type == GESTURE_TYPE_HOLD) { | ||
246 | sway_assert(false, "hold does not update."); | ||
247 | return; | ||
248 | } | ||
249 | |||
250 | tracker->dx += dx; | ||
251 | tracker->dy += dy; | ||
252 | |||
253 | if (tracker->type == GESTURE_TYPE_PINCH) { | ||
254 | tracker->scale = scale; | ||
255 | tracker->rotation += rotation; | ||
256 | } | ||
257 | |||
258 | sway_log(SWAY_DEBUG, "update tracking %s:%u:? gesture: %f %f %f %f", | ||
259 | gesture_type_string(tracker->type), | ||
260 | tracker->fingers, | ||
261 | tracker->dx, tracker->dy, | ||
262 | tracker->scale, tracker->rotation); | ||
263 | } | ||
264 | |||
265 | void gesture_tracker_cancel(struct gesture_tracker *tracker) { | ||
266 | sway_log(SWAY_DEBUG, "cancel tracking %s:%u:? gesture", | ||
267 | gesture_type_string(tracker->type), tracker->fingers); | ||
268 | |||
269 | tracker->type = GESTURE_TYPE_NONE; | ||
270 | } | ||
271 | |||
272 | struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) { | ||
273 | struct gesture *result = calloc(1, sizeof(struct gesture)); | ||
274 | |||
275 | // Ignore gesture under some threshold | ||
276 | // TODO: Make configurable | ||
277 | const double min_rotation = 5; | ||
278 | const double min_scale_delta = 0.1; | ||
279 | |||
280 | // Determine direction | ||
281 | switch(tracker->type) { | ||
282 | // Gestures with scale and rotation | ||
283 | case GESTURE_TYPE_PINCH: | ||
284 | if (tracker->rotation > min_rotation) { | ||
285 | result->directions |= GESTURE_DIRECTION_CLOCKWISE; | ||
286 | } | ||
287 | if (tracker->rotation < -min_rotation) { | ||
288 | result->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; | ||
289 | } | ||
290 | |||
291 | if (tracker->scale > (1.0 + min_scale_delta)) { | ||
292 | result->directions |= GESTURE_DIRECTION_OUTWARD; | ||
293 | } | ||
294 | if (tracker->scale < (1.0 - min_scale_delta)) { | ||
295 | result->directions |= GESTURE_DIRECTION_INWARD; | ||
296 | } | ||
297 | __attribute__ ((fallthrough)); | ||
298 | // Gestures with dx and dy | ||
299 | case GESTURE_TYPE_SWIPE: | ||
300 | if (fabs(tracker->dx) > fabs(tracker->dy)) { | ||
301 | if (tracker->dx > 0) { | ||
302 | result->directions |= GESTURE_DIRECTION_RIGHT; | ||
303 | } else { | ||
304 | result->directions |= GESTURE_DIRECTION_LEFT; | ||
305 | } | ||
306 | } else { | ||
307 | if (tracker->dy > 0) { | ||
308 | result->directions |= GESTURE_DIRECTION_DOWN; | ||
309 | } else { | ||
310 | result->directions |= GESTURE_DIRECTION_UP; | ||
311 | } | ||
312 | } | ||
313 | // Gesture without any direction | ||
314 | case GESTURE_TYPE_HOLD: | ||
315 | break; | ||
316 | // Not tracking any gesture | ||
317 | case GESTURE_TYPE_NONE: | ||
318 | sway_assert(false, "Not tracking any gesture."); | ||
319 | return result; | ||
320 | } | ||
321 | |||
322 | result->type = tracker->type; | ||
323 | result->fingers = tracker->fingers; | ||
324 | |||
325 | char *description = gesture_to_string(result); | ||
326 | sway_log(SWAY_DEBUG, "end tracking gesture: %s", description); | ||
327 | free(description); | ||
328 | |||
329 | tracker->type = GESTURE_TYPE_NONE; | ||
330 | |||
331 | return result; | ||
332 | } | ||
diff --git a/common/ipc-client.c b/common/ipc-client.c index d30212d2..a0be2b2d 100644 --- a/common/ipc-client.c +++ b/common/ipc-client.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #include <stdint.h> | 2 | #include <stdint.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
diff --git a/common/log.c b/common/log.c index 483420e7..3eacdb34 100644 --- a/common/log.c +++ b/common/log.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200112L | ||
2 | #include <signal.h> | 1 | #include <signal.h> |
3 | #include <stdarg.h> | 2 | #include <stdarg.h> |
4 | #include <stdio.h> | 3 | #include <stdio.h> |
diff --git a/common/loop.c b/common/loop.c index 80fe18ea..b99c6d55 100644 --- a/common/loop.c +++ b/common/loop.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200112L | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
diff --git a/common/meson.build b/common/meson.build index c653dd72..c0ce1f68 100644 --- a/common/meson.build +++ b/common/meson.build | |||
@@ -1,8 +1,8 @@ | |||
1 | lib_sway_common = static_library( | 1 | lib_sway_common = static_library( |
2 | 'sway-common', | 2 | 'sway-common', |
3 | files( | 3 | files( |
4 | 'background-image.c', | ||
5 | 'cairo.c', | 4 | 'cairo.c', |
5 | 'gesture.c', | ||
6 | 'ipc-client.c', | 6 | 'ipc-client.c', |
7 | 'log.c', | 7 | 'log.c', |
8 | 'loop.c', | 8 | 'loop.c', |
@@ -13,7 +13,6 @@ lib_sway_common = static_library( | |||
13 | ), | 13 | ), |
14 | dependencies: [ | 14 | dependencies: [ |
15 | cairo, | 15 | cairo, |
16 | gdk_pixbuf, | ||
17 | pango, | 16 | pango, |
18 | pangocairo, | 17 | pangocairo, |
19 | wayland_client.partial_dependency(compile_args: true) | 18 | wayland_client.partial_dependency(compile_args: true) |
diff --git a/common/pango.c b/common/pango.c index fc3d0688..288569b3 100644 --- a/common/pango.c +++ b/common/pango.c | |||
@@ -1,4 +1,4 @@ | |||
1 | #include <cairo/cairo.h> | 1 | #include <cairo.h> |
2 | #include <pango/pangocairo.h> | 2 | #include <pango/pangocairo.h> |
3 | #include <stdarg.h> | 3 | #include <stdarg.h> |
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
@@ -6,7 +6,7 @@ | |||
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 "cairo.h" | 9 | #include "cairo_util.h" |
10 | #include "log.h" | 10 | #include "log.h" |
11 | #include "stringop.h" | 11 | #include "stringop.h" |
12 | 12 | ||
@@ -50,7 +50,7 @@ size_t escape_markup_text(const char *src, char *dest) { | |||
50 | return length; | 50 | return length; |
51 | } | 51 | } |
52 | 52 | ||
53 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, | 53 | PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, |
54 | const char *text, double scale, bool markup) { | 54 | const char *text, double scale, bool markup) { |
55 | PangoLayout *layout = pango_cairo_create_layout(cairo); | 55 | PangoLayout *layout = pango_cairo_create_layout(cairo); |
56 | PangoAttrList *attrs; | 56 | PangoAttrList *attrs; |
@@ -73,60 +73,59 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, | |||
73 | } | 73 | } |
74 | 74 | ||
75 | pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); | 75 | pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); |
76 | PangoFontDescription *desc = pango_font_description_from_string(font); | ||
77 | pango_layout_set_font_description(layout, desc); | 76 | pango_layout_set_font_description(layout, desc); |
78 | pango_layout_set_single_paragraph_mode(layout, 1); | 77 | pango_layout_set_single_paragraph_mode(layout, 1); |
79 | pango_layout_set_attributes(layout, attrs); | 78 | pango_layout_set_attributes(layout, attrs); |
80 | pango_attr_list_unref(attrs); | 79 | pango_attr_list_unref(attrs); |
81 | pango_font_description_free(desc); | ||
82 | return layout; | 80 | return layout; |
83 | } | 81 | } |
84 | 82 | ||
85 | void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, | 83 | void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, |
86 | int *baseline, double scale, bool markup, const char *fmt, ...) { | 84 | int *baseline, double scale, bool markup, const char *fmt, ...) { |
87 | va_list args; | 85 | va_list args; |
88 | va_start(args, fmt); | 86 | va_start(args, fmt); |
89 | // Add one since vsnprintf excludes null terminator. | 87 | char *buf = vformat_str(fmt, args); |
90 | int length = vsnprintf(NULL, 0, fmt, args) + 1; | ||
91 | va_end(args); | 88 | va_end(args); |
92 | |||
93 | char *buf = malloc(length); | ||
94 | if (buf == NULL) { | 89 | if (buf == NULL) { |
95 | sway_log(SWAY_ERROR, "Failed to allocate memory"); | ||
96 | return; | 90 | return; |
97 | } | 91 | } |
98 | va_start(args, fmt); | ||
99 | vsnprintf(buf, length, fmt, args); | ||
100 | va_end(args); | ||
101 | 92 | ||
102 | PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); | 93 | PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); |
103 | pango_cairo_update_layout(cairo, layout); | 94 | pango_cairo_update_layout(cairo, layout); |
104 | pango_layout_get_pixel_size(layout, width, height); | 95 | pango_layout_get_pixel_size(layout, width, height); |
105 | if (baseline) { | 96 | if (baseline) { |
106 | *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; | 97 | *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; |
107 | } | 98 | } |
108 | g_object_unref(layout); | 99 | g_object_unref(layout); |
100 | |||
109 | free(buf); | 101 | free(buf); |
110 | } | 102 | } |
111 | 103 | ||
112 | void pango_printf(cairo_t *cairo, const char *font, | 104 | void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) { |
105 | cairo_t *cairo = cairo_create(NULL); | ||
106 | PangoContext *pango = pango_cairo_create_context(cairo); | ||
107 | // When passing NULL as a language, pango uses the current locale. | ||
108 | PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL); | ||
109 | |||
110 | *baseline = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; | ||
111 | *height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE; | ||
112 | |||
113 | pango_font_metrics_unref(metrics); | ||
114 | g_object_unref(pango); | ||
115 | cairo_destroy(cairo); | ||
116 | } | ||
117 | |||
118 | void render_text(cairo_t *cairo, const PangoFontDescription *desc, | ||
113 | double scale, bool markup, const char *fmt, ...) { | 119 | double scale, bool markup, const char *fmt, ...) { |
114 | va_list args; | 120 | va_list args; |
115 | va_start(args, fmt); | 121 | va_start(args, fmt); |
116 | // Add one since vsnprintf excludes null terminator. | 122 | char *buf = vformat_str(fmt, args); |
117 | int length = vsnprintf(NULL, 0, fmt, args) + 1; | ||
118 | va_end(args); | 123 | va_end(args); |
119 | |||
120 | char *buf = malloc(length); | ||
121 | if (buf == NULL) { | 124 | if (buf == NULL) { |
122 | sway_log(SWAY_ERROR, "Failed to allocate memory"); | ||
123 | return; | 125 | return; |
124 | } | 126 | } |
125 | va_start(args, fmt); | ||
126 | vsnprintf(buf, length, fmt, args); | ||
127 | va_end(args); | ||
128 | 127 | ||
129 | PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); | 128 | PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); |
130 | cairo_font_options_t *fo = cairo_font_options_create(); | 129 | cairo_font_options_t *fo = cairo_font_options_create(); |
131 | cairo_get_font_options(cairo, fo); | 130 | cairo_get_font_options(cairo, fo); |
132 | pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); | 131 | pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); |
@@ -134,5 +133,6 @@ void pango_printf(cairo_t *cairo, const char *font, | |||
134 | pango_cairo_update_layout(cairo, layout); | 133 | pango_cairo_update_layout(cairo, layout); |
135 | pango_cairo_show_layout(cairo, layout); | 134 | pango_cairo_show_layout(cairo, layout); |
136 | g_object_unref(layout); | 135 | g_object_unref(layout); |
136 | |||
137 | free(buf); | 137 | free(buf); |
138 | } | 138 | } |
diff --git a/common/stringop.c b/common/stringop.c index 7fb3fe12..16d04917 100644 --- a/common/stringop.c +++ b/common/stringop.c | |||
@@ -1,5 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
2 | #include <stdarg.h> | ||
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include <stdio.h> | 4 | #include <stdio.h> |
5 | #include <stdlib.h> | 5 | #include <stdlib.h> |
@@ -328,3 +328,35 @@ bool expand_path(char **path) { | |||
328 | wordfree(&p); | 328 | wordfree(&p); |
329 | return true; | 329 | return true; |
330 | } | 330 | } |
331 | |||
332 | char *vformat_str(const char *fmt, va_list args) { | ||
333 | char *str = NULL; | ||
334 | va_list args_copy; | ||
335 | va_copy(args_copy, args); | ||
336 | |||
337 | int len = vsnprintf(NULL, 0, fmt, args); | ||
338 | if (len < 0) { | ||
339 | sway_log_errno(SWAY_ERROR, "vsnprintf(\"%s\") failed", fmt); | ||
340 | goto out; | ||
341 | } | ||
342 | |||
343 | str = malloc(len + 1); | ||
344 | if (str == NULL) { | ||
345 | sway_log_errno(SWAY_ERROR, "malloc() failed"); | ||
346 | goto out; | ||
347 | } | ||
348 | |||
349 | vsnprintf(str, len + 1, fmt, args_copy); | ||
350 | |||
351 | out: | ||
352 | va_end(args_copy); | ||
353 | return str; | ||
354 | } | ||
355 | |||
356 | char *format_str(const char *fmt, ...) { | ||
357 | va_list args; | ||
358 | va_start(args, fmt); | ||
359 | char *str = vformat_str(fmt, args); | ||
360 | va_end(args); | ||
361 | return str; | ||
362 | } | ||
diff --git a/common/util.c b/common/util.c index 5ea94f48..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> |
@@ -10,12 +9,6 @@ | |||
10 | #include "log.h" | 9 | #include "log.h" |
11 | #include "util.h" | 10 | #include "util.h" |
12 | 11 | ||
13 | uint32_t get_current_time_msec(void) { | ||
14 | struct timespec now; | ||
15 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
16 | return now.tv_sec * 1000 + now.tv_nsec / 1000000; | ||
17 | } | ||
18 | |||
19 | int wrap(int i, int max) { | 12 | int wrap(int i, int max) { |
20 | return ((i % max) + max) % max; | 13 | return ((i % max) + max) % max; |
21 | } | 14 | } |
@@ -86,6 +79,12 @@ enum movement_unit parse_movement_unit(const char *unit) { | |||
86 | 79 | ||
87 | int parse_movement_amount(int argc, char **argv, | 80 | int parse_movement_amount(int argc, char **argv, |
88 | struct movement_amount *amount) { | 81 | struct movement_amount *amount) { |
82 | if (!sway_assert(argc > 0, "Expected args in parse_movement_amount")) { | ||
83 | amount->amount = 0; | ||
84 | amount->unit = MOVEMENT_UNIT_INVALID; | ||
85 | return 0; | ||
86 | } | ||
87 | |||
89 | char *err; | 88 | char *err; |
90 | amount->amount = (int)strtol(argv[0], &err, 10); | 89 | amount->amount = (int)strtol(argv[0], &err, 10); |
91 | if (*err) { | 90 | if (*err) { |
diff --git a/completions/bash/sway b/completions/bash/sway index edd752cd..01b20073 100644 --- a/completions/bash/sway +++ b/completions/bash/sway | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | _sway() | 3 | _sway() |
4 | { | 4 | { |
5 | local cur prev | 5 | local cur prev short long |
6 | _get_comp_words_by_ref cur prev | 6 | _get_comp_words_by_ref cur prev |
7 | 7 | ||
8 | short=( | 8 | short=( |
diff --git a/completions/bash/swaybar b/completions/bash/swaybar index 1e085c65..3709d4f9 100644 --- a/completions/bash/swaybar +++ b/completions/bash/swaybar | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | _swaybar() | 3 | _swaybar() |
4 | { | 4 | { |
5 | local cur prev | 5 | local cur prev short long |
6 | _get_comp_words_by_ref cur prev | 6 | _get_comp_words_by_ref cur prev |
7 | 7 | ||
8 | short=( | 8 | short=( |
diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg index f865e4e1..30457751 100644 --- a/completions/bash/swaymsg +++ b/completions/bash/swaymsg | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | _swaymsg() | 3 | _swaymsg() |
4 | { | 4 | { |
5 | local cur prev | 5 | local cur prev types short long |
6 | _get_comp_words_by_ref cur prev | 6 | _get_comp_words_by_ref cur prev |
7 | 7 | ||
8 | types=( | 8 | types=( |
diff --git a/completions/meson.build b/completions/meson.build new file mode 100644 index 00000000..6bca9391 --- /dev/null +++ b/completions/meson.build | |||
@@ -0,0 +1,57 @@ | |||
1 | if get_option('zsh-completions') | ||
2 | zsh_files = files( | ||
3 | 'zsh/_sway', | ||
4 | 'zsh/_swaymsg', | ||
5 | ) | ||
6 | zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions') | ||
7 | |||
8 | install_data(zsh_files, install_dir: zsh_install_dir) | ||
9 | endif | ||
10 | |||
11 | if get_option('bash-completions') | ||
12 | bash_comp = dependency('bash-completion', required: false) | ||
13 | |||
14 | bash_files = files( | ||
15 | 'bash/sway', | ||
16 | 'bash/swaymsg', | ||
17 | ) | ||
18 | |||
19 | if get_option('swaybar') | ||
20 | bash_files += files('bash/swaybar') | ||
21 | endif | ||
22 | |||
23 | if bash_comp.found() | ||
24 | bash_install_dir = bash_comp.get_variable( | ||
25 | pkgconfig: 'completionsdir', | ||
26 | pkgconfig_define: ['datadir', datadir] | ||
27 | ) | ||
28 | else | ||
29 | bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') | ||
30 | endif | ||
31 | |||
32 | install_data(bash_files, install_dir: bash_install_dir) | ||
33 | endif | ||
34 | |||
35 | if get_option('fish-completions') | ||
36 | fish_comp = dependency('fish', required: false) | ||
37 | |||
38 | fish_files = files( | ||
39 | 'fish/sway.fish', | ||
40 | 'fish/swaymsg.fish', | ||
41 | ) | ||
42 | |||
43 | if get_option('swaynag') | ||
44 | fish_files += files('fish/swaynag.fish') | ||
45 | endif | ||
46 | |||
47 | if fish_comp.found() | ||
48 | fish_install_dir = fish_comp.get_variable( | ||
49 | pkgconfig: 'completionsdir', | ||
50 | pkgconfig_define: ['datadir', datadir] | ||
51 | ) | ||
52 | else | ||
53 | fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') | ||
54 | endif | ||
55 | |||
56 | install_data(fish_files, install_dir: fish_install_dir) | ||
57 | endif | ||
@@ -14,11 +14,11 @@ set $down j | |||
14 | set $up k | 14 | set $up k |
15 | set $right l | 15 | set $right l |
16 | # Your preferred terminal emulator | 16 | # Your preferred terminal emulator |
17 | set $term alacritty | 17 | set $term foot |
18 | # Your preferred application launcher | 18 | # Your preferred application launcher |
19 | # Note: pass the final command to swaymsg so that the resulting window can be opened | 19 | # Note: pass the final command to swaymsg so that the resulting window can be opened |
20 | # on the original workspace that the command was run on. | 20 | # on the original workspace that the command was run on. |
21 | set $menu dmenu_path | dmenu | xargs swaymsg exec -- | 21 | set $menu dmenu_path | wmenu | xargs swaymsg exec -- |
22 | 22 | ||
23 | ### Output configuration | 23 | ### Output configuration |
24 | # | 24 | # |
@@ -37,7 +37,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill | |||
37 | # | 37 | # |
38 | # exec swayidle -w \ | 38 | # exec swayidle -w \ |
39 | # timeout 300 'swaylock -f -c 000000' \ | 39 | # timeout 300 'swaylock -f -c 000000' \ |
40 | # timeout 600 'swaymsg "output * dpms off"' resume 'swaymsg "output * dpms on"' \ | 40 | # timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \ |
41 | # before-sleep 'swaylock -f -c 000000' | 41 | # before-sleep 'swaylock -f -c 000000' |
42 | # | 42 | # |
43 | # This will lock your screen after 300 seconds of inactivity, then turn off | 43 | # This will lock your screen after 300 seconds of inactivity, then turn off |
@@ -82,7 +82,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill | |||
82 | bindsym $mod+Shift+c reload | 82 | bindsym $mod+Shift+c reload |
83 | 83 | ||
84 | # Exit sway (logs you out of your Wayland session) | 84 | # Exit sway (logs you out of your Wayland session) |
85 | bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit' | 85 | bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit' |
86 | # | 86 | # |
87 | # Moving around: | 87 | # Moving around: |
88 | # | 88 | # |
@@ -205,7 +205,7 @@ bar { | |||
205 | 205 | ||
206 | # When the status_command prints a new line to stdout, swaybar updates. | 206 | # When the status_command prints a new line to stdout, swaybar updates. |
207 | # The default just shows the current date and time. | 207 | # The default just shows the current date and time. |
208 | status_command while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done | 208 | status_command while date +'%Y-%m-%d %X'; do sleep 1; done |
209 | 209 | ||
210 | colors { | 210 | colors { |
211 | statusline #ffffff | 211 | statusline #ffffff |
diff --git a/contrib/_incr_version b/contrib/_incr_version deleted file mode 100755 index a4fa2654..00000000 --- a/contrib/_incr_version +++ /dev/null | |||
@@ -1,21 +0,0 @@ | |||
1 | #!/bin/sh -eu | ||
2 | old_version="$1" | ||
3 | new_version="$2" | ||
4 | |||
5 | if [ "$new_version" != "${new_version#v}" ] | ||
6 | then | ||
7 | echo "Error: The new version shouldn't be prefixed with a \"v\"." >&2 | ||
8 | exit 1 | ||
9 | fi | ||
10 | |||
11 | sed -i meson.build -e "s/^ version: .*#release_version/ version: '$new_version', #release_version/g" | ||
12 | |||
13 | printf "Minimum wlroots version? " | ||
14 | read wlr_version_min | ||
15 | printf "Maximum wlroots version? " | ||
16 | read wlr_version_max | ||
17 | |||
18 | sed -i meson.build -e "s/wlroots_version =.*/wlroots_version = ['>=$wlr_version_min', '<$wlr_version_max']/" | ||
19 | |||
20 | git add meson.build | ||
21 | git commit -m "Update version to $new_version" | ||
diff --git a/contrib/autoname-workspaces.py b/contrib/autoname-workspaces.py deleted file mode 100755 index 297d91b2..00000000 --- a/contrib/autoname-workspaces.py +++ /dev/null | |||
@@ -1,130 +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 | app_id = window.app_id | ||
26 | if app_id is not None and len(app_id) > 0: | ||
27 | app_id = app_id.lower() | ||
28 | if app_id in WINDOW_ICONS: | ||
29 | return WINDOW_ICONS[app_id] | ||
30 | logging.info("No icon available for window with app_id: %s" % str(app_id)) | ||
31 | else: | ||
32 | # xwayland support | ||
33 | class_name = window.window_class | ||
34 | if len(class_name) > 0: | ||
35 | class_name = class_name.lower() | ||
36 | if class_name in WINDOW_ICONS: | ||
37 | return WINDOW_ICONS[class_name] | ||
38 | logging.info( | ||
39 | "No icon available for window with class_name: %s" % str(class_name) | ||
40 | ) | ||
41 | return DEFAULT_ICON | ||
42 | |||
43 | |||
44 | def rename_workspaces(ipc): | ||
45 | for workspace in ipc.get_tree().workspaces(): | ||
46 | name_parts = parse_workspace_name(workspace.name) | ||
47 | icon_tuple = () | ||
48 | for w in workspace: | ||
49 | if w.app_id is not None or w.window_class is not None: | ||
50 | icon = icon_for_window(w) | ||
51 | if not ARGUMENTS.duplicates and icon in icon_tuple: | ||
52 | continue | ||
53 | icon_tuple += (icon,) | ||
54 | name_parts["icons"] = " ".join(icon_tuple) + " " | ||
55 | new_name = construct_workspace_name(name_parts) | ||
56 | ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name)) | ||
57 | |||
58 | |||
59 | def undo_window_renaming(ipc): | ||
60 | for workspace in ipc.get_tree().workspaces(): | ||
61 | name_parts = parse_workspace_name(workspace.name) | ||
62 | name_parts["icons"] = None | ||
63 | new_name = construct_workspace_name(name_parts) | ||
64 | ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name)) | ||
65 | ipc.main_quit() | ||
66 | sys.exit(0) | ||
67 | |||
68 | |||
69 | def parse_workspace_name(name): | ||
70 | return re.match( | ||
71 | "(?P<num>[0-9]+):?(?P<shortname>\w+)? ?(?P<icons>.+)?", name | ||
72 | ).groupdict() | ||
73 | |||
74 | |||
75 | def construct_workspace_name(parts): | ||
76 | new_name = str(parts["num"]) | ||
77 | if parts["shortname"] or parts["icons"]: | ||
78 | new_name += ":" | ||
79 | |||
80 | if parts["shortname"]: | ||
81 | new_name += parts["shortname"] | ||
82 | |||
83 | if parts["icons"]: | ||
84 | new_name += " " + parts["icons"] | ||
85 | |||
86 | return new_name | ||
87 | |||
88 | |||
89 | if __name__ == "__main__": | ||
90 | parser = argparse.ArgumentParser( | ||
91 | description="This script automatically changes the workspace name in sway depending on your open applications." | ||
92 | ) | ||
93 | parser.add_argument( | ||
94 | "--duplicates", | ||
95 | "-d", | ||
96 | action="store_true", | ||
97 | help="Set it when you want an icon for each instance of the same application per workspace.", | ||
98 | ) | ||
99 | parser.add_argument( | ||
100 | "--logfile", | ||
101 | "-l", | ||
102 | type=str, | ||
103 | default="/tmp/sway-autoname-workspaces.log", | ||
104 | help="Path for the logfile.", | ||
105 | ) | ||
106 | args = parser.parse_args() | ||
107 | global ARGUMENTS | ||
108 | ARGUMENTS = args | ||
109 | |||
110 | logging.basicConfig( | ||
111 | level=logging.INFO, | ||
112 | filename=ARGUMENTS.logfile, | ||
113 | filemode="w", | ||
114 | format="%(message)s", | ||
115 | ) | ||
116 | |||
117 | ipc = i3ipc.Connection() | ||
118 | |||
119 | for sig in [signal.SIGINT, signal.SIGTERM]: | ||
120 | signal.signal(sig, lambda signal, frame: undo_window_renaming(ipc)) | ||
121 | |||
122 | def window_event_handler(ipc, e): | ||
123 | if e.change in ["new", "close", "move"]: | ||
124 | rename_workspaces(ipc) | ||
125 | |||
126 | ipc.on("window", window_event_handler) | ||
127 | |||
128 | rename_workspaces(ipc) | ||
129 | |||
130 | ipc.main() | ||
diff --git a/contrib/grimshot b/contrib/grimshot deleted file mode 100755 index 461a5eef..00000000 --- a/contrib/grimshot +++ /dev/null | |||
@@ -1,154 +0,0 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | ## Grimshot: a helper for screenshots within sway | ||
4 | ## Requirements: | ||
5 | ## - `grim`: screenshot utility for wayland | ||
6 | ## - `slurp`: to select an area | ||
7 | ## - `swaymsg`: to read properties of current window | ||
8 | ## - `wl-copy`: clipboard utility | ||
9 | ## - `jq`: json utility to parse swaymsg output | ||
10 | ## - `notify-send`: to show notifications | ||
11 | ## Those are needed to be installed, if unsure, run `grimshot check` | ||
12 | ## | ||
13 | ## See `man 1 grimshot` or `grimshot usage` for further details. | ||
14 | |||
15 | getTargetDirectory() { | ||
16 | test -f ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs && \ | ||
17 | . ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs | ||
18 | |||
19 | echo ${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}} | ||
20 | } | ||
21 | |||
22 | if [ "$1" = "--notify" ]; then | ||
23 | NOTIFY=yes | ||
24 | shift 1 | ||
25 | else | ||
26 | NOTIFY=no | ||
27 | fi | ||
28 | |||
29 | ACTION=${1:-usage} | ||
30 | SUBJECT=${2:-screen} | ||
31 | FILE=${3:-$(getTargetDirectory)/$(date -Ins).png} | ||
32 | |||
33 | if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then | ||
34 | echo "Usage:" | ||
35 | echo " grimshot [--notify] (copy|save) [active|screen|output|area|window] [FILE]" | ||
36 | echo " grimshot check" | ||
37 | echo " grimshot usage" | ||
38 | echo "" | ||
39 | echo "Commands:" | ||
40 | echo " copy: Copy the screenshot data into the clipboard." | ||
41 | echo " save: Save the screenshot to a regular file." | ||
42 | echo " check: Verify if required tools are installed and exit." | ||
43 | echo " usage: Show this message and exit." | ||
44 | echo "" | ||
45 | echo "Targets:" | ||
46 | echo " active: Currently active window." | ||
47 | echo " screen: All visible outputs." | ||
48 | echo " output: Currently active output." | ||
49 | echo " area: Manually select a region." | ||
50 | echo " window: Manually select a window." | ||
51 | exit | ||
52 | fi | ||
53 | |||
54 | notify() { | ||
55 | notify-send -t 3000 -a grimshot "$@" | ||
56 | } | ||
57 | notifyOk() { | ||
58 | [ "$NOTIFY" = "no" ] && return | ||
59 | |||
60 | TITLE=${2:-"Screenshot"} | ||
61 | MESSAGE=${1:-"OK"} | ||
62 | notify "$TITLE" "$MESSAGE" | ||
63 | } | ||
64 | notifyError() { | ||
65 | if [ $NOTIFY = "yes" ]; then | ||
66 | TITLE=${2:-"Screenshot"} | ||
67 | MESSAGE=${1:-"Error taking screenshot with grim"} | ||
68 | notify -u critical "$TITLE" "$MESSAGE" | ||
69 | else | ||
70 | echo $1 | ||
71 | fi | ||
72 | } | ||
73 | |||
74 | die() { | ||
75 | MSG=${1:-Bye} | ||
76 | notifyError "Error: $MSG" | ||
77 | exit 2 | ||
78 | } | ||
79 | |||
80 | check() { | ||
81 | COMMAND=$1 | ||
82 | if command -v "$COMMAND" > /dev/null 2>&1; then | ||
83 | RESULT="OK" | ||
84 | else | ||
85 | RESULT="NOT FOUND" | ||
86 | fi | ||
87 | echo " $COMMAND: $RESULT" | ||
88 | } | ||
89 | |||
90 | takeScreenshot() { | ||
91 | FILE=$1 | ||
92 | GEOM=$2 | ||
93 | OUTPUT=$3 | ||
94 | if [ ! -z "$OUTPUT" ]; then | ||
95 | grim -o "$OUTPUT" "$FILE" || die "Unable to invoke grim" | ||
96 | elif [ -z "$GEOM" ]; then | ||
97 | grim "$FILE" || die "Unable to invoke grim" | ||
98 | else | ||
99 | grim -g "$GEOM" "$FILE" || die "Unable to invoke grim" | ||
100 | fi | ||
101 | } | ||
102 | |||
103 | if [ "$ACTION" = "check" ] ; then | ||
104 | echo "Checking if required tools are installed. If something is missing, install it to your system and make it available in PATH..." | ||
105 | check grim | ||
106 | check slurp | ||
107 | check swaymsg | ||
108 | check wl-copy | ||
109 | check jq | ||
110 | check notify-send | ||
111 | exit | ||
112 | elif [ "$SUBJECT" = "area" ] ; then | ||
113 | GEOM=$(slurp -d) | ||
114 | # Check if user exited slurp without selecting the area | ||
115 | if [ -z "$GEOM" ]; then | ||
116 | exit | ||
117 | fi | ||
118 | WHAT="Area" | ||
119 | elif [ "$SUBJECT" = "active" ] ; then | ||
120 | FOCUSED=$(swaymsg -t get_tree | jq -r 'recurse(.nodes[]?, .floating_nodes[]?) | select(.focused)') | ||
121 | GEOM=$(echo "$FOCUSED" | jq -r '.rect | "\(.x),\(.y) \(.width)x\(.height)"') | ||
122 | APP_ID=$(echo "$FOCUSED" | jq -r '.app_id') | ||
123 | WHAT="$APP_ID window" | ||
124 | elif [ "$SUBJECT" = "screen" ] ; then | ||
125 | GEOM="" | ||
126 | WHAT="Screen" | ||
127 | elif [ "$SUBJECT" = "output" ] ; then | ||
128 | GEOM="" | ||
129 | OUTPUT=$(swaymsg -t get_outputs | jq -r '.[] | select(.focused)' | jq -r '.name') | ||
130 | WHAT="$OUTPUT" | ||
131 | elif [ "$SUBJECT" = "window" ] ; then | ||
132 | GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp) | ||
133 | # Check if user exited slurp without selecting the area | ||
134 | if [ -z "$GEOM" ]; then | ||
135 | exit | ||
136 | fi | ||
137 | WHAT="Window" | ||
138 | else | ||
139 | die "Unknown subject to take a screen shot from" "$SUBJECT" | ||
140 | fi | ||
141 | |||
142 | if [ "$ACTION" = "copy" ] ; then | ||
143 | takeScreenshot - "$GEOM" "$OUTPUT" | wl-copy --type image/png || die "Clipboard error" | ||
144 | notifyOk "$WHAT copied to buffer" | ||
145 | else | ||
146 | if takeScreenshot "$FILE" "$GEOM" "$OUTPUT"; then | ||
147 | TITLE="Screenshot of $SUBJECT" | ||
148 | MESSAGE=$(basename "$FILE") | ||
149 | notifyOk "$MESSAGE" "$TITLE" | ||
150 | echo $FILE | ||
151 | else | ||
152 | notifyError "Error taking screenshot with grim" | ||
153 | fi | ||
154 | fi | ||
diff --git a/contrib/grimshot.1 b/contrib/grimshot.1 deleted file mode 100644 index f6c8a377..00000000 --- a/contrib/grimshot.1 +++ /dev/null | |||
@@ -1,103 +0,0 @@ | |||
1 | .\" Generated by scdoc 1.11.1 | ||
2 | .\" Complete documentation for this program is not available as a GNU info page | ||
3 | .ie \n(.g .ds Aq \(aq | ||
4 | .el .ds Aq ' | ||
5 | .nh | ||
6 | .ad l | ||
7 | .\" Begin generated content: | ||
8 | .TH "grimshot" "1" "2020-12-20" | ||
9 | .P | ||
10 | .SH NAME | ||
11 | .P | ||
12 | grimshot - a helper for screenshots within sway | ||
13 | .P | ||
14 | .SH SYNOPSIS | ||
15 | .P | ||
16 | \fBgrimshot\fR [--notify] (copy|save) [TARGET] [FILE] | ||
17 | .br | ||
18 | \fBgrimshot\fR check | ||
19 | .br | ||
20 | \fBgrimshot\fR usage | ||
21 | .P | ||
22 | .SH OPTIONS | ||
23 | .P | ||
24 | \fB--notify\fR | ||
25 | .RS 4 | ||
26 | Show notifications to the user that a screenshot has been taken.\& | ||
27 | .P | ||
28 | .RE | ||
29 | \fBsave\fR | ||
30 | .RS 4 | ||
31 | Save the screenshot into a regular file.\& Grimshot will write images | ||
32 | files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined | ||
33 | in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\& | ||
34 | .P | ||
35 | .RE | ||
36 | \fBcopy\fR | ||
37 | .RS 4 | ||
38 | Copy the screenshot data (as image/png) into the clipboard.\& | ||
39 | .P | ||
40 | .RE | ||
41 | .SH DESCRIPTION | ||
42 | .P | ||
43 | Grimshot is an easy-to-use screenshot utility for sway.\& It provides a | ||
44 | convenient interface over grim, slurp and jq, and supports storing the | ||
45 | screenshot either directly to the clipboard using wl-copy or to a file.\& | ||
46 | .P | ||
47 | .SH EXAMPLES | ||
48 | .P | ||
49 | An example usage pattern is to add these bindings to your sway config: | ||
50 | .P | ||
51 | .nf | ||
52 | .RS 4 | ||
53 | # Screenshots: | ||
54 | # Super+P: Current window | ||
55 | # Super+Shift+p: Select area | ||
56 | # Super+Alt+p Current output | ||
57 | # Super+Ctrl+p Select a window | ||
58 | |||
59 | bindsym Mod4+p exec grimshot save active | ||
60 | bindsym Mod4+Shift+p exec grimshot save area | ||
61 | bindsym Mod4+Mod1+p exec grimshot save output | ||
62 | bindsym Mod4+Ctrl+p exec grimshot save window | ||
63 | .fi | ||
64 | .RE | ||
65 | .P | ||
66 | .SH TARGETS | ||
67 | .P | ||
68 | grimshot can capture the following named targets: | ||
69 | .P | ||
70 | \fIactive\fR | ||
71 | .RS 4 | ||
72 | Captures the currently active window.\& | ||
73 | .P | ||
74 | .RE | ||
75 | \fIscreen\fR | ||
76 | .RS 4 | ||
77 | Captures the entire screen.\& This includes all visible outputs.\& | ||
78 | .P | ||
79 | .RE | ||
80 | \fIarea\fR | ||
81 | .RS 4 | ||
82 | Allows manually selecting a rectangular region, and captures that.\& | ||
83 | .P | ||
84 | .RE | ||
85 | \fIwindow\fR | ||
86 | .RS 4 | ||
87 | Allows manually selecting a single window (by clicking on it), and | ||
88 | captures it.\& | ||
89 | .P | ||
90 | .RE | ||
91 | \fIoutput\fR | ||
92 | .RS 4 | ||
93 | Captures the currently active output.\& | ||
94 | .P | ||
95 | .RE | ||
96 | .SH OUTPUT | ||
97 | .P | ||
98 | Grimshot will print the filename of the captured screenshot to stdout if called | ||
99 | with the \fIsave\fR subcommand.\& | ||
100 | .P | ||
101 | .SH SEE ALSO | ||
102 | .P | ||
103 | \fBgrim\fR(1) | ||
diff --git a/contrib/grimshot.1.scd b/contrib/grimshot.1.scd deleted file mode 100644 index 4ab58532..00000000 --- a/contrib/grimshot.1.scd +++ /dev/null | |||
@@ -1,76 +0,0 @@ | |||
1 | grimshot(1) | ||
2 | |||
3 | # NAME | ||
4 | |||
5 | grimshot - a helper for screenshots within sway | ||
6 | |||
7 | # SYNOPSIS | ||
8 | |||
9 | *grimshot* [--notify] (copy|save) [TARGET] [FILE]++ | ||
10 | *grimshot* check++ | ||
11 | *grimshot* usage | ||
12 | |||
13 | # OPTIONS | ||
14 | |||
15 | *--notify* | ||
16 | Show notifications to the user that a screenshot has been taken. | ||
17 | |||
18 | *save* | ||
19 | Save the screenshot into a regular file. Grimshot will write images | ||
20 | files to *XDG_SCREENSHOTS_DIR* if this is set (or defined | ||
21 | in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*. | ||
22 | |||
23 | *copy* | ||
24 | Copy the screenshot data (as image/png) into the clipboard. | ||
25 | |||
26 | # DESCRIPTION | ||
27 | |||
28 | Grimshot is an easy-to-use screenshot utility for sway. It provides a | ||
29 | convenient interface over grim, slurp and jq, and supports storing the | ||
30 | screenshot either directly to the clipboard using wl-copy or to a file. | ||
31 | |||
32 | # EXAMPLES | ||
33 | |||
34 | An example usage pattern is to add these bindings to your sway config: | ||
35 | |||
36 | ``` | ||
37 | # Screenshots: | ||
38 | # Super+P: Current window | ||
39 | # Super+Shift+p: Select area | ||
40 | # Super+Alt+p Current output | ||
41 | # Super+Ctrl+p Select a window | ||
42 | |||
43 | bindsym Mod4+p exec grimshot save active | ||
44 | bindsym Mod4+Shift+p exec grimshot save area | ||
45 | bindsym Mod4+Mod1+p exec grimshot save output | ||
46 | bindsym Mod4+Ctrl+p exec grimshot save window | ||
47 | ``` | ||
48 | |||
49 | # TARGETS | ||
50 | |||
51 | grimshot can capture the following named targets: | ||
52 | |||
53 | _active_ | ||
54 | Captures the currently active window. | ||
55 | |||
56 | _screen_ | ||
57 | Captures the entire screen. This includes all visible outputs. | ||
58 | |||
59 | _area_ | ||
60 | Allows manually selecting a rectangular region, and captures that. | ||
61 | |||
62 | _window_ | ||
63 | Allows manually selecting a single window (by clicking on it), and | ||
64 | captures it. | ||
65 | |||
66 | _output_ | ||
67 | Captures the currently active output. | ||
68 | |||
69 | # OUTPUT | ||
70 | |||
71 | Grimshot will print the filename of the captured screenshot to stdout if called | ||
72 | with the _save_ subcommand. | ||
73 | |||
74 | # SEE ALSO | ||
75 | |||
76 | *grim*(1) | ||
diff --git a/contrib/inactive-windows-transparency.py b/contrib/inactive-windows-transparency.py deleted file mode 100755 index 77b1f221..00000000 --- a/contrib/inactive-windows-transparency.py +++ /dev/null | |||
@@ -1,64 +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 = event.container | ||
19 | workspace = ipc.get_tree().find_focused().workspace().num | ||
20 | |||
21 | if focused.id != prev_focused.id: # https://github.com/swaywm/sway/issues/2859 | ||
22 | focused.command("opacity 1") | ||
23 | if workspace == prev_workspace: | ||
24 | prev_focused.command("opacity " + inactive_opacity) | ||
25 | prev_focused = focused | ||
26 | prev_workspace = workspace | ||
27 | |||
28 | |||
29 | def remove_opacity(ipc): | ||
30 | for workspace in ipc.get_tree().workspaces(): | ||
31 | for w in workspace: | ||
32 | w.command("opacity 1") | ||
33 | ipc.main_quit() | ||
34 | sys.exit(0) | ||
35 | |||
36 | |||
37 | if __name__ == "__main__": | ||
38 | transparency_val = "0.80" | ||
39 | |||
40 | parser = argparse.ArgumentParser( | ||
41 | description="This script allows you to set the transparency of unfocused windows in sway." | ||
42 | ) | ||
43 | parser.add_argument( | ||
44 | "--opacity", | ||
45 | "-o", | ||
46 | type=str, | ||
47 | default=transparency_val, | ||
48 | help="set opacity value in range 0...1", | ||
49 | ) | ||
50 | args = parser.parse_args() | ||
51 | |||
52 | ipc = i3ipc.Connection() | ||
53 | prev_focused = None | ||
54 | prev_workspace = ipc.get_tree().find_focused().workspace().num | ||
55 | |||
56 | for window in ipc.get_tree(): | ||
57 | if window.focused: | ||
58 | prev_focused = window | ||
59 | else: | ||
60 | window.command("opacity " + args.opacity) | ||
61 | for sig in [signal.SIGINT, signal.SIGTERM]: | ||
62 | signal.signal(sig, lambda signal, frame: remove_opacity(ipc)) | ||
63 | ipc.on("window::focus", partial(on_window_focus, args.opacity)) | ||
64 | 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 15935ffd..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.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/cairo.h b/include/cairo_util.h index c1275db2..dc049c6d 100644 --- a/include/cairo.h +++ b/include/cairo_util.h | |||
@@ -1,8 +1,8 @@ | |||
1 | #ifndef _SWAY_CAIRO_H | 1 | #ifndef _SWAY_CAIRO_UTIL_H |
2 | #define _SWAY_CAIRO_H | 2 | #define _SWAY_CAIRO_UTIL_H |
3 | #include "config.h" | 3 | #include "config.h" |
4 | #include <stdint.h> | 4 | #include <stdint.h> |
5 | #include <cairo/cairo.h> | 5 | #include <cairo.h> |
6 | #include <wayland-client-protocol.h> | 6 | #include <wayland-client-protocol.h> |
7 | 7 | ||
8 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color); | 8 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color); |
diff --git a/include/gesture.h b/include/gesture.h new file mode 100644 index 00000000..9c6b0f91 --- /dev/null +++ b/include/gesture.h | |||
@@ -0,0 +1,104 @@ | |||
1 | #ifndef _SWAY_GESTURE_H | ||
2 | #define _SWAY_GESTURE_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include <stdint.h> | ||
6 | |||
7 | /** | ||
8 | * A gesture type used in binding. | ||
9 | */ | ||
10 | enum gesture_type { | ||
11 | GESTURE_TYPE_NONE = 0, | ||
12 | GESTURE_TYPE_HOLD, | ||
13 | GESTURE_TYPE_PINCH, | ||
14 | GESTURE_TYPE_SWIPE, | ||
15 | }; | ||
16 | |||
17 | // Turns single type enum value to constant string representation. | ||
18 | const char *gesture_type_string(enum gesture_type direction); | ||
19 | |||
20 | // Value to use to accept any finger count | ||
21 | extern const uint8_t GESTURE_FINGERS_ANY; | ||
22 | |||
23 | /** | ||
24 | * A gesture direction used in binding. | ||
25 | */ | ||
26 | enum gesture_direction { | ||
27 | GESTURE_DIRECTION_NONE = 0, | ||
28 | // Directions based on delta x and y | ||
29 | GESTURE_DIRECTION_UP = 1 << 0, | ||
30 | GESTURE_DIRECTION_DOWN = 1 << 1, | ||
31 | GESTURE_DIRECTION_LEFT = 1 << 2, | ||
32 | GESTURE_DIRECTION_RIGHT = 1 << 3, | ||
33 | // Directions based on scale | ||
34 | GESTURE_DIRECTION_INWARD = 1 << 4, | ||
35 | GESTURE_DIRECTION_OUTWARD = 1 << 5, | ||
36 | // Directions based on rotation | ||
37 | GESTURE_DIRECTION_CLOCKWISE = 1 << 6, | ||
38 | GESTURE_DIRECTION_COUNTERCLOCKWISE = 1 << 7, | ||
39 | }; | ||
40 | |||
41 | // Turns single direction enum value to constant string representation. | ||
42 | const char *gesture_direction_string(enum gesture_direction direction); | ||
43 | |||
44 | /** | ||
45 | * Struct representing a pointer gesture | ||
46 | */ | ||
47 | struct gesture { | ||
48 | enum gesture_type type; | ||
49 | uint8_t fingers; | ||
50 | uint32_t directions; | ||
51 | }; | ||
52 | |||
53 | /** | ||
54 | * Parses gesture from <gesture>[:<fingers>][:<directions>] string. | ||
55 | * | ||
56 | * Return NULL on success, otherwise error message string | ||
57 | */ | ||
58 | char *gesture_parse(const char *input, struct gesture *output); | ||
59 | |||
60 | // Turns gesture into string representation | ||
61 | char *gesture_to_string(struct gesture *gesture); | ||
62 | |||
63 | // Check if gesture is of certain type and finger count. | ||
64 | bool gesture_check(struct gesture *target, | ||
65 | enum gesture_type type, uint8_t fingers); | ||
66 | |||
67 | // Check if a gesture target/binding is match by other gesture/input | ||
68 | bool gesture_match(struct gesture *target, | ||
69 | struct gesture *to_match, bool exact); | ||
70 | |||
71 | // Returns true if gesture are exactly the same | ||
72 | bool gesture_equal(struct gesture *a, struct gesture *b); | ||
73 | |||
74 | // Compare distance between two matched target gestures. | ||
75 | int8_t gesture_compare(struct gesture *a, struct gesture *b); | ||
76 | |||
77 | // Small helper struct to track gestures over time | ||
78 | struct gesture_tracker { | ||
79 | enum gesture_type type; | ||
80 | uint8_t fingers; | ||
81 | double dx, dy; | ||
82 | double scale; | ||
83 | double rotation; | ||
84 | }; | ||
85 | |||
86 | // Begin gesture tracking | ||
87 | void gesture_tracker_begin(struct gesture_tracker *tracker, | ||
88 | enum gesture_type type, uint8_t fingers); | ||
89 | |||
90 | // Check if the provides type is currently being tracked | ||
91 | bool gesture_tracker_check(struct gesture_tracker *tracker, | ||
92 | enum gesture_type type); | ||
93 | |||
94 | // Update gesture track with new data point | ||
95 | void gesture_tracker_update(struct gesture_tracker *tracker, double dx, | ||
96 | double dy, double scale, double rotation); | ||
97 | |||
98 | // Reset tracker | ||
99 | void gesture_tracker_cancel(struct gesture_tracker *tracker); | ||
100 | |||
101 | // Reset tracker and return gesture tracked | ||
102 | struct gesture *gesture_tracker_end(struct gesture_tracker *tracker); | ||
103 | |||
104 | #endif | ||
diff --git a/include/ipc-client.h b/include/ipc-client.h index d3895023..9c5712d7 100644 --- a/include/ipc-client.h +++ b/include/ipc-client.h | |||
@@ -1,6 +1,9 @@ | |||
1 | #ifndef _SWAY_IPC_CLIENT_H | 1 | #ifndef _SWAY_IPC_CLIENT_H |
2 | #define _SWAY_IPC_CLIENT_H | 2 | #define _SWAY_IPC_CLIENT_H |
3 | 3 | ||
4 | // arbitrary number, it's probably sufficient, higher number = more memory usage | ||
5 | #define JSON_MAX_DEPTH 512 | ||
6 | |||
4 | #include <stdbool.h> | 7 | #include <stdbool.h> |
5 | #include <stdint.h> | 8 | #include <stdint.h> |
6 | #include <sys/time.h> | 9 | #include <sys/time.h> |
diff --git a/include/pango.h b/include/pango.h index 6ab83c16..228e39cf 100644 --- a/include/pango.h +++ b/include/pango.h | |||
@@ -3,8 +3,9 @@ | |||
3 | #include <stdarg.h> | 3 | #include <stdarg.h> |
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <stdint.h> | 5 | #include <stdint.h> |
6 | #include <cairo/cairo.h> | 6 | #include <cairo.h> |
7 | #include <pango/pangocairo.h> | 7 | #include <pango/pangocairo.h> |
8 | #include "stringop.h" | ||
8 | 9 | ||
9 | /** | 10 | /** |
10 | * Utility function which escape characters a & < > ' ". | 11 | * Utility function which escape characters a & < > ' ". |
@@ -13,11 +14,12 @@ | |||
13 | * escaped string to dest if provided. | 14 | * escaped string to dest if provided. |
14 | */ | 15 | */ |
15 | size_t escape_markup_text(const char *src, char *dest); | 16 | size_t escape_markup_text(const char *src, char *dest); |
16 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, | 17 | PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, |
17 | const char *text, double scale, bool markup); | 18 | const char *text, double scale, bool markup); |
18 | void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, | 19 | void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, |
19 | int *baseline, double scale, bool markup, const char *fmt, ...); | 20 | int *baseline, double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(8, 9); |
20 | void pango_printf(cairo_t *cairo, const char *font, | 21 | void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline); |
21 | double scale, bool markup, const char *fmt, ...); | 22 | void render_text(cairo_t *cairo, PangoFontDescription *desc, |
23 | double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(5, 6); | ||
22 | 24 | ||
23 | #endif | 25 | #endif |
diff --git a/include/pool-buffer.h b/include/pool-buffer.h index 54f5be06..b7a95afe 100644 --- a/include/pool-buffer.h +++ b/include/pool-buffer.h | |||
@@ -1,6 +1,6 @@ | |||
1 | #ifndef _SWAY_BUFFERS_H | 1 | #ifndef _SWAY_BUFFERS_H |
2 | #define _SWAY_BUFFERS_H | 2 | #define _SWAY_BUFFERS_H |
3 | #include <cairo/cairo.h> | 3 | #include <cairo.h> |
4 | #include <pango/pangocairo.h> | 4 | #include <pango/pangocairo.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <stdint.h> | 6 | #include <stdint.h> |
diff --git a/include/stringop.h b/include/stringop.h index 8d7089e9..19a50f23 100644 --- a/include/stringop.h +++ b/include/stringop.h | |||
@@ -2,8 +2,15 @@ | |||
2 | #define _SWAY_STRINGOP_H | 2 | #define _SWAY_STRINGOP_H |
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <stddef.h> | ||
5 | #include "list.h" | 6 | #include "list.h" |
6 | 7 | ||
8 | #ifdef __GNUC__ | ||
9 | #define _SWAY_ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end))) | ||
10 | #else | ||
11 | #define _SWAY_ATTRIB_PRINTF(start, end) | ||
12 | #endif | ||
13 | |||
7 | void strip_whitespace(char *str); | 14 | void strip_whitespace(char *str); |
8 | void strip_quotes(char *str); | 15 | void strip_quotes(char *str); |
9 | 16 | ||
@@ -30,4 +37,7 @@ char *argsep(char **stringp, const char *delim, char *matched_delim); | |||
30 | // Expand a path using shell replacements such as $HOME and ~ | 37 | // Expand a path using shell replacements such as $HOME and ~ |
31 | bool expand_path(char **path); | 38 | bool expand_path(char **path); |
32 | 39 | ||
40 | char *vformat_str(const char *fmt, va_list args) _SWAY_ATTRIB_PRINTF(1, 0); | ||
41 | char *format_str(const char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); | ||
42 | |||
33 | #endif | 43 | #endif |
diff --git a/include/sway/commands.h b/include/sway/commands.h index 964b3661..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,8 +47,8 @@ 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 | struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers, | 50 | const struct cmd_handler *find_handler(const char *line, |
50 | size_t handlers_size); | 51 | const struct cmd_handler *cmd_handlers, size_t handlers_size); |
51 | 52 | ||
52 | /** | 53 | /** |
53 | * Parse and executes a command. | 54 | * Parse and executes a command. |
@@ -68,7 +69,7 @@ struct cmd_results *config_command(char *command, char **new_block); | |||
68 | * Parse and handle a sub command | 69 | * Parse and handle a sub command |
69 | */ | 70 | */ |
70 | struct cmd_results *config_subcommand(char **argv, int argc, | 71 | struct cmd_results *config_subcommand(char **argv, int argc, |
71 | struct cmd_handler *handlers, size_t handlers_size); | 72 | const struct cmd_handler *handlers, size_t handlers_size); |
72 | /* | 73 | /* |
73 | * Parses a command policy rule. | 74 | * Parses a command policy rule. |
74 | */ | 75 | */ |
@@ -76,7 +77,7 @@ struct cmd_results *config_commands_command(char *exec); | |||
76 | /** | 77 | /** |
77 | * Allocates a cmd_results object. | 78 | * Allocates a cmd_results object. |
78 | */ | 79 | */ |
79 | struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...); | 80 | struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...) _SWAY_ATTRIB_PRINTF(2, 3); |
80 | /** | 81 | /** |
81 | * Frees a cmd_results object. | 82 | * Frees a cmd_results object. |
82 | */ | 83 | */ |
@@ -106,12 +107,14 @@ sway_cmd cmd_exec_process; | |||
106 | sway_cmd cmd_assign; | 107 | sway_cmd cmd_assign; |
107 | sway_cmd cmd_bar; | 108 | sway_cmd cmd_bar; |
108 | sway_cmd cmd_bindcode; | 109 | sway_cmd cmd_bindcode; |
110 | sway_cmd cmd_bindgesture; | ||
109 | sway_cmd cmd_bindswitch; | 111 | sway_cmd cmd_bindswitch; |
110 | sway_cmd cmd_bindsym; | 112 | sway_cmd cmd_bindsym; |
111 | sway_cmd cmd_border; | 113 | sway_cmd cmd_border; |
112 | sway_cmd cmd_client_noop; | 114 | sway_cmd cmd_client_noop; |
113 | sway_cmd cmd_client_focused; | 115 | sway_cmd cmd_client_focused; |
114 | sway_cmd cmd_client_focused_inactive; | 116 | sway_cmd cmd_client_focused_inactive; |
117 | sway_cmd cmd_client_focused_tab_title; | ||
115 | sway_cmd cmd_client_unfocused; | 118 | sway_cmd cmd_client_unfocused; |
116 | sway_cmd cmd_client_urgent; | 119 | sway_cmd cmd_client_urgent; |
117 | sway_cmd cmd_client_placeholder; | 120 | sway_cmd cmd_client_placeholder; |
@@ -157,12 +160,11 @@ sway_cmd cmd_new_float; | |||
157 | sway_cmd cmd_new_window; | 160 | sway_cmd cmd_new_window; |
158 | sway_cmd cmd_nop; | 161 | sway_cmd cmd_nop; |
159 | sway_cmd cmd_opacity; | 162 | sway_cmd cmd_opacity; |
160 | sway_cmd cmd_new_float; | ||
161 | sway_cmd cmd_new_window; | ||
162 | sway_cmd cmd_no_focus; | 163 | sway_cmd cmd_no_focus; |
163 | sway_cmd cmd_output; | 164 | sway_cmd cmd_output; |
164 | sway_cmd cmd_permit; | 165 | sway_cmd cmd_permit; |
165 | sway_cmd cmd_popup_during_fullscreen; | 166 | sway_cmd cmd_popup_during_fullscreen; |
167 | sway_cmd cmd_primary_selection; | ||
166 | sway_cmd cmd_reject; | 168 | sway_cmd cmd_reject; |
167 | sway_cmd cmd_reload; | 169 | sway_cmd cmd_reload; |
168 | sway_cmd cmd_rename; | 170 | sway_cmd cmd_rename; |
@@ -190,6 +192,7 @@ sway_cmd cmd_titlebar_border_thickness; | |||
190 | sway_cmd cmd_titlebar_padding; | 192 | sway_cmd cmd_titlebar_padding; |
191 | sway_cmd cmd_unbindcode; | 193 | sway_cmd cmd_unbindcode; |
192 | sway_cmd cmd_unbindswitch; | 194 | sway_cmd cmd_unbindswitch; |
195 | sway_cmd cmd_unbindgesture; | ||
193 | sway_cmd cmd_unbindsym; | 196 | sway_cmd cmd_unbindsym; |
194 | sway_cmd cmd_unmark; | 197 | sway_cmd cmd_unmark; |
195 | sway_cmd cmd_urgent; | 198 | sway_cmd cmd_urgent; |
@@ -249,6 +252,7 @@ sway_cmd input_cmd_click_method; | |||
249 | sway_cmd input_cmd_drag; | 252 | sway_cmd input_cmd_drag; |
250 | sway_cmd input_cmd_drag_lock; | 253 | sway_cmd input_cmd_drag_lock; |
251 | sway_cmd input_cmd_dwt; | 254 | sway_cmd input_cmd_dwt; |
255 | sway_cmd input_cmd_dwtp; | ||
252 | sway_cmd input_cmd_events; | 256 | sway_cmd input_cmd_events; |
253 | sway_cmd input_cmd_left_handed; | 257 | sway_cmd input_cmd_left_handed; |
254 | sway_cmd input_cmd_map_from_region; | 258 | sway_cmd input_cmd_map_from_region; |
@@ -257,10 +261,12 @@ sway_cmd input_cmd_map_to_region; | |||
257 | sway_cmd input_cmd_middle_emulation; | 261 | sway_cmd input_cmd_middle_emulation; |
258 | sway_cmd input_cmd_natural_scroll; | 262 | sway_cmd input_cmd_natural_scroll; |
259 | sway_cmd input_cmd_pointer_accel; | 263 | sway_cmd input_cmd_pointer_accel; |
264 | sway_cmd input_cmd_rotation_angle; | ||
260 | sway_cmd input_cmd_scroll_factor; | 265 | sway_cmd input_cmd_scroll_factor; |
261 | sway_cmd input_cmd_repeat_delay; | 266 | sway_cmd input_cmd_repeat_delay; |
262 | sway_cmd input_cmd_repeat_rate; | 267 | sway_cmd input_cmd_repeat_rate; |
263 | sway_cmd input_cmd_scroll_button; | 268 | sway_cmd input_cmd_scroll_button; |
269 | sway_cmd input_cmd_scroll_button_lock; | ||
264 | sway_cmd input_cmd_scroll_method; | 270 | sway_cmd input_cmd_scroll_method; |
265 | sway_cmd input_cmd_tap; | 271 | sway_cmd input_cmd_tap; |
266 | sway_cmd input_cmd_tap_button_map; | 272 | sway_cmd input_cmd_tap_button_map; |
@@ -282,12 +288,16 @@ sway_cmd output_cmd_dpms; | |||
282 | sway_cmd output_cmd_enable; | 288 | sway_cmd output_cmd_enable; |
283 | sway_cmd output_cmd_max_render_time; | 289 | sway_cmd output_cmd_max_render_time; |
284 | sway_cmd output_cmd_mode; | 290 | sway_cmd output_cmd_mode; |
291 | sway_cmd output_cmd_modeline; | ||
285 | sway_cmd output_cmd_position; | 292 | sway_cmd output_cmd_position; |
293 | sway_cmd output_cmd_power; | ||
294 | sway_cmd output_cmd_render_bit_depth; | ||
286 | sway_cmd output_cmd_scale; | 295 | sway_cmd output_cmd_scale; |
287 | sway_cmd output_cmd_scale_filter; | 296 | sway_cmd output_cmd_scale_filter; |
288 | sway_cmd output_cmd_subpixel; | 297 | sway_cmd output_cmd_subpixel; |
289 | sway_cmd output_cmd_toggle; | 298 | sway_cmd output_cmd_toggle; |
290 | sway_cmd output_cmd_transform; | 299 | sway_cmd output_cmd_transform; |
300 | sway_cmd output_cmd_unplug; | ||
291 | 301 | ||
292 | sway_cmd seat_cmd_attach; | 302 | sway_cmd seat_cmd_attach; |
293 | sway_cmd seat_cmd_cursor; | 303 | sway_cmd seat_cmd_cursor; |
diff --git a/include/sway/config.h b/include/sway/config.h index 59f22ae2..0be1cd22 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -5,16 +5,20 @@ | |||
5 | #include <string.h> | 5 | #include <string.h> |
6 | #include <time.h> | 6 | #include <time.h> |
7 | #include <wlr/interfaces/wlr_switch.h> | 7 | #include <wlr/interfaces/wlr_switch.h> |
8 | #include <wlr/types/wlr_box.h> | ||
9 | #include <wlr/types/wlr_tablet_tool.h> | 8 | #include <wlr/types/wlr_tablet_tool.h> |
9 | #include <wlr/util/box.h> | ||
10 | #include <xkbcommon/xkbcommon.h> | 10 | #include <xkbcommon/xkbcommon.h> |
11 | #include <xf86drmMode.h> | ||
11 | #include "../include/config.h" | 12 | #include "../include/config.h" |
13 | #include "gesture.h" | ||
12 | #include "list.h" | 14 | #include "list.h" |
15 | #include "stringop.h" | ||
13 | #include "swaynag.h" | 16 | #include "swaynag.h" |
14 | #include "tree/container.h" | 17 | #include "tree/container.h" |
15 | #include "sway/input/tablet.h" | 18 | #include "sway/input/tablet.h" |
16 | #include "sway/tree/root.h" | 19 | #include "sway/tree/root.h" |
17 | #include "wlr-layer-shell-unstable-v1-protocol.h" | 20 | #include "wlr-layer-shell-unstable-v1-protocol.h" |
21 | #include <pango/pangocairo.h> | ||
18 | 22 | ||
19 | // TODO: Refactor this shit | 23 | // TODO: Refactor this shit |
20 | 24 | ||
@@ -31,7 +35,8 @@ enum binding_input_type { | |||
31 | BINDING_KEYSYM, | 35 | BINDING_KEYSYM, |
32 | BINDING_MOUSECODE, | 36 | BINDING_MOUSECODE, |
33 | BINDING_MOUSESYM, | 37 | BINDING_MOUSESYM, |
34 | BINDING_SWITCH | 38 | BINDING_SWITCH, // dummy, only used to call seat_execute_command |
39 | BINDING_GESTURE // dummy, only used to call seat_execute_command | ||
35 | }; | 40 | }; |
36 | 41 | ||
37 | enum binding_flags { | 42 | enum binding_flags { |
@@ -44,10 +49,11 @@ enum binding_flags { | |||
44 | BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload | 49 | BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload |
45 | BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor | 50 | BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor |
46 | BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key | 51 | BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key |
52 | BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match | ||
47 | }; | 53 | }; |
48 | 54 | ||
49 | /** | 55 | /** |
50 | * A key binding and an associated command. | 56 | * A key (or mouse) binding and an associated command. |
51 | */ | 57 | */ |
52 | struct sway_binding { | 58 | struct sway_binding { |
53 | enum binding_input_type type; | 59 | enum binding_input_type type; |
@@ -61,12 +67,10 @@ struct sway_binding { | |||
61 | char *command; | 67 | char *command; |
62 | }; | 68 | }; |
63 | 69 | ||
64 | /** | 70 | enum sway_switch_trigger { |
65 | * A mouse binding and an associated command. | 71 | SWAY_SWITCH_TRIGGER_OFF, |
66 | */ | 72 | SWAY_SWITCH_TRIGGER_ON, |
67 | struct sway_mouse_binding { | 73 | SWAY_SWITCH_TRIGGER_TOGGLE, |
68 | uint32_t button; | ||
69 | char *command; | ||
70 | }; | 74 | }; |
71 | 75 | ||
72 | /** | 76 | /** |
@@ -74,12 +78,22 @@ struct sway_mouse_binding { | |||
74 | */ | 78 | */ |
75 | struct sway_switch_binding { | 79 | struct sway_switch_binding { |
76 | enum wlr_switch_type type; | 80 | enum wlr_switch_type type; |
77 | enum wlr_switch_state state; | 81 | enum sway_switch_trigger trigger; |
78 | uint32_t flags; | 82 | uint32_t flags; |
79 | char *command; | 83 | char *command; |
80 | }; | 84 | }; |
81 | 85 | ||
82 | /** | 86 | /** |
87 | * A gesture binding and an associated command. | ||
88 | */ | ||
89 | struct sway_gesture_binding { | ||
90 | char *input; | ||
91 | uint32_t flags; | ||
92 | struct gesture gesture; | ||
93 | char *command; | ||
94 | }; | ||
95 | |||
96 | /** | ||
83 | * Focus on window activation. | 97 | * Focus on window activation. |
84 | */ | 98 | */ |
85 | enum sway_fowa { | 99 | enum sway_fowa { |
@@ -98,6 +112,7 @@ struct sway_mode { | |||
98 | list_t *keycode_bindings; | 112 | list_t *keycode_bindings; |
99 | list_t *mouse_bindings; | 113 | list_t *mouse_bindings; |
100 | list_t *switch_bindings; | 114 | list_t *switch_bindings; |
115 | list_t *gesture_bindings; | ||
101 | bool pango; | 116 | bool pango; |
102 | }; | 117 | }; |
103 | 118 | ||
@@ -136,14 +151,17 @@ struct input_config { | |||
136 | int drag; | 151 | int drag; |
137 | int drag_lock; | 152 | int drag_lock; |
138 | int dwt; | 153 | int dwt; |
154 | int dwtp; | ||
139 | int left_handed; | 155 | int left_handed; |
140 | int middle_emulation; | 156 | int middle_emulation; |
141 | int natural_scroll; | 157 | int natural_scroll; |
142 | float pointer_accel; | 158 | float pointer_accel; |
159 | float rotation_angle; | ||
143 | float scroll_factor; | 160 | float scroll_factor; |
144 | int repeat_delay; | 161 | int repeat_delay; |
145 | int repeat_rate; | 162 | int repeat_rate; |
146 | int scroll_button; | 163 | int scroll_button; |
164 | int scroll_button_lock; | ||
147 | int scroll_method; | 165 | int scroll_method; |
148 | int send_events; | 166 | int send_events; |
149 | int tap; | 167 | int tap; |
@@ -233,12 +251,6 @@ struct seat_config { | |||
233 | } xcursor_theme; | 251 | } xcursor_theme; |
234 | }; | 252 | }; |
235 | 253 | ||
236 | enum config_dpms { | ||
237 | DPMS_IGNORE, | ||
238 | DPMS_ON, | ||
239 | DPMS_OFF, | ||
240 | }; | ||
241 | |||
242 | enum scale_filter_mode { | 254 | enum scale_filter_mode { |
243 | SCALE_FILTER_DEFAULT, // the default is currently smart | 255 | SCALE_FILTER_DEFAULT, // the default is currently smart |
244 | SCALE_FILTER_LINEAR, | 256 | SCALE_FILTER_LINEAR, |
@@ -246,6 +258,12 @@ enum scale_filter_mode { | |||
246 | SCALE_FILTER_SMART, | 258 | SCALE_FILTER_SMART, |
247 | }; | 259 | }; |
248 | 260 | ||
261 | enum render_bit_depth { | ||
262 | RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8 | ||
263 | RENDER_BIT_DEPTH_8, | ||
264 | RENDER_BIT_DEPTH_10, | ||
265 | }; | ||
266 | |||
249 | /** | 267 | /** |
250 | * Size and position configuration for a particular output. | 268 | * Size and position configuration for a particular output. |
251 | * | 269 | * |
@@ -254,9 +272,11 @@ enum scale_filter_mode { | |||
254 | struct output_config { | 272 | struct output_config { |
255 | char *name; | 273 | char *name; |
256 | int enabled; | 274 | int enabled; |
275 | int power; | ||
257 | int width, height; | 276 | int width, height; |
258 | float refresh_rate; | 277 | float refresh_rate; |
259 | int custom_mode; | 278 | int custom_mode; |
279 | drmModeModeInfo drm_mode; | ||
260 | int x, y; | 280 | int x, y; |
261 | float scale; | 281 | float scale; |
262 | enum scale_filter_mode scale_filter; | 282 | enum scale_filter_mode scale_filter; |
@@ -264,11 +284,19 @@ struct output_config { | |||
264 | enum wl_output_subpixel subpixel; | 284 | enum wl_output_subpixel subpixel; |
265 | int max_render_time; // In milliseconds | 285 | int max_render_time; // In milliseconds |
266 | int adaptive_sync; | 286 | int adaptive_sync; |
287 | enum render_bit_depth render_bit_depth; | ||
267 | 288 | ||
268 | char *background; | 289 | char *background; |
269 | char *background_option; | 290 | char *background_option; |
270 | char *background_fallback; | 291 | char *background_fallback; |
271 | enum config_dpms dpms_state; | 292 | }; |
293 | |||
294 | /** | ||
295 | * An output config pre-matched to an output | ||
296 | */ | ||
297 | struct matched_output_config { | ||
298 | struct sway_output *output; | ||
299 | struct output_config *config; | ||
272 | }; | 300 | }; |
273 | 301 | ||
274 | /** | 302 | /** |
@@ -281,6 +309,12 @@ struct side_gaps { | |||
281 | int left; | 309 | int left; |
282 | }; | 310 | }; |
283 | 311 | ||
312 | enum smart_gaps_mode { | ||
313 | SMART_GAPS_OFF, | ||
314 | SMART_GAPS_ON, | ||
315 | SMART_GAPS_INVERSE_OUTER, | ||
316 | }; | ||
317 | |||
284 | /** | 318 | /** |
285 | * Stores configuration for a workspace, regardless of whether the workspace | 319 | * Stores configuration for a workspace, regardless of whether the workspace |
286 | * exists. | 320 | * exists. |
@@ -292,6 +326,12 @@ struct workspace_config { | |||
292 | struct side_gaps gaps_outer; | 326 | struct side_gaps gaps_outer; |
293 | }; | 327 | }; |
294 | 328 | ||
329 | enum pango_markup_config { | ||
330 | PANGO_MARKUP_DISABLED = false, | ||
331 | PANGO_MARKUP_ENABLED = true, | ||
332 | PANGO_MARKUP_DEFAULT // The default is font dependent ("pango:" prefix) | ||
333 | }; | ||
334 | |||
295 | struct bar_config { | 335 | struct bar_config { |
296 | char *swaybar_command; | 336 | char *swaybar_command; |
297 | struct wl_client *client; | 337 | struct wl_client *client; |
@@ -323,7 +363,7 @@ struct bar_config { | |||
323 | char *position; | 363 | char *position; |
324 | list_t *bindings; | 364 | list_t *bindings; |
325 | char *status_command; | 365 | char *status_command; |
326 | bool pango_markup; | 366 | enum pango_markup_config pango_markup; |
327 | char *font; | 367 | char *font; |
328 | int height; // -1 not defined | 368 | int height; // -1 not defined |
329 | bool workspace_buttons; | 369 | bool workspace_buttons; |
@@ -410,14 +450,6 @@ enum sway_popup_during_fullscreen { | |||
410 | POPUP_LEAVE, | 450 | POPUP_LEAVE, |
411 | }; | 451 | }; |
412 | 452 | ||
413 | enum command_context { | ||
414 | CONTEXT_CONFIG = 1 << 0, | ||
415 | CONTEXT_BINDING = 1 << 1, | ||
416 | CONTEXT_IPC = 1 << 2, | ||
417 | CONTEXT_CRITERIA = 1 << 3, | ||
418 | CONTEXT_ALL = 0xFFFFFFFF, | ||
419 | }; | ||
420 | |||
421 | enum focus_follows_mouse_mode { | 453 | enum focus_follows_mouse_mode { |
422 | FOLLOWS_NO, | 454 | FOLLOWS_NO, |
423 | FOLLOWS_YES, | 455 | FOLLOWS_YES, |
@@ -479,9 +511,10 @@ struct sway_config { | |||
479 | char *floating_scroll_right_cmd; | 511 | char *floating_scroll_right_cmd; |
480 | enum sway_container_layout default_orientation; | 512 | enum sway_container_layout default_orientation; |
481 | enum sway_container_layout default_layout; | 513 | enum sway_container_layout default_layout; |
482 | char *font; | 514 | char *font; // Used for IPC. |
483 | size_t font_height; | 515 | PangoFontDescription *font_description; // Used internally for rendering and validating. |
484 | size_t font_baseline; | 516 | int font_height; |
517 | int font_baseline; | ||
485 | bool pango_markup; | 518 | bool pango_markup; |
486 | int titlebar_border_thickness; | 519 | int titlebar_border_thickness; |
487 | int titlebar_h_padding; | 520 | int titlebar_h_padding; |
@@ -508,11 +541,12 @@ struct sway_config { | |||
508 | bool auto_back_and_forth; | 541 | bool auto_back_and_forth; |
509 | bool show_marks; | 542 | bool show_marks; |
510 | enum alignment title_align; | 543 | enum alignment title_align; |
544 | bool primary_selection; | ||
511 | 545 | ||
512 | bool tiling_drag; | 546 | bool tiling_drag; |
513 | int tiling_drag_threshold; | 547 | int tiling_drag_threshold; |
514 | 548 | ||
515 | bool smart_gaps; | 549 | enum smart_gaps_mode smart_gaps; |
516 | int gaps_inner; | 550 | int gaps_inner; |
517 | struct side_gaps gaps_outer; | 551 | struct side_gaps gaps_outer; |
518 | 552 | ||
@@ -535,12 +569,15 @@ struct sway_config { | |||
535 | struct { | 569 | struct { |
536 | struct border_colors focused; | 570 | struct border_colors focused; |
537 | struct border_colors focused_inactive; | 571 | struct border_colors focused_inactive; |
572 | struct border_colors focused_tab_title; | ||
538 | struct border_colors unfocused; | 573 | struct border_colors unfocused; |
539 | struct border_colors urgent; | 574 | struct border_colors urgent; |
540 | struct border_colors placeholder; | 575 | struct border_colors placeholder; |
541 | float background[4]; | 576 | float background[4]; |
542 | } border_colors; | 577 | } border_colors; |
543 | 578 | ||
579 | bool has_focused_tab_title; | ||
580 | |||
544 | // floating view | 581 | // floating view |
545 | int32_t floating_maximum_width; | 582 | int32_t floating_maximum_width; |
546 | int32_t floating_maximum_height; | 583 | int32_t floating_maximum_height; |
@@ -559,7 +596,7 @@ struct sway_config { | |||
559 | struct sway_node *node; | 596 | struct sway_node *node; |
560 | struct sway_container *container; | 597 | struct sway_container *container; |
561 | struct sway_workspace *workspace; | 598 | struct sway_workspace *workspace; |
562 | bool using_criteria; | 599 | bool node_overridden; // True if the node is selected by means other than focus |
563 | struct { | 600 | struct { |
564 | int argc; | 601 | int argc; |
565 | char **argv; | 602 | char **argv; |
@@ -598,7 +635,7 @@ void run_deferred_bindings(void); | |||
598 | /** | 635 | /** |
599 | * Adds a warning entry to the swaynag instance used for errors. | 636 | * Adds a warning entry to the swaynag instance used for errors. |
600 | */ | 637 | */ |
601 | void config_add_swaynag_warning(char *fmt, ...); | 638 | void config_add_swaynag_warning(char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); |
602 | 639 | ||
603 | /** | 640 | /** |
604 | * Free config struct | 641 | * Free config struct |
@@ -651,20 +688,22 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt | |||
651 | 688 | ||
652 | struct output_config *new_output_config(const char *name); | 689 | struct output_config *new_output_config(const char *name); |
653 | 690 | ||
654 | void merge_output_config(struct output_config *dst, struct output_config *src); | 691 | bool apply_output_configs(struct matched_output_config *configs, |
692 | size_t configs_len, bool test_only); | ||
655 | 693 | ||
656 | bool apply_output_config(struct output_config *oc, struct sway_output *output); | 694 | void apply_all_output_configs(void); |
657 | 695 | ||
658 | bool test_output_config(struct output_config *oc, struct sway_output *output); | 696 | /** |
659 | 697 | * store_output_config stores a new output config. An output may be matched by | |
660 | struct output_config *store_output_config(struct output_config *oc); | 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); | ||
661 | 704 | ||
662 | struct output_config *find_output_config(struct sway_output *output); | 705 | struct output_config *find_output_config(struct sway_output *output); |
663 | 706 | ||
664 | void apply_output_config_to_outputs(struct output_config *oc); | ||
665 | |||
666 | void reset_outputs(void); | ||
667 | |||
668 | void free_output_config(struct output_config *oc); | 707 | void free_output_config(struct output_config *oc); |
669 | 708 | ||
670 | bool spawn_swaybg(void); | 709 | bool spawn_swaybg(void); |
@@ -675,6 +714,8 @@ void free_sway_binding(struct sway_binding *sb); | |||
675 | 714 | ||
676 | void free_switch_binding(struct sway_switch_binding *binding); | 715 | void free_switch_binding(struct sway_switch_binding *binding); |
677 | 716 | ||
717 | void free_gesture_binding(struct sway_gesture_binding *binding); | ||
718 | |||
678 | void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); | 719 | void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); |
679 | 720 | ||
680 | void load_swaybar(struct bar_config *bar); | 721 | void load_swaybar(struct bar_config *bar); |
@@ -690,14 +731,13 @@ void free_bar_binding(struct bar_binding *binding); | |||
690 | void free_workspace_config(struct workspace_config *wsc); | 731 | void free_workspace_config(struct workspace_config *wsc); |
691 | 732 | ||
692 | /** | 733 | /** |
693 | * Updates the value of config->font_height based on the max title height | 734 | * Updates the value of config->font_height based on the metrics for title's |
694 | * reported by each container. If recalculate is true, the containers will | 735 | * font as reported by pango. |
695 | * recalculate their heights before reporting. | ||
696 | * | 736 | * |
697 | * 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 |
698 | * new size. | 738 | * new size. |
699 | */ | 739 | */ |
700 | void config_update_font_height(bool recalculate); | 740 | void config_update_font_height(void); |
701 | 741 | ||
702 | /** | 742 | /** |
703 | * Convert bindsym into bindcode using the first configured layout. | 743 | * Convert bindsym into bindcode using the first configured layout. |
diff --git a/include/sway/criteria.h b/include/sway/criteria.h index ad8610cd..8da345ea 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h | |||
@@ -1,7 +1,8 @@ | |||
1 | #ifndef _SWAY_CRITERIA_H | 1 | #ifndef _SWAY_CRITERIA_H |
2 | #define _SWAY_CRITERIA_H | 2 | #define _SWAY_CRITERIA_H |
3 | 3 | ||
4 | #include <pcre.h> | 4 | #define PCRE2_CODE_UNIT_WIDTH 8 |
5 | #include <pcre2.h> | ||
5 | #include "config.h" | 6 | #include "config.h" |
6 | #include "list.h" | 7 | #include "list.h" |
7 | #include "tree/view.h" | 8 | #include "tree/view.h" |
@@ -15,13 +16,13 @@ enum criteria_type { | |||
15 | }; | 16 | }; |
16 | 17 | ||
17 | enum pattern_type { | 18 | enum pattern_type { |
18 | PATTERN_PCRE, | 19 | PATTERN_PCRE2, |
19 | PATTERN_FOCUSED, | 20 | PATTERN_FOCUSED, |
20 | }; | 21 | }; |
21 | 22 | ||
22 | struct pattern { | 23 | struct pattern { |
23 | enum pattern_type match_type; | 24 | enum pattern_type match_type; |
24 | pcre *regex; | 25 | pcre2_code *regex; |
25 | }; | 26 | }; |
26 | 27 | ||
27 | struct criteria { | 28 | struct criteria { |
@@ -42,6 +43,7 @@ struct criteria { | |||
42 | struct pattern *window_role; | 43 | struct pattern *window_role; |
43 | enum atom_name window_type; | 44 | enum atom_name window_type; |
44 | #endif | 45 | #endif |
46 | bool all; | ||
45 | bool floating; | 47 | bool floating; |
46 | bool tiling; | 48 | bool tiling; |
47 | char urgent; // 'l' for latest or 'o' for oldest | 49 | char urgent; // 'l' for latest or 'o' for oldest |
diff --git a/include/sway/desktop.h b/include/sway/desktop.h deleted file mode 100644 index c969a76b..00000000 --- a/include/sway/desktop.h +++ /dev/null | |||
@@ -1,13 +0,0 @@ | |||
1 | #include <wlr/types/wlr_surface.h> | ||
2 | |||
3 | struct sway_container; | ||
4 | struct sway_view; | ||
5 | |||
6 | void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, | ||
7 | bool whole); | ||
8 | |||
9 | void desktop_damage_whole_container(struct sway_container *con); | ||
10 | |||
11 | void desktop_damage_box(struct wlr_box *box); | ||
12 | |||
13 | void desktop_damage_view(struct sway_view *view); | ||
diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index 0adafdb9..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,10 @@ 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; | 20 | struct wlr_idle_inhibitor_v1 *wlr_inhibitor; |
25 | struct sway_view *view; | 21 | struct sway_view *view; |
26 | enum sway_idle_inhibit_mode mode; | 22 | enum sway_idle_inhibit_mode mode; |
27 | 23 | ||
@@ -32,8 +28,7 @@ struct sway_idle_inhibitor_v1 { | |||
32 | bool sway_idle_inhibit_v1_is_active( | 28 | bool sway_idle_inhibit_v1_is_active( |
33 | struct sway_idle_inhibitor_v1 *inhibitor); | 29 | struct sway_idle_inhibitor_v1 *inhibitor); |
34 | 30 | ||
35 | void sway_idle_inhibit_v1_check_active( | 31 | void sway_idle_inhibit_v1_check_active(void); |
36 | struct sway_idle_inhibit_manager_v1 *manager); | ||
37 | 32 | ||
38 | 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, |
39 | enum sway_idle_inhibit_mode mode); | 34 | enum sway_idle_inhibit_mode mode); |
@@ -47,6 +42,6 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_vi | |||
47 | void sway_idle_inhibit_v1_user_inhibitor_destroy( | 42 | void sway_idle_inhibit_v1_user_inhibitor_destroy( |
48 | struct sway_idle_inhibitor_v1 *inhibitor); | 43 | struct sway_idle_inhibitor_v1 *inhibitor); |
49 | 44 | ||
50 | struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( | 45 | bool sway_idle_inhibit_manager_v1_init(void); |
51 | struct wl_display *wl_display, struct wlr_idle *idle); | 46 | |
52 | #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 175489c5..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. |
@@ -28,12 +30,21 @@ struct sway_view; | |||
28 | */ | 30 | */ |
29 | void transaction_commit_dirty(void); | 31 | void transaction_commit_dirty(void); |
30 | 32 | ||
33 | /* | ||
34 | * Same as transaction_commit_dirty, but signalling that this is a | ||
35 | * client-initiated change has already taken effect. | ||
36 | */ | ||
37 | void transaction_commit_dirty_client(void); | ||
38 | |||
31 | /** | 39 | /** |
32 | * 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. |
33 | * | 41 | * |
34 | * 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. | ||
35 | */ | 46 | */ |
36 | void transaction_notify_view_ready_by_serial(struct sway_view *view, | 47 | bool transaction_notify_view_ready_by_serial(struct sway_view *view, |
37 | uint32_t serial); | 48 | uint32_t serial); |
38 | 49 | ||
39 | /** | 50 | /** |
@@ -41,14 +52,13 @@ void transaction_notify_view_ready_by_serial(struct sway_view *view, | |||
41 | * identifying the instruction by geometry rather than by serial. | 52 | * identifying the instruction by geometry rather than by serial. |
42 | * | 53 | * |
43 | * 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. | ||
44 | */ | 58 | */ |
45 | void transaction_notify_view_ready_by_geometry(struct sway_view *view, | 59 | bool transaction_notify_view_ready_by_geometry(struct sway_view *view, |
46 | double x, double y, int width, int height); | 60 | double x, double y, int width, int height); |
47 | 61 | ||
48 | /** | 62 | void arrange_popups(struct wlr_scene_tree *popups); |
49 | * Unconditionally notify the transaction system that a view is ready for the | ||
50 | * new layout. | ||
51 | */ | ||
52 | void transaction_notify_view_ready_immediately(struct sway_view *view); | ||
53 | 63 | ||
54 | #endif | 64 | #endif |
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 6a38190b..527d0350 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h | |||
@@ -4,7 +4,7 @@ | |||
4 | #include <stdint.h> | 4 | #include <stdint.h> |
5 | #include <wlr/types/wlr_pointer_constraints_v1.h> | 5 | #include <wlr/types/wlr_pointer_constraints_v1.h> |
6 | #include <wlr/types/wlr_pointer_gestures_v1.h> | 6 | #include <wlr/types/wlr_pointer_gestures_v1.h> |
7 | #include <wlr/types/wlr_surface.h> | 7 | #include <wlr/types/wlr_compositor.h> |
8 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
9 | #include "config.h" | 9 | #include "config.h" |
10 | 10 | ||
@@ -35,7 +35,8 @@ struct sway_cursor { | |||
35 | pixman_region32_t confine; // invalid if active_constraint == NULL | 35 | pixman_region32_t confine; // invalid if active_constraint == NULL |
36 | bool active_confine_requires_warp; | 36 | bool active_confine_requires_warp; |
37 | 37 | ||
38 | struct wlr_pointer_gestures_v1 *pointer_gestures; | 38 | struct wl_listener hold_begin; |
39 | struct wl_listener hold_end; | ||
39 | struct wl_listener pinch_begin; | 40 | struct wl_listener pinch_begin; |
40 | struct wl_listener pinch_update; | 41 | struct wl_listener pinch_update; |
41 | struct wl_listener pinch_end; | 42 | struct wl_listener pinch_end; |
@@ -51,8 +52,11 @@ struct sway_cursor { | |||
51 | 52 | ||
52 | struct wl_listener touch_down; | 53 | struct wl_listener touch_down; |
53 | struct wl_listener touch_up; | 54 | struct wl_listener touch_up; |
55 | struct wl_listener touch_cancel; | ||
54 | struct wl_listener touch_motion; | 56 | struct wl_listener touch_motion; |
57 | struct wl_listener touch_frame; | ||
55 | bool simulating_pointer_from_touch; | 58 | bool simulating_pointer_from_touch; |
59 | bool pointer_touch_up; | ||
56 | int32_t pointer_touch_id; | 60 | int32_t pointer_touch_id; |
57 | 61 | ||
58 | struct wl_listener tool_axis; | 62 | struct wl_listener tool_axis; |
@@ -60,6 +64,7 @@ struct sway_cursor { | |||
60 | struct wl_listener tool_proximity; | 64 | struct wl_listener tool_proximity; |
61 | struct wl_listener tool_button; | 65 | struct wl_listener tool_button; |
62 | bool simulating_pointer_from_tool_tip; | 66 | bool simulating_pointer_from_tool_tip; |
67 | bool simulating_pointer_from_tool_button; | ||
63 | uint32_t tool_buttons; | 68 | uint32_t tool_buttons; |
64 | 69 | ||
65 | struct wl_listener request_set_cursor; | 70 | struct wl_listener request_set_cursor; |
@@ -103,12 +108,16 @@ void cursor_unhide(struct sway_cursor *cursor); | |||
103 | int cursor_get_timeout(struct sway_cursor *cursor); | 108 | int cursor_get_timeout(struct sway_cursor *cursor); |
104 | void cursor_notify_key_press(struct sway_cursor *cursor); | 109 | void cursor_notify_key_press(struct sway_cursor *cursor); |
105 | 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 | |||
106 | void dispatch_cursor_button(struct sway_cursor *cursor, | 115 | void dispatch_cursor_button(struct sway_cursor *cursor, |
107 | 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, |
108 | enum wlr_button_state state); | 117 | enum wl_pointer_button_state state); |
109 | 118 | ||
110 | void dispatch_cursor_axis(struct sway_cursor *cursor, | 119 | void dispatch_cursor_axis(struct sway_cursor *cursor, |
111 | struct wlr_event_pointer_axis *event); | 120 | struct wlr_pointer_axis_event *event); |
112 | 121 | ||
113 | void cursor_set_image(struct sway_cursor *cursor, const char *image, | 122 | void cursor_set_image(struct sway_cursor *cursor, const char *image, |
114 | struct wl_client *client); | 123 | struct wl_client *client); |
@@ -136,4 +145,6 @@ uint32_t get_mouse_button(const char *name, char **error); | |||
136 | 145 | ||
137 | const char *get_mouse_button_name(uint32_t button); | 146 | const char *get_mouse_button_name(uint32_t button); |
138 | 147 | ||
148 | void handle_request_set_cursor_shape(struct wl_listener *listener, void *data); | ||
149 | |||
139 | #endif | 150 | #endif |
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index c9bd08f0..45c75199 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h | |||
@@ -1,10 +1,10 @@ | |||
1 | #ifndef _SWAY_INPUT_INPUT_MANAGER_H | 1 | #ifndef _SWAY_INPUT_INPUT_MANAGER_H |
2 | #define _SWAY_INPUT_INPUT_MANAGER_H | 2 | #define _SWAY_INPUT_INPUT_MANAGER_H |
3 | #include <libinput.h> | 3 | #include <libinput.h> |
4 | #include <wlr/types/wlr_input_inhibitor.h> | ||
5 | #include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> | 4 | #include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> |
6 | #include <wlr/types/wlr_virtual_keyboard_v1.h> | 5 | #include <wlr/types/wlr_virtual_keyboard_v1.h> |
7 | #include <wlr/types/wlr_virtual_pointer_v1.h> | 6 | #include <wlr/types/wlr_virtual_pointer_v1.h> |
7 | #include <wlr/types/wlr_transient_seat_v1.h> | ||
8 | #include "sway/server.h" | 8 | #include "sway/server.h" |
9 | #include "sway/config.h" | 9 | #include "sway/config.h" |
10 | #include "list.h" | 10 | #include "list.h" |
@@ -21,10 +21,11 @@ struct sway_input_manager { | |||
21 | struct wl_list devices; | 21 | struct wl_list devices; |
22 | struct wl_list seats; | 22 | struct wl_list seats; |
23 | 23 | ||
24 | struct wlr_input_inhibit_manager *inhibit; | ||
25 | struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; | 24 | struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; |
26 | struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; | 25 | struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; |
27 | struct wlr_virtual_pointer_manager_v1 *virtual_pointer; | 26 | struct wlr_virtual_pointer_manager_v1 *virtual_pointer; |
27 | struct wlr_pointer_gestures_v1 *pointer_gestures; | ||
28 | struct wlr_transient_seat_manager_v1 *transient_seat_manager; | ||
28 | 29 | ||
29 | struct wl_listener new_input; | 30 | struct wl_listener new_input; |
30 | struct wl_listener inhibit_activate; | 31 | struct wl_listener inhibit_activate; |
@@ -32,6 +33,7 @@ struct sway_input_manager { | |||
32 | struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor; | 33 | struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor; |
33 | struct wl_listener virtual_keyboard_new; | 34 | struct wl_listener virtual_keyboard_new; |
34 | struct wl_listener virtual_pointer_new; | 35 | struct wl_listener virtual_pointer_new; |
36 | struct wl_listener transient_seat_create; | ||
35 | }; | 37 | }; |
36 | 38 | ||
37 | struct sway_input_manager *input_manager_create(struct sway_server *server); | 39 | struct sway_input_manager *input_manager_create(struct sway_server *server); |
@@ -44,7 +46,7 @@ void input_manager_configure_xcursor(void); | |||
44 | 46 | ||
45 | void input_manager_apply_input_config(struct input_config *input_config); | 47 | void input_manager_apply_input_config(struct input_config *input_config); |
46 | 48 | ||
47 | void input_manager_configure_all_inputs(void); | 49 | void input_manager_configure_all_input_mappings(void); |
48 | 50 | ||
49 | void input_manager_reset_input(struct sway_input_device *input_device); | 51 | void input_manager_reset_input(struct sway_input_device *input_device); |
50 | 52 | ||
diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index 2c61e5a7..571d9e6f 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h | |||
@@ -50,6 +50,7 @@ struct sway_shortcut_state { | |||
50 | 50 | ||
51 | struct sway_keyboard { | 51 | struct sway_keyboard { |
52 | struct sway_seat_device *seat_device; | 52 | struct sway_seat_device *seat_device; |
53 | struct wlr_keyboard *wlr; | ||
53 | 54 | ||
54 | struct xkb_keymap *keymap; | 55 | struct xkb_keymap *keymap; |
55 | xkb_layout_index_t effective_layout; | 56 | xkb_layout_index_t effective_layout; |
diff --git a/include/sway/input/libinput.h b/include/sway/input/libinput.h index de019976..1f84a8e3 100644 --- a/include/sway/input/libinput.h +++ b/include/sway/input/libinput.h | |||
@@ -2,8 +2,13 @@ | |||
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 | ||
12 | bool sway_libinput_device_is_builtin(struct sway_input_device *device); | ||
13 | |||
9 | #endif | 14 | #endif |
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 4118df66..428f9679 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h | |||
@@ -3,7 +3,9 @@ | |||
3 | 3 | ||
4 | #include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> | 4 | #include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> |
5 | #include <wlr/types/wlr_layer_shell_v1.h> | 5 | #include <wlr/types/wlr_layer_shell_v1.h> |
6 | #include <wlr/types/wlr_scene.h> | ||
6 | #include <wlr/types/wlr_seat.h> | 7 | #include <wlr/types/wlr_seat.h> |
8 | #include <wlr/types/wlr_touch.h> | ||
7 | #include <wlr/util/edges.h> | 9 | #include <wlr/util/edges.h> |
8 | #include "sway/config.h" | 10 | #include "sway/config.h" |
9 | #include "sway/input/input-manager.h" | 11 | #include "sway/input/input-manager.h" |
@@ -15,19 +17,41 @@ struct sway_seat; | |||
15 | struct sway_seatop_impl { | 17 | struct sway_seatop_impl { |
16 | void (*button)(struct sway_seat *seat, uint32_t time_msec, | 18 | void (*button)(struct sway_seat *seat, uint32_t time_msec, |
17 | struct wlr_input_device *device, uint32_t button, | 19 | struct wlr_input_device *device, uint32_t button, |
18 | enum wlr_button_state state); | 20 | enum wl_pointer_button_state state); |
19 | void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); | 21 | void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); |
20 | void (*pointer_axis)(struct sway_seat *seat, | 22 | void (*pointer_axis)(struct sway_seat *seat, |
21 | struct wlr_event_pointer_axis *event); | 23 | struct wlr_pointer_axis_event *event); |
24 | void (*hold_begin)(struct sway_seat *seat, | ||
25 | struct wlr_pointer_hold_begin_event *event); | ||
26 | void (*hold_end)(struct sway_seat *seat, | ||
27 | struct wlr_pointer_hold_end_event *event); | ||
28 | void (*pinch_begin)(struct sway_seat *seat, | ||
29 | struct wlr_pointer_pinch_begin_event *event); | ||
30 | void (*pinch_update)(struct sway_seat *seat, | ||
31 | struct wlr_pointer_pinch_update_event *event); | ||
32 | void (*pinch_end)(struct sway_seat *seat, | ||
33 | struct wlr_pointer_pinch_end_event *event); | ||
34 | void (*swipe_begin)(struct sway_seat *seat, | ||
35 | struct wlr_pointer_swipe_begin_event *event); | ||
36 | void (*swipe_update)(struct sway_seat *seat, | ||
37 | struct wlr_pointer_swipe_update_event *event); | ||
38 | void (*swipe_end)(struct sway_seat *seat, | ||
39 | struct wlr_pointer_swipe_end_event *event); | ||
22 | void (*rebase)(struct sway_seat *seat, uint32_t time_msec); | 40 | void (*rebase)(struct sway_seat *seat, uint32_t time_msec); |
41 | void (*touch_motion)(struct sway_seat *seat, | ||
42 | struct wlr_touch_motion_event *event, double lx, double ly); | ||
43 | void (*touch_up)(struct sway_seat *seat, | ||
44 | struct wlr_touch_up_event *event); | ||
45 | void (*touch_down)(struct sway_seat *seat, | ||
46 | struct wlr_touch_down_event *event, double lx, double ly); | ||
47 | void (*touch_cancel)(struct sway_seat *seat, | ||
48 | struct wlr_touch_cancel_event *event); | ||
23 | void (*tablet_tool_motion)(struct sway_seat *seat, | 49 | void (*tablet_tool_motion)(struct sway_seat *seat, |
24 | struct sway_tablet_tool *tool, uint32_t time_msec); | 50 | struct sway_tablet_tool *tool, uint32_t time_msec); |
25 | void (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool, | 51 | void (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool, |
26 | uint32_t time_msec, enum wlr_tablet_tool_tip_state state); | 52 | uint32_t time_msec, enum wlr_tablet_tool_tip_state state); |
27 | void (*end)(struct sway_seat *seat); | 53 | void (*end)(struct sway_seat *seat); |
28 | void (*unref)(struct sway_seat *seat, struct sway_container *con); | 54 | void (*unref)(struct sway_seat *seat, struct sway_container *con); |
29 | void (*render)(struct sway_seat *seat, struct sway_output *output, | ||
30 | pixman_region32_t *damage); | ||
31 | bool allow_set_cursor; | 55 | bool allow_set_cursor; |
32 | }; | 56 | }; |
33 | 57 | ||
@@ -50,19 +74,6 @@ struct sway_seat_node { | |||
50 | struct wl_listener destroy; | 74 | struct wl_listener destroy; |
51 | }; | 75 | }; |
52 | 76 | ||
53 | struct sway_drag_icon { | ||
54 | struct sway_seat *seat; | ||
55 | struct wlr_drag_icon *wlr_drag_icon; | ||
56 | struct wl_list link; // sway_root::drag_icons | ||
57 | |||
58 | double x, y; // in layout-local coordinates | ||
59 | |||
60 | struct wl_listener surface_commit; | ||
61 | struct wl_listener map; | ||
62 | struct wl_listener unmap; | ||
63 | struct wl_listener destroy; | ||
64 | }; | ||
65 | |||
66 | struct sway_drag { | 77 | struct sway_drag { |
67 | struct sway_seat *seat; | 78 | struct sway_seat *seat; |
68 | struct wlr_drag *wlr_drag; | 79 | struct wlr_drag *wlr_drag; |
@@ -73,16 +84,23 @@ struct sway_seat { | |||
73 | struct wlr_seat *wlr_seat; | 84 | struct wlr_seat *wlr_seat; |
74 | struct sway_cursor *cursor; | 85 | struct sway_cursor *cursor; |
75 | 86 | ||
87 | // Seat scene tree structure | ||
88 | // - scene_tree | ||
89 | // - drag icons | ||
90 | // - drag icon 1 | ||
91 | // - drag icon 2 | ||
92 | // - seatop specific stuff | ||
93 | struct wlr_scene_tree *scene_tree; | ||
94 | struct wlr_scene_tree *drag_icons; | ||
95 | |||
76 | bool has_focus; | 96 | bool has_focus; |
77 | struct wl_list focus_stack; // list of containers in focus order | 97 | struct wl_list focus_stack; // list of containers in focus order |
78 | struct sway_workspace *workspace; | 98 | struct sway_workspace *workspace; |
79 | char *prev_workspace_name; // for workspace back_and_forth | 99 | char *prev_workspace_name; // for workspace back_and_forth |
80 | 100 | ||
81 | // If the focused layer is set, views cannot receive keyboard focus | ||
82 | struct wlr_layer_surface_v1 *focused_layer; | 101 | struct wlr_layer_surface_v1 *focused_layer; |
83 | 102 | // If the exclusive layer is set, views cannot receive keyboard focus | |
84 | // If exclusive_client is set, no other clients will receive input events | 103 | bool has_exclusive_layer; |
85 | struct wl_client *exclusive_client; | ||
86 | 104 | ||
87 | // Last touch point | 105 | // Last touch point |
88 | int32_t touch_id; | 106 | int32_t touch_id; |
@@ -106,6 +124,7 @@ struct sway_seat { | |||
106 | struct wl_listener start_drag; | 124 | struct wl_listener start_drag; |
107 | struct wl_listener request_set_selection; | 125 | struct wl_listener request_set_selection; |
108 | struct wl_listener request_set_primary_selection; | 126 | struct wl_listener request_set_primary_selection; |
127 | struct wl_listener destroy; | ||
109 | 128 | ||
110 | struct wl_list devices; // sway_seat_device::link | 129 | struct wl_list devices; // sway_seat_device::link |
111 | struct wl_list keyboard_groups; // sway_keyboard_group::link | 130 | struct wl_list keyboard_groups; // sway_keyboard_group::link |
@@ -141,6 +160,9 @@ void seat_add_device(struct sway_seat *seat, | |||
141 | void seat_configure_device(struct sway_seat *seat, | 160 | void seat_configure_device(struct sway_seat *seat, |
142 | struct sway_input_device *device); | 161 | struct sway_input_device *device); |
143 | 162 | ||
163 | void seat_configure_device_mapping(struct sway_seat *seat, | ||
164 | struct sway_input_device *input_device); | ||
165 | |||
144 | void seat_reset_device(struct sway_seat *seat, | 166 | void seat_reset_device(struct sway_seat *seat, |
145 | struct sway_input_device *input_device); | 167 | struct sway_input_device *input_device); |
146 | 168 | ||
@@ -171,8 +193,7 @@ void seat_set_focus_surface(struct sway_seat *seat, | |||
171 | void seat_set_focus_layer(struct sway_seat *seat, | 193 | void seat_set_focus_layer(struct sway_seat *seat, |
172 | struct wlr_layer_surface_v1 *layer); | 194 | struct wlr_layer_surface_v1 *layer); |
173 | 195 | ||
174 | void seat_set_exclusive_client(struct sway_seat *seat, | 196 | void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client); |
175 | struct wl_client *client); | ||
176 | 197 | ||
177 | struct sway_node *seat_get_focus(struct sway_seat *seat); | 198 | struct sway_node *seat_get_focus(struct sway_seat *seat); |
178 | 199 | ||
@@ -231,7 +252,7 @@ void seat_idle_notify_activity(struct sway_seat *seat, | |||
231 | 252 | ||
232 | bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); | 253 | bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); |
233 | 254 | ||
234 | void drag_icon_update_position(struct sway_drag_icon *icon); | 255 | void drag_icons_update_position(struct sway_seat *seat); |
235 | 256 | ||
236 | enum wlr_edges find_resize_edge(struct sway_container *cont, | 257 | enum wlr_edges find_resize_edge(struct sway_container *cont, |
237 | struct wlr_surface *surface, struct sway_cursor *cursor); | 258 | struct wlr_surface *surface, struct sway_cursor *cursor); |
@@ -239,7 +260,13 @@ enum wlr_edges find_resize_edge(struct sway_container *cont, | |||
239 | void seatop_begin_default(struct sway_seat *seat); | 260 | void seatop_begin_default(struct sway_seat *seat); |
240 | 261 | ||
241 | void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, | 262 | void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, |
242 | uint32_t time_msec, int sx, int sy); | 263 | double sx, double sy); |
264 | |||
265 | void seatop_begin_down_on_surface(struct sway_seat *seat, | ||
266 | struct wlr_surface *surface, double sx, double sy); | ||
267 | |||
268 | void seatop_begin_touch_down(struct sway_seat *seat, struct wlr_surface *surface, | ||
269 | struct wlr_touch_down_event *event, double sx, double sy, double lx, double ly); | ||
243 | 270 | ||
244 | void seatop_begin_move_floating(struct sway_seat *seat, | 271 | void seatop_begin_move_floating(struct sway_seat *seat, |
245 | struct sway_container *con); | 272 | struct sway_container *con); |
@@ -260,18 +287,18 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, | |||
260 | struct sway_workspace *workspace); | 287 | struct sway_workspace *workspace); |
261 | 288 | ||
262 | void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, | 289 | void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, |
263 | uint32_t button, enum wlr_button_state state); | 290 | uint32_t button, enum wl_pointer_button_state state); |
264 | 291 | ||
265 | void seat_consider_warp_to_focus(struct sway_seat *seat); | 292 | void seat_consider_warp_to_focus(struct sway_seat *seat); |
266 | 293 | ||
267 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, | 294 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, |
268 | struct wlr_input_device *device, uint32_t button, | 295 | struct wlr_input_device *device, uint32_t button, |
269 | enum wlr_button_state state); | 296 | enum wl_pointer_button_state state); |
270 | 297 | ||
271 | void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); | 298 | void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); |
272 | 299 | ||
273 | void seatop_pointer_axis(struct sway_seat *seat, | 300 | void seatop_pointer_axis(struct sway_seat *seat, |
274 | struct wlr_event_pointer_axis *event); | 301 | struct wlr_pointer_axis_event *event); |
275 | 302 | ||
276 | void seatop_tablet_tool_tip(struct sway_seat *seat, | 303 | void seatop_tablet_tool_tip(struct sway_seat *seat, |
277 | struct sway_tablet_tool *tool, uint32_t time_msec, | 304 | struct sway_tablet_tool *tool, uint32_t time_msec, |
@@ -280,6 +307,37 @@ void seatop_tablet_tool_tip(struct sway_seat *seat, | |||
280 | void seatop_tablet_tool_motion(struct sway_seat *seat, | 307 | void seatop_tablet_tool_motion(struct sway_seat *seat, |
281 | struct sway_tablet_tool *tool, uint32_t time_msec); | 308 | struct sway_tablet_tool *tool, uint32_t time_msec); |
282 | 309 | ||
310 | void seatop_hold_begin(struct sway_seat *seat, | ||
311 | struct wlr_pointer_hold_begin_event *event); | ||
312 | void seatop_hold_end(struct sway_seat *seat, | ||
313 | struct wlr_pointer_hold_end_event *event); | ||
314 | |||
315 | void seatop_pinch_begin(struct sway_seat *seat, | ||
316 | struct wlr_pointer_pinch_begin_event *event); | ||
317 | void seatop_pinch_update(struct sway_seat *seat, | ||
318 | struct wlr_pointer_pinch_update_event *event); | ||
319 | void seatop_pinch_end(struct sway_seat *seat, | ||
320 | struct wlr_pointer_pinch_end_event *event); | ||
321 | |||
322 | void seatop_swipe_begin(struct sway_seat *seat, | ||
323 | struct wlr_pointer_swipe_begin_event *event); | ||
324 | void seatop_swipe_update(struct sway_seat *seat, | ||
325 | struct wlr_pointer_swipe_update_event *event); | ||
326 | void seatop_swipe_end(struct sway_seat *seat, | ||
327 | struct wlr_pointer_swipe_end_event *event); | ||
328 | |||
329 | void seatop_touch_motion(struct sway_seat *seat, | ||
330 | struct wlr_touch_motion_event *event, double lx, double ly); | ||
331 | |||
332 | void seatop_touch_up(struct sway_seat *seat, | ||
333 | struct wlr_touch_up_event *event); | ||
334 | |||
335 | void seatop_touch_down(struct sway_seat *seat, | ||
336 | struct wlr_touch_down_event *event, double lx, double ly); | ||
337 | |||
338 | void seatop_touch_cancel(struct sway_seat *seat, | ||
339 | struct wlr_touch_cancel_event *event); | ||
340 | |||
283 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); | 341 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); |
284 | 342 | ||
285 | /** | 343 | /** |
@@ -294,13 +352,6 @@ void seatop_end(struct sway_seat *seat); | |||
294 | */ | 352 | */ |
295 | void seatop_unref(struct sway_seat *seat, struct sway_container *con); | 353 | void seatop_unref(struct sway_seat *seat, struct sway_container *con); |
296 | 354 | ||
297 | /** | ||
298 | * Instructs a seatop to render anything that it needs to render | ||
299 | * (eg. dropzone for move-tiling) | ||
300 | */ | ||
301 | void seatop_render(struct sway_seat *seat, struct sway_output *output, | ||
302 | pixman_region32_t *damage); | ||
303 | |||
304 | bool seatop_allows_set_cursor(struct sway_seat *seat); | 355 | bool seatop_allows_set_cursor(struct sway_seat *seat); |
305 | 356 | ||
306 | /** | 357 | /** |
diff --git a/include/sway/input/switch.h b/include/sway/input/switch.h index 213b471d..de6787b7 100644 --- a/include/sway/input/switch.h +++ b/include/sway/input/switch.h | |||
@@ -5,6 +5,7 @@ | |||
5 | 5 | ||
6 | struct sway_switch { | 6 | struct sway_switch { |
7 | struct sway_seat_device *seat_device; | 7 | struct sway_seat_device *seat_device; |
8 | struct wlr_switch *wlr; | ||
8 | enum wlr_switch_state state; | 9 | enum wlr_switch_state state; |
9 | enum wlr_switch_type type; | 10 | enum wlr_switch_type type; |
10 | 11 | ||
diff --git a/include/sway/input/tablet.h b/include/sway/input/tablet.h index d7e4c242..2fa5db6d 100644 --- a/include/sway/input/tablet.h +++ b/include/sway/input/tablet.h | |||
@@ -32,6 +32,7 @@ struct sway_tablet_pad { | |||
32 | struct wl_list link; | 32 | struct wl_list link; |
33 | struct sway_seat_device *seat_device; | 33 | struct sway_seat_device *seat_device; |
34 | struct sway_tablet *tablet; | 34 | struct sway_tablet *tablet; |
35 | struct wlr_tablet_pad *wlr; | ||
35 | struct wlr_tablet_v2_tablet_pad *tablet_v2_pad; | 36 | struct wlr_tablet_v2_tablet_pad *tablet_v2_pad; |
36 | 37 | ||
37 | struct wl_listener attach; | 38 | struct wl_listener attach; |
@@ -62,7 +63,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad); | |||
62 | 63 | ||
63 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad); | 64 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad); |
64 | 65 | ||
65 | void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | 66 | void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad, |
66 | struct wlr_surface *surface); | 67 | struct wlr_surface *surface); |
67 | 68 | ||
68 | #endif | 69 | #endif |
diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h index 6cf9bdb3..1993f928 100644 --- a/include/sway/input/text_input.h +++ b/include/sway/input/text_input.h | |||
@@ -3,13 +3,12 @@ | |||
3 | 3 | ||
4 | #include <wlr/types/wlr_text_input_v3.h> | 4 | #include <wlr/types/wlr_text_input_v3.h> |
5 | #include <wlr/types/wlr_input_method_v2.h> | 5 | #include <wlr/types/wlr_input_method_v2.h> |
6 | #include <wlr/types/wlr_surface.h> | 6 | #include <wlr/types/wlr_compositor.h> |
7 | #include "sway/input/seat.h" | ||
8 | 7 | ||
9 | /** | 8 | /** |
10 | * The relay structure manages the relationship between text-input and | 9 | * The relay structure manages the relationship between text-input and |
11 | * input_method interfaces on a given seat. Multiple text-input interfaces may | 10 | * input_method interfaces on a given seat. Multiple text-input interfaces may |
12 | * be bound to a relay, but at most one will be focused (reveiving events) at | 11 | * be bound to a relay, but at most one will be focused (receiving events) at |
13 | * a time. At most one input-method interface may be bound to the seat. The | 12 | * a time. At most one input-method interface may be bound to the seat. The |
14 | * relay manages life cycle of both sides. When both sides are present and | 13 | * relay manages life cycle of both sides. When both sides are present and |
15 | * focused, the relay passes messages between them. | 14 | * focused, the relay passes messages between them. |
@@ -22,15 +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; | ||
32 | struct wl_listener input_method_grab_keyboard; | ||
31 | struct wl_listener input_method_destroy; | 33 | struct wl_listener input_method_destroy; |
34 | |||
35 | struct wl_listener input_method_keyboard_grab_destroy; | ||
32 | }; | 36 | }; |
33 | 37 | ||
38 | |||
34 | struct sway_text_input { | 39 | struct sway_text_input { |
35 | struct sway_input_method_relay *relay; | 40 | struct sway_input_method_relay *relay; |
36 | 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 457634c2..fd6384e0 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h | |||
@@ -1,59 +1,44 @@ | |||
1 | #ifndef _SWAY_LAYERS_H | 1 | #ifndef _SWAY_LAYERS_H |
2 | #define _SWAY_LAYERS_H | 2 | #define _SWAY_LAYERS_H |
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include <wlr/types/wlr_box.h> | 4 | #include <wlr/types/wlr_compositor.h> |
5 | #include <wlr/types/wlr_surface.h> | ||
6 | #include <wlr/types/wlr_layer_shell_v1.h> | 5 | #include <wlr/types/wlr_layer_shell_v1.h> |
7 | 6 | #include "sway/tree/view.h" | |
8 | enum layer_parent { | ||
9 | LAYER_PARENT_LAYER, | ||
10 | LAYER_PARENT_POPUP, | ||
11 | }; | ||
12 | 7 | ||
13 | struct sway_layer_surface { | 8 | struct sway_layer_surface { |
14 | struct wlr_layer_surface_v1 *layer_surface; | ||
15 | struct wl_list link; | ||
16 | |||
17 | struct wl_listener destroy; | ||
18 | struct wl_listener map; | 9 | struct wl_listener map; |
19 | struct wl_listener unmap; | 10 | struct wl_listener unmap; |
20 | struct wl_listener surface_commit; | 11 | struct wl_listener surface_commit; |
21 | struct wl_listener output_destroy; | 12 | struct wl_listener output_destroy; |
13 | struct wl_listener node_destroy; | ||
22 | struct wl_listener new_popup; | 14 | struct wl_listener new_popup; |
23 | struct wl_listener new_subsurface; | ||
24 | 15 | ||
25 | struct wlr_box geo; | 16 | bool mapped; |
26 | enum zwlr_layer_shell_v1_layer layer; | 17 | |
18 | struct wlr_scene_tree *popups; | ||
19 | struct sway_popup_desc desc; | ||
20 | |||
21 | struct sway_output *output; | ||
22 | struct wlr_scene_layer_surface_v1 *scene; | ||
23 | struct wlr_scene_tree *tree; | ||
24 | struct wlr_layer_surface_v1 *layer_surface; | ||
27 | }; | 25 | }; |
28 | 26 | ||
29 | struct sway_layer_popup { | 27 | struct sway_layer_popup { |
30 | struct wlr_xdg_popup *wlr_popup; | 28 | struct wlr_xdg_popup *wlr_popup; |
31 | enum layer_parent parent_type; | 29 | struct wlr_scene_tree *scene; |
32 | union { | 30 | struct sway_layer_surface *toplevel; |
33 | struct sway_layer_surface *parent_layer; | ||
34 | struct sway_layer_popup *parent_popup; | ||
35 | }; | ||
36 | struct wl_listener map; | ||
37 | struct wl_listener unmap; | ||
38 | struct wl_listener destroy; | ||
39 | struct wl_listener commit; | ||
40 | struct wl_listener new_popup; | ||
41 | }; | ||
42 | 31 | ||
43 | struct sway_layer_subsurface { | ||
44 | struct wlr_subsurface *wlr_subsurface; | ||
45 | struct sway_layer_surface *layer_surface; | ||
46 | |||
47 | struct wl_listener map; | ||
48 | struct wl_listener unmap; | ||
49 | struct wl_listener destroy; | 32 | struct wl_listener destroy; |
33 | struct wl_listener new_popup; | ||
50 | struct wl_listener commit; | 34 | struct wl_listener commit; |
51 | }; | 35 | }; |
52 | 36 | ||
53 | struct sway_output; | 37 | struct sway_output; |
54 | void arrange_layers(struct sway_output *output); | ||
55 | 38 | ||
56 | struct sway_layer_surface *layer_from_wlr_layer_surface_v1( | 39 | struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( |
57 | struct wlr_layer_surface_v1 *layer_surface); | 40 | struct wlr_surface *surface); |
41 | |||
42 | void arrange_layers(struct sway_output *output); | ||
58 | 43 | ||
59 | #endif | 44 | #endif |
diff --git a/include/sway/output.h b/include/sway/output.h index 96986700..d546d488 100644 --- a/include/sway/output.h +++ b/include/sway/output.h | |||
@@ -3,8 +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_box.h> | 6 | #include <wlr/types/wlr_damage_ring.h> |
7 | #include <wlr/types/wlr_output.h> | 7 | #include <wlr/types/wlr_output.h> |
8 | #include <wlr/types/wlr_scene.h> | ||
8 | #include "config.h" | 9 | #include "config.h" |
9 | #include "sway/tree/node.h" | 10 | #include "sway/tree/node.h" |
10 | #include "sway/tree/view.h" | 11 | #include "sway/tree/view.h" |
@@ -19,43 +20,63 @@ struct sway_output_state { | |||
19 | 20 | ||
20 | struct sway_output { | 21 | struct sway_output { |
21 | 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 | |||
22 | struct wlr_output *wlr_output; | 41 | struct wlr_output *wlr_output; |
42 | struct wlr_scene_output *scene_output; | ||
23 | struct sway_server *server; | 43 | struct sway_server *server; |
24 | struct wl_list link; | 44 | struct wl_list link; |
25 | 45 | ||
26 | struct wl_list layers[4]; // sway_layer_surface::link | ||
27 | struct wlr_box usable_area; | 46 | struct wlr_box usable_area; |
28 | 47 | ||
29 | struct timespec last_frame; | ||
30 | struct wlr_output_damage *damage; | ||
31 | |||
32 | int lx, ly; // layout coords | 48 | int lx, ly; // layout coords |
33 | int width, height; // transformed buffer size | 49 | int width, height; // transformed buffer size |
34 | enum wl_output_subpixel detected_subpixel; | 50 | enum wl_output_subpixel detected_subpixel; |
35 | enum scale_filter_mode scale_filter; | 51 | enum scale_filter_mode scale_filter; |
36 | // last applied mode when the output is DPMS'ed | ||
37 | struct wlr_output_mode *current_mode; | ||
38 | 52 | ||
39 | bool enabling, enabled; | 53 | bool enabled; |
40 | list_t *workspaces; | 54 | list_t *workspaces; |
41 | 55 | ||
42 | struct sway_output_state current; | 56 | struct sway_output_state current; |
43 | 57 | ||
58 | struct wl_listener layout_destroy; | ||
44 | struct wl_listener destroy; | 59 | struct wl_listener destroy; |
45 | struct wl_listener commit; | 60 | struct wl_listener commit; |
46 | struct wl_listener mode; | ||
47 | struct wl_listener present; | 61 | struct wl_listener present; |
48 | struct wl_listener damage_destroy; | 62 | struct wl_listener frame; |
49 | struct wl_listener damage_frame; | 63 | struct wl_listener request_state; |
50 | 64 | ||
51 | struct { | 65 | struct { |
52 | struct wl_signal destroy; | 66 | struct wl_signal disable; |
53 | } events; | 67 | } events; |
54 | 68 | ||
55 | struct timespec last_presentation; | 69 | struct timespec last_presentation; |
56 | uint32_t refresh_nsec; | 70 | uint32_t refresh_nsec; |
57 | int max_render_time; // In milliseconds | 71 | int max_render_time; // In milliseconds |
58 | 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; | ||
59 | }; | 80 | }; |
60 | 81 | ||
61 | struct sway_output *output_create(struct wlr_output *wlr_output); | 82 | struct sway_output *output_create(struct wlr_output *wlr_output); |
@@ -72,22 +93,12 @@ struct sway_output *output_get_in_direction(struct sway_output *reference, | |||
72 | void output_add_workspace(struct sway_output *output, | 93 | void output_add_workspace(struct sway_output *output, |
73 | struct sway_workspace *workspace); | 94 | struct sway_workspace *workspace); |
74 | 95 | ||
75 | typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct sway_view *view, | 96 | typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, |
76 | struct wlr_surface *surface, struct wlr_box *box, float rotation, | 97 | struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box, |
77 | void *user_data); | 98 | void *user_data); |
78 | 99 | ||
79 | void output_damage_whole(struct sway_output *output); | 100 | bool output_match_name_or_id(struct sway_output *output, |
80 | 101 | const char *name_or_id); | |
81 | void output_damage_surface(struct sway_output *output, double ox, double oy, | ||
82 | struct wlr_surface *surface, bool whole); | ||
83 | |||
84 | void output_damage_from_view(struct sway_output *output, | ||
85 | struct sway_view *view); | ||
86 | |||
87 | void output_damage_box(struct sway_output *output, struct wlr_box *box); | ||
88 | |||
89 | void output_damage_whole_container(struct sway_output *output, | ||
90 | struct sway_container *con); | ||
91 | 102 | ||
92 | // this ONLY includes the enabled outputs | 103 | // this ONLY includes the enabled outputs |
93 | 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); |
@@ -101,47 +112,8 @@ void output_enable(struct sway_output *output); | |||
101 | 112 | ||
102 | void output_disable(struct sway_output *output); | 113 | void output_disable(struct sway_output *output); |
103 | 114 | ||
104 | bool output_has_opaque_overlay_layer_surface(struct sway_output *output); | ||
105 | |||
106 | struct sway_workspace *output_get_active_workspace(struct sway_output *output); | 115 | struct sway_workspace *output_get_active_workspace(struct sway_output *output); |
107 | 116 | ||
108 | void output_render(struct sway_output *output, struct timespec *when, | ||
109 | pixman_region32_t *damage); | ||
110 | |||
111 | void output_surface_for_each_surface(struct sway_output *output, | ||
112 | struct wlr_surface *surface, double ox, double oy, | ||
113 | sway_surface_iterator_func_t iterator, void *user_data); | ||
114 | |||
115 | void output_view_for_each_surface(struct sway_output *output, | ||
116 | struct sway_view *view, sway_surface_iterator_func_t iterator, | ||
117 | void *user_data); | ||
118 | |||
119 | void output_view_for_each_popup_surface(struct sway_output *output, | ||
120 | struct sway_view *view, sway_surface_iterator_func_t iterator, | ||
121 | void *user_data); | ||
122 | |||
123 | void output_layer_for_each_surface(struct sway_output *output, | ||
124 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | ||
125 | void *user_data); | ||
126 | |||
127 | void output_layer_for_each_toplevel_surface(struct sway_output *output, | ||
128 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | ||
129 | void *user_data); | ||
130 | |||
131 | void output_layer_for_each_popup_surface(struct sway_output *output, | ||
132 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | ||
133 | void *user_data); | ||
134 | |||
135 | #if HAVE_XWAYLAND | ||
136 | void output_unmanaged_for_each_surface(struct sway_output *output, | ||
137 | struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, | ||
138 | void *user_data); | ||
139 | #endif | ||
140 | |||
141 | void output_drag_icons_for_each_surface(struct sway_output *output, | ||
142 | struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, | ||
143 | void *user_data); | ||
144 | |||
145 | void output_for_each_workspace(struct sway_output *output, | 117 | void output_for_each_workspace(struct sway_output *output, |
146 | void (*f)(struct sway_workspace *ws, void *data), void *data); | 118 | void (*f)(struct sway_workspace *ws, void *data), void *data); |
147 | 119 | ||
@@ -159,18 +131,12 @@ void output_get_box(struct sway_output *output, struct wlr_box *box); | |||
159 | enum sway_container_layout output_get_default_layout( | 131 | enum sway_container_layout output_get_default_layout( |
160 | struct sway_output *output); | 132 | struct sway_output *output); |
161 | 133 | ||
162 | void render_rect(struct sway_output *output, | ||
163 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
164 | float color[static 4]); | ||
165 | |||
166 | void premultiply_alpha(float color[4], float opacity); | ||
167 | |||
168 | void scale_box(struct wlr_box *box, float scale); | ||
169 | |||
170 | enum wlr_direction opposite_direction(enum wlr_direction d); | 134 | enum wlr_direction opposite_direction(enum wlr_direction d); |
171 | 135 | ||
172 | void handle_output_layout_change(struct wl_listener *listener, void *data); | 136 | void handle_output_layout_change(struct wl_listener *listener, void *data); |
173 | 137 | ||
138 | void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data); | ||
139 | |||
174 | void handle_output_manager_apply(struct wl_listener *listener, void *data); | 140 | void handle_output_manager_apply(struct wl_listener *listener, void *data); |
175 | 141 | ||
176 | void handle_output_manager_test(struct wl_listener *listener, void *data); | 142 | void handle_output_manager_test(struct wl_listener *listener, void *data); |
@@ -178,4 +144,6 @@ void handle_output_manager_test(struct wl_listener *listener, void *data); | |||
178 | void handle_output_power_manager_set_mode(struct wl_listener *listener, | 144 | void handle_output_power_manager_set_mode(struct wl_listener *listener, |
179 | void *data); | 145 | void *data); |
180 | 146 | ||
147 | struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output); | ||
148 | |||
181 | #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 0f5e3ab2..c71851f6 100644 --- a/include/sway/server.h +++ b/include/sway/server.h | |||
@@ -2,39 +2,43 @@ | |||
2 | #define _SWAY_SERVER_H | 2 | #define _SWAY_SERVER_H |
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include <wayland-server-core.h> | 4 | #include <wayland-server-core.h> |
5 | #include <wlr/backend.h> | ||
6 | #include <wlr/backend/session.h> | ||
7 | #include <wlr/render/wlr_renderer.h> | ||
8 | #include <wlr/types/wlr_compositor.h> | ||
9 | #include <wlr/types/wlr_data_device.h> | ||
10 | #include <wlr/types/wlr_input_method_v2.h> | ||
11 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
12 | #include <wlr/types/wlr_layer_shell_v1.h> | ||
13 | #include <wlr/types/wlr_output_management_v1.h> | ||
14 | #include <wlr/types/wlr_output_power_management_v1.h> | ||
15 | #include <wlr/types/wlr_presentation_time.h> | ||
16 | #include <wlr/types/wlr_relative_pointer_v1.h> | ||
17 | #include <wlr/types/wlr_server_decoration.h> | ||
18 | #include <wlr/types/wlr_text_input_v3.h> | ||
19 | #include <wlr/types/wlr_xdg_shell.h> | ||
20 | #include "config.h" | 5 | #include "config.h" |
21 | #include "list.h" | 6 | #include "list.h" |
7 | #include "sway/desktop/idle_inhibit_v1.h" | ||
22 | #if HAVE_XWAYLAND | 8 | #if HAVE_XWAYLAND |
23 | #include "sway/xwayland.h" | 9 | #include "sway/xwayland.h" |
24 | #endif | 10 | #endif |
25 | 11 | ||
12 | struct sway_transaction; | ||
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 | |||
26 | struct sway_server { | 27 | struct sway_server { |
27 | struct wl_display *wl_display; | 28 | struct wl_display *wl_display; |
28 | struct wl_event_loop *wl_event_loop; | 29 | struct wl_event_loop *wl_event_loop; |
29 | const char *socket; | 30 | const char *socket; |
30 | 31 | ||
31 | struct wlr_backend *backend; | 32 | struct wlr_backend *backend; |
32 | struct wlr_backend *noop_backend; | 33 | struct wlr_session *session; |
33 | // secondary headless backend used for creating virtual outputs on-the-fly | 34 | // secondary headless backend used for creating virtual outputs on-the-fly |
34 | struct wlr_backend *headless_backend; | 35 | struct wlr_backend *headless_backend; |
36 | struct wlr_renderer *renderer; | ||
37 | struct wlr_allocator *allocator; | ||
35 | 38 | ||
36 | struct wlr_compositor *compositor; | 39 | struct wlr_compositor *compositor; |
37 | struct wl_listener compositor_new_surface; | 40 | |
41 | struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; | ||
38 | 42 | ||
39 | struct wlr_data_device_manager *data_device_manager; | 43 | struct wlr_data_device_manager *data_device_manager; |
40 | 44 | ||
@@ -42,15 +46,16 @@ struct sway_server { | |||
42 | 46 | ||
43 | struct wl_listener new_output; | 47 | struct wl_listener new_output; |
44 | struct wl_listener output_layout_change; | 48 | struct wl_listener output_layout_change; |
49 | struct wl_listener renderer_lost; | ||
45 | 50 | ||
46 | struct wlr_idle *idle; | 51 | struct wlr_idle_notifier_v1 *idle_notifier_v1; |
47 | struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1; | 52 | struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; |
48 | 53 | ||
49 | struct wlr_layer_shell_v1 *layer_shell; | 54 | struct wlr_layer_shell_v1 *layer_shell; |
50 | struct wl_listener layer_shell_surface; | 55 | struct wl_listener layer_shell_surface; |
51 | 56 | ||
52 | struct wlr_xdg_shell *xdg_shell; | 57 | struct wlr_xdg_shell *xdg_shell; |
53 | struct wl_listener xdg_shell_surface; | 58 | struct wl_listener xdg_shell_toplevel; |
54 | 59 | ||
55 | struct wlr_tablet_manager_v2 *tablet_v2; | 60 | struct wlr_tablet_manager_v2 *tablet_v2; |
56 | 61 | ||
@@ -70,7 +75,8 @@ struct sway_server { | |||
70 | struct wl_listener xdg_decoration; | 75 | struct wl_listener xdg_decoration; |
71 | struct wl_list xdg_decorations; // sway_xdg_decoration::link | 76 | struct wl_list xdg_decorations; // sway_xdg_decoration::link |
72 | 77 | ||
73 | struct wlr_presentation *presentation; | 78 | struct wlr_drm_lease_v1_manager *drm_lease_manager; |
79 | struct wl_listener drm_lease_request; | ||
74 | 80 | ||
75 | struct wlr_pointer_constraints_v1 *pointer_constraints; | 81 | struct wlr_pointer_constraints_v1 *pointer_constraints; |
76 | struct wl_listener pointer_constraint; | 82 | struct wl_listener pointer_constraint; |
@@ -79,14 +85,53 @@ struct sway_server { | |||
79 | struct wl_listener output_manager_apply; | 85 | struct wl_listener output_manager_apply; |
80 | struct wl_listener output_manager_test; | 86 | struct wl_listener output_manager_test; |
81 | 87 | ||
88 | struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; | ||
89 | struct wl_listener gamma_control_set_gamma; | ||
90 | |||
91 | struct { | ||
92 | struct sway_session_lock *lock; | ||
93 | struct wlr_session_lock_manager_v1 *manager; | ||
94 | |||
95 | struct wl_listener new_lock; | ||
96 | struct wl_listener manager_destroy; | ||
97 | } session_lock; | ||
98 | |||
82 | struct wlr_output_power_manager_v1 *output_power_manager_v1; | 99 | struct wlr_output_power_manager_v1 *output_power_manager_v1; |
83 | struct wl_listener output_power_manager_set_mode; | 100 | struct wl_listener output_power_manager_set_mode; |
84 | struct wlr_input_method_manager_v2 *input_method; | 101 | struct wlr_input_method_manager_v2 *input_method; |
85 | 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; | ||
86 | 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; | ||
110 | |||
111 | struct wlr_xdg_activation_v1 *xdg_activation_v1; | ||
112 | struct wl_listener xdg_activation_v1_request_activate; | ||
113 | struct wl_listener xdg_activation_v1_new_token; | ||
87 | 114 | ||
115 | struct wl_listener request_set_cursor_shape; | ||
116 | |||
117 | struct wl_list pending_launcher_ctxs; // launcher_ctx::link | ||
118 | |||
119 | // The timeout for transactions, after which a transaction is applied | ||
120 | // regardless of readiness. | ||
88 | size_t txn_timeout_ms; | 121 | size_t txn_timeout_ms; |
89 | list_t *transactions; | 122 | |
123 | // Stores a transaction after it has been committed, but is waiting for | ||
124 | // views to ack the new dimensions before being applied. A queued | ||
125 | // transaction is frozen and must not have new instructions added to it. | ||
126 | struct sway_transaction *queued_transaction; | ||
127 | |||
128 | // Stores a pending transaction that will be committed once the existing | ||
129 | // queued transaction is applied and freed. The pending transaction can be | ||
130 | // updated with new instructions as needed. | ||
131 | struct sway_transaction *pending_transaction; | ||
132 | |||
133 | // Stores the nodes that have been marked as "dirty" and will be put into | ||
134 | // the pending transaction. | ||
90 | list_t *dirty_nodes; | 135 | list_t *dirty_nodes; |
91 | }; | 136 | }; |
92 | 137 | ||
@@ -96,34 +141,41 @@ struct sway_debug { | |||
96 | bool noatomic; // Ignore atomic layout updates | 141 | bool noatomic; // Ignore atomic layout updates |
97 | bool txn_timings; // Log verbose messages about transactions | 142 | bool txn_timings; // Log verbose messages about transactions |
98 | bool txn_wait; // Always wait for the timeout before applying | 143 | bool txn_wait; // Always wait for the timeout before applying |
99 | 144 | bool legacy_wl_drm; // Enable the legacy wl_drm interface | |
100 | enum { | ||
101 | DAMAGE_DEFAULT, // Default behaviour | ||
102 | DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged | ||
103 | DAMAGE_RERENDER, // Render the full output when any damage occurs | ||
104 | } damage; | ||
105 | }; | 145 | }; |
106 | 146 | ||
107 | extern struct sway_debug debug; | 147 | extern struct sway_debug debug; |
108 | 148 | ||
109 | /* Prepares an unprivileged server_init by performing all privileged operations in advance */ | 149 | extern bool allow_unsupported_gpu; |
110 | bool server_privileged_prepare(struct sway_server *server); | 150 | |
111 | bool server_init(struct sway_server *server); | 151 | bool server_init(struct sway_server *server); |
112 | void server_fini(struct sway_server *server); | 152 | void server_fini(struct sway_server *server); |
113 | bool server_start(struct sway_server *server); | 153 | bool server_start(struct sway_server *server); |
114 | void server_run(struct sway_server *server); | 154 | void server_run(struct sway_server *server); |
115 | 155 | ||
116 | void handle_compositor_new_surface(struct wl_listener *listener, void *data); | 156 | void restore_nofile_limit(void); |
157 | |||
117 | void handle_new_output(struct wl_listener *listener, void *data); | 158 | void handle_new_output(struct wl_listener *listener, void *data); |
118 | 159 | ||
119 | void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); | 160 | void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); |
120 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); | 161 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); |
121 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data); | 162 | void sway_session_lock_init(void); |
163 | void sway_session_lock_add_output(struct sway_session_lock *lock, | ||
164 | struct sway_output *output); | ||
165 | bool sway_session_lock_has_surface(struct sway_session_lock *lock, | ||
166 | struct wlr_surface *surface); | ||
167 | void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data); | ||
122 | #if HAVE_XWAYLAND | 168 | #if HAVE_XWAYLAND |
123 | void handle_xwayland_surface(struct wl_listener *listener, void *data); | 169 | void handle_xwayland_surface(struct wl_listener *listener, void *data); |
124 | #endif | 170 | #endif |
125 | void handle_server_decoration(struct wl_listener *listener, void *data); | 171 | void handle_server_decoration(struct wl_listener *listener, void *data); |
126 | void handle_xdg_decoration(struct wl_listener *listener, void *data); | 172 | void handle_xdg_decoration(struct wl_listener *listener, void *data); |
127 | void handle_pointer_constraint(struct wl_listener *listener, void *data); | 173 | void handle_pointer_constraint(struct wl_listener *listener, void *data); |
174 | void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, | ||
175 | void *data); | ||
176 | void xdg_activation_v1_handle_new_token(struct wl_listener *listener, | ||
177 | void *data); | ||
178 | |||
179 | void set_rr_scheduling(void); | ||
128 | 180 | ||
129 | #endif | 181 | #endif |
diff --git a/include/sway/surface.h b/include/sway/surface.h deleted file mode 100644 index 4da96c02..00000000 --- a/include/sway/surface.h +++ /dev/null | |||
@@ -1,18 +0,0 @@ | |||
1 | #ifndef _SWAY_SURFACE_H | ||
2 | #define _SWAY_SURFACE_H | ||
3 | #include <wlr/types/wlr_surface.h> | ||
4 | |||
5 | struct sway_surface { | ||
6 | struct wlr_surface *wlr_surface; | ||
7 | |||
8 | struct wl_listener destroy; | ||
9 | |||
10 | /** | ||
11 | * This timer can be used for issuing delayed frame done callbacks (for | ||
12 | * example, to improve presentation latency). Its handler is set to a | ||
13 | * function that issues a frame done callback to this surface. | ||
14 | */ | ||
15 | struct wl_event_source *frame_done_timer; | ||
16 | }; | ||
17 | |||
18 | #endif | ||
diff --git a/include/sway/sway_text_node.h b/include/sway/sway_text_node.h new file mode 100644 index 00000000..0d4209bb --- /dev/null +++ b/include/sway/sway_text_node.h | |||
@@ -0,0 +1,28 @@ | |||
1 | #ifndef _SWAY_BUFFER_H | ||
2 | #define _SWAY_BUFFER_H | ||
3 | #include <wlr/types/wlr_scene.h> | ||
4 | |||
5 | struct sway_text_node { | ||
6 | int width; | ||
7 | int max_width; | ||
8 | int height; | ||
9 | int baseline; | ||
10 | bool pango_markup; | ||
11 | float color[4]; | ||
12 | float background[4]; | ||
13 | |||
14 | struct wlr_scene_node *node; | ||
15 | }; | ||
16 | |||
17 | struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, | ||
18 | char *text, float color[4], bool pango_markup); | ||
19 | |||
20 | void sway_text_node_set_color(struct sway_text_node *node, float color[4]); | ||
21 | |||
22 | void sway_text_node_set_text(struct sway_text_node *node, char *text); | ||
23 | |||
24 | void sway_text_node_set_max_width(struct sway_text_node *node, int max_width); | ||
25 | |||
26 | void sway_text_node_set_background(struct sway_text_node *node, float background[4]); | ||
27 | |||
28 | #endif | ||
diff --git a/include/sway/swaynag.h b/include/sway/swaynag.h index 74d9ea18..03bd52c3 100644 --- a/include/sway/swaynag.h +++ b/include/sway/swaynag.h | |||
@@ -1,6 +1,7 @@ | |||
1 | #ifndef _SWAY_SWAYNAG_H | 1 | #ifndef _SWAY_SWAYNAG_H |
2 | #define _SWAY_SWAYNAG_H | 2 | #define _SWAY_SWAYNAG_H |
3 | #include <wayland-server-core.h> | 3 | #include <wayland-server-core.h> |
4 | #include "stringop.h" | ||
4 | 5 | ||
5 | struct swaynag_instance { | 6 | struct swaynag_instance { |
6 | struct wl_client *client; | 7 | struct wl_client *client; |
@@ -21,7 +22,7 @@ bool swaynag_spawn(const char *swaynag_command, | |||
21 | // Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed | 22 | // Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed |
22 | // is false. | 23 | // is false. |
23 | void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, | 24 | void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, |
24 | const char *fmt, ...); | 25 | const char *fmt, ...) _SWAY_ATTRIB_PRINTF(3, 4); |
25 | 26 | ||
26 | // If swaynag->detailed, close swaynag->fd[1] so swaynag displays | 27 | // If swaynag->detailed, close swaynag->fd[1] so swaynag displays |
27 | void swaynag_show(struct swaynag_instance *swaynag); | 28 | void swaynag_show(struct swaynag_instance *swaynag); |
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 7e9df59f..93f6bfbb 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h | |||
@@ -2,8 +2,8 @@ | |||
2 | #define _SWAY_CONTAINER_H | 2 | #define _SWAY_CONTAINER_H |
3 | #include <stdint.h> | 3 | #include <stdint.h> |
4 | #include <sys/types.h> | 4 | #include <sys/types.h> |
5 | #include <wlr/types/wlr_box.h> | 5 | #include <wlr/types/wlr_compositor.h> |
6 | #include <wlr/types/wlr_surface.h> | 6 | #include <wlr/types/wlr_scene.h> |
7 | #include "list.h" | 7 | #include "list.h" |
8 | #include "sway/tree/node.h" | 8 | #include "sway/tree/node.h" |
9 | 9 | ||
@@ -46,9 +46,9 @@ struct sway_container_state { | |||
46 | 46 | ||
47 | enum sway_fullscreen_mode fullscreen_mode; | 47 | enum sway_fullscreen_mode fullscreen_mode; |
48 | 48 | ||
49 | struct sway_workspace *workspace; | 49 | struct sway_workspace *workspace; // NULL when hidden in the scratchpad |
50 | struct sway_container *parent; | 50 | struct sway_container *parent; // NULL if container in root of workspace |
51 | list_t *children; | 51 | list_t *children; // struct sway_container |
52 | 52 | ||
53 | struct sway_container *focused_inactive_child; | 53 | struct sway_container *focused_inactive_child; |
54 | bool focused; | 54 | bool focused; |
@@ -60,6 +60,7 @@ struct sway_container_state { | |||
60 | bool border_left; | 60 | bool border_left; |
61 | bool border_right; | 61 | bool border_right; |
62 | 62 | ||
63 | // These are in layout coordinates. | ||
63 | double content_x, content_y; | 64 | double content_x, content_y; |
64 | double content_width, content_height; | 65 | double content_width, content_height; |
65 | }; | 66 | }; |
@@ -68,14 +69,40 @@ 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 | ||
71 | // The pending state is the main container properties, and the current state is in the below struct. | 72 | struct wlr_scene_tree *scene_tree; |
72 | // This means most places of the code can refer to the main variables (pending state) and it'll just work. | 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 | |||
73 | struct sway_container_state current; | 99 | struct sway_container_state current; |
100 | struct sway_container_state pending; | ||
74 | 101 | ||
75 | char *title; // The view's title (unformatted) | 102 | char *title; // The view's title (unformatted) |
76 | 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; | ||
77 | 105 | ||
78 | enum sway_container_layout layout; | ||
79 | enum sway_container_layout prev_split_layout; | 106 | enum sway_container_layout prev_split_layout; |
80 | 107 | ||
81 | // Whether stickiness has been enabled on this container. Use | 108 | // Whether stickiness has been enabled on this container. Use |
@@ -86,11 +113,13 @@ struct sway_container { | |||
86 | // For C_ROOT, this has no meaning | 113 | // For C_ROOT, this has no meaning |
87 | // For other types, this is the position in layout coordinates | 114 | // For other types, this is the position in layout coordinates |
88 | // Includes borders | 115 | // Includes borders |
89 | double x, y; | ||
90 | double width, height; | ||
91 | double saved_x, saved_y; | 116 | double saved_x, saved_y; |
92 | double saved_width, saved_height; | 117 | double saved_width, saved_height; |
93 | 118 | ||
119 | // Used when the view changes to CSD unexpectedly. This will be a non-B_CSD | ||
120 | // border which we use to restore when the view returns to SSD. | ||
121 | enum sway_container_border saved_border; | ||
122 | |||
94 | // The share of the space of parent container this container occupies | 123 | // The share of the space of parent container this container occupies |
95 | double width_fraction; | 124 | double width_fraction; |
96 | double height_fraction; | 125 | double height_fraction; |
@@ -100,55 +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 | // These are in layout coordinates. | ||
104 | double content_x, content_y; | ||
105 | int content_width, content_height; | ||
106 | |||
107 | // In most cases this is the same as the content x and y, but if the view | ||
108 | // refuses to resize to the content dimensions then it can be smaller. | ||
109 | // These are in layout coordinates. | ||
110 | double surface_x, surface_y; | ||
111 | |||
112 | enum sway_fullscreen_mode fullscreen_mode; | ||
113 | |||
114 | enum sway_container_border border; | ||
115 | |||
116 | // Used when the view changes to CSD unexpectedly. This will be a non-B_CSD | ||
117 | // border which we use to restore when the view returns to SSD. | ||
118 | enum sway_container_border saved_border; | ||
119 | |||
120 | int border_thickness; | ||
121 | bool border_top; | ||
122 | bool border_bottom; | ||
123 | bool border_left; | ||
124 | bool border_right; | ||
125 | |||
126 | struct sway_workspace *workspace; // NULL when hidden in the scratchpad | ||
127 | struct sway_container *parent; // NULL if container in root of workspace | ||
128 | list_t *children; // struct sway_container | ||
129 | |||
130 | // Outputs currently being intersected | ||
131 | list_t *outputs; // struct sway_output | ||
132 | |||
133 | // Indicates that the container is a scratchpad container. | 132 | // Indicates that the container is a scratchpad container. |
134 | // Both hidden and visible scratchpad containers have scratchpad=true. | 133 | // Both hidden and visible scratchpad containers have scratchpad=true. |
135 | // Hidden scratchpad containers have a NULL parent. | 134 | // Hidden scratchpad containers have a NULL parent. |
136 | bool scratchpad; | 135 | bool scratchpad; |
137 | 136 | ||
138 | 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; | ||
139 | 141 | ||
140 | struct wlr_texture *title_focused; | 142 | float alpha; |
141 | struct wlr_texture *title_focused_inactive; | ||
142 | struct wlr_texture *title_unfocused; | ||
143 | struct wlr_texture *title_urgent; | ||
144 | size_t title_height; | ||
145 | size_t title_baseline; | ||
146 | 143 | ||
147 | list_t *marks; // char * | 144 | list_t *marks; // char * |
148 | struct wlr_texture *marks_focused; | ||
149 | struct wlr_texture *marks_focused_inactive; | ||
150 | struct wlr_texture *marks_unfocused; | ||
151 | struct wlr_texture *marks_urgent; | ||
152 | 145 | ||
153 | struct { | 146 | struct { |
154 | struct wl_signal destroy; | 147 | struct wl_signal destroy; |
@@ -168,23 +161,15 @@ void container_begin_destroy(struct sway_container *con); | |||
168 | struct sway_container *container_find_child(struct sway_container *container, | 161 | struct sway_container *container_find_child(struct sway_container *container, |
169 | bool (*test)(struct sway_container *view, void *data), void *data); | 162 | bool (*test)(struct sway_container *view, void *data), void *data); |
170 | 163 | ||
171 | /** | ||
172 | * Find a container at the given coordinates. Returns the surface and | ||
173 | * surface-local coordinates of the given layout coordinates if the container | ||
174 | * is a view and the view contains a surface at those coordinates. | ||
175 | */ | ||
176 | struct sway_container *container_at(struct sway_workspace *workspace, | ||
177 | double lx, double ly, struct wlr_surface **surface, | ||
178 | double *sx, double *sy); | ||
179 | |||
180 | struct sway_container *tiling_container_at( | ||
181 | struct sway_node *parent, double lx, double ly, | ||
182 | struct wlr_surface **surface, double *sx, double *sy); | ||
183 | |||
184 | void container_for_each_child(struct sway_container *container, | 164 | void container_for_each_child(struct sway_container *container, |
185 | void (*f)(struct sway_container *container, void *data), void *data); | 165 | void (*f)(struct sway_container *container, void *data), void *data); |
186 | 166 | ||
187 | /** | 167 | /** |
168 | * Returns the fullscreen container obstructing this container if it exists. | ||
169 | */ | ||
170 | struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container); | ||
171 | |||
172 | /** | ||
188 | * Returns true if the given container is an ancestor of this container. | 173 | * Returns true if the given container is an ancestor of this container. |
189 | */ | 174 | */ |
190 | bool container_has_ancestor(struct sway_container *container, | 175 | bool container_has_ancestor(struct sway_container *container, |
@@ -192,18 +177,13 @@ bool container_has_ancestor(struct sway_container *container, | |||
192 | 177 | ||
193 | void container_update_textures_recursive(struct sway_container *con); | 178 | void container_update_textures_recursive(struct sway_container *con); |
194 | 179 | ||
195 | void container_damage_whole(struct sway_container *container); | ||
196 | |||
197 | void container_reap_empty(struct sway_container *con); | 180 | void container_reap_empty(struct sway_container *con); |
198 | 181 | ||
199 | struct sway_container *container_flatten(struct sway_container *container); | 182 | struct sway_container *container_flatten(struct sway_container *container); |
200 | 183 | ||
201 | void container_update_title_textures(struct sway_container *container); | 184 | void container_update_title_bar(struct sway_container *container); |
202 | 185 | ||
203 | /** | 186 | void container_update_marks(struct sway_container *container); |
204 | * Calculate the container's title_height property. | ||
205 | */ | ||
206 | void container_calculate_title_height(struct sway_container *container); | ||
207 | 187 | ||
208 | size_t container_build_representation(enum sway_container_layout layout, | 188 | size_t container_build_representation(enum sway_container_layout layout, |
209 | list_t *children, char *buffer); | 189 | list_t *children, char *buffer); |
@@ -218,6 +198,9 @@ size_t container_titlebar_height(void); | |||
218 | void floating_calculate_constraints(int *min_width, int *max_width, | 198 | void floating_calculate_constraints(int *min_width, int *max_width, |
219 | int *min_height, int *max_height); | 199 | int *min_height, int *max_height); |
220 | 200 | ||
201 | void floating_fix_coordinates(struct sway_container *con, | ||
202 | struct wlr_box *old, struct wlr_box *new); | ||
203 | |||
221 | void container_floating_resize_and_center(struct sway_container *con); | 204 | void container_floating_resize_and_center(struct sway_container *con); |
222 | 205 | ||
223 | void container_floating_set_default_size(struct sway_container *con); | 206 | void container_floating_set_default_size(struct sway_container *con); |
@@ -231,6 +214,8 @@ void container_set_geometry_from_content(struct sway_container *con); | |||
231 | /** | 214 | /** |
232 | * Determine if the given container is itself floating. | 215 | * Determine if the given container is itself floating. |
233 | * This will return false for any descendants of a floating container. | 216 | * This will return false for any descendants of a floating container. |
217 | * | ||
218 | * Uses pending container state. | ||
234 | */ | 219 | */ |
235 | bool container_is_floating(struct sway_container *container); | 220 | bool container_is_floating(struct sway_container *container); |
236 | 221 | ||
@@ -296,25 +281,12 @@ bool container_is_floating_or_child(struct sway_container *container); | |||
296 | */ | 281 | */ |
297 | bool container_is_fullscreen_or_child(struct sway_container *container); | 282 | bool container_is_fullscreen_or_child(struct sway_container *container); |
298 | 283 | ||
299 | /** | ||
300 | * Return the output which will be used for scale purposes. | ||
301 | * This is the most recently entered output. | ||
302 | */ | ||
303 | struct sway_output *container_get_effective_output(struct sway_container *con); | ||
304 | |||
305 | void container_discover_outputs(struct sway_container *con); | ||
306 | |||
307 | enum sway_container_layout container_parent_layout(struct sway_container *con); | 284 | enum sway_container_layout container_parent_layout(struct sway_container *con); |
308 | 285 | ||
309 | enum sway_container_layout container_current_parent_layout( | ||
310 | struct sway_container *con); | ||
311 | |||
312 | list_t *container_get_siblings(struct sway_container *container); | 286 | list_t *container_get_siblings(struct sway_container *container); |
313 | 287 | ||
314 | int container_sibling_index(struct sway_container *child); | 288 | int container_sibling_index(struct sway_container *child); |
315 | 289 | ||
316 | list_t *container_get_current_siblings(struct sway_container *container); | ||
317 | |||
318 | void container_handle_fullscreen_reparent(struct sway_container *con); | 290 | void container_handle_fullscreen_reparent(struct sway_container *con); |
319 | 291 | ||
320 | void container_add_child(struct sway_container *parent, | 292 | void container_add_child(struct sway_container *parent, |
@@ -362,8 +334,6 @@ bool container_has_mark(struct sway_container *container, char *mark); | |||
362 | 334 | ||
363 | void container_add_mark(struct sway_container *container, char *mark); | 335 | void container_add_mark(struct sway_container *container, char *mark); |
364 | 336 | ||
365 | void container_update_marks_textures(struct sway_container *container); | ||
366 | |||
367 | void container_raise_floating(struct sway_container *con); | 337 | void container_raise_floating(struct sway_container *con); |
368 | 338 | ||
369 | bool container_is_scratchpad_hidden(struct sway_container *con); | 339 | bool container_is_scratchpad_hidden(struct sway_container *con); |
@@ -378,7 +348,7 @@ bool container_is_sticky_or_child(struct sway_container *con); | |||
378 | * This will destroy pairs of redundant H/V splits | 348 | * This will destroy pairs of redundant H/V splits |
379 | * e.g. H[V[H[app app]] app] -> H[app app app] | 349 | * e.g. H[V[H[app app]] app] -> H[app app app] |
380 | * The middle "V[H[" are eliminated by a call to container_squash | 350 | * The middle "V[H[" are eliminated by a call to container_squash |
381 | * on the V[ con. It's grandchildren are added to it's parent. | 351 | * on the V[ con. It's grandchildren are added to its parent. |
382 | * | 352 | * |
383 | * This function is roughly equivalent to i3's tree_flatten here: | 353 | * This function is roughly equivalent to i3's tree_flatten here: |
384 | * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651 | 354 | * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651 |
@@ -387,4 +357,10 @@ bool container_is_sticky_or_child(struct sway_container *con); | |||
387 | */ | 357 | */ |
388 | int container_squash(struct sway_container *con); | 358 | int container_squash(struct sway_container *con); |
389 | 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 | |||
390 | #endif | 366 | #endif |
diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index 470ee3b5..e2dbcdf0 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h | |||
@@ -1,6 +1,8 @@ | |||
1 | #ifndef _SWAY_NODE_H | 1 | #ifndef _SWAY_NODE_H |
2 | #define _SWAY_NODE_H | 2 | #define _SWAY_NODE_H |
3 | #include <wayland-server-core.h> | ||
3 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <wlr/types/wlr_scene.h> | ||
4 | #include "list.h" | 6 | #include "list.h" |
5 | 7 | ||
6 | #define MIN_SANE_W 100 | 8 | #define MIN_SANE_W 100 |
@@ -74,4 +76,15 @@ list_t *node_get_children(struct sway_node *node); | |||
74 | 76 | ||
75 | bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor); | 77 | bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor); |
76 | 78 | ||
79 | // when destroying a sway tree, it's not known which order the tree will be | ||
80 | // destroyed. To prevent freeing of scene_nodes recursing up the tree, | ||
81 | // let's use this helper function to disown them to the staging node. | ||
82 | void scene_node_disown_children(struct wlr_scene_tree *tree); | ||
83 | |||
84 | // a helper function used to allocate tree nodes. If an allocation failure | ||
85 | // occurs a flag is flipped that can be checked later to destroy a parent | ||
86 | // of this scene node preventing memory leaks. | ||
87 | struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, | ||
88 | bool *failed); | ||
89 | |||
77 | #endif | 90 | #endif |
diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index e8f4d573..15df0f55 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <wayland-server-core.h> | 3 | #include <wayland-server-core.h> |
4 | #include <wayland-util.h> | 4 | #include <wayland-util.h> |
5 | #include <wlr/types/wlr_output_layout.h> | 5 | #include <wlr/types/wlr_output_layout.h> |
6 | #include <wlr/types/wlr_scene.h> | ||
6 | #include <wlr/render/wlr_texture.h> | 7 | #include <wlr/render/wlr_texture.h> |
7 | #include "sway/tree/container.h" | 8 | #include "sway/tree/container.h" |
8 | #include "sway/tree/node.h" | 9 | #include "sway/tree/node.h" |
@@ -16,10 +17,44 @@ struct sway_root { | |||
16 | struct wlr_output_layout *output_layout; | 17 | struct wlr_output_layout *output_layout; |
17 | 18 | ||
18 | struct wl_listener output_layout_change; | 19 | struct wl_listener output_layout_change; |
20 | |||
21 | // scene node layout: | ||
22 | // - root | ||
23 | // - staging | ||
24 | // - layer shell stuff | ||
25 | // - tiling | ||
26 | // - floating | ||
27 | // - fullscreen stuff | ||
28 | // - seat stuff | ||
29 | // - ext_session_lock | ||
30 | struct wlr_scene *root_scene; | ||
31 | |||
32 | // since wlr_scene nodes can't be orphaned and must always | ||
33 | // have a parent, use this staging scene_tree so that a | ||
34 | // node always have a valid parent. Nothing in this | ||
35 | // staging node will be visible. | ||
36 | struct wlr_scene_tree *staging; | ||
37 | |||
38 | // tree containing all layers the compositor will render. Cursor handling | ||
39 | // will end up iterating this tree. | ||
40 | struct wlr_scene_tree *layer_tree; | ||
41 | |||
42 | struct { | ||
43 | struct wlr_scene_tree *shell_background; | ||
44 | struct wlr_scene_tree *shell_bottom; | ||
45 | struct wlr_scene_tree *tiling; | ||
46 | struct wlr_scene_tree *floating; | ||
47 | struct wlr_scene_tree *shell_top; | ||
48 | struct wlr_scene_tree *fullscreen; | ||
49 | struct wlr_scene_tree *fullscreen_global; | ||
19 | #if HAVE_XWAYLAND | 50 | #if HAVE_XWAYLAND |
20 | struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link | 51 | struct wlr_scene_tree *unmanaged; |
21 | #endif | 52 | #endif |
22 | struct wl_list drag_icons; // sway_drag_icon::link | 53 | struct wlr_scene_tree *shell_overlay; |
54 | struct wlr_scene_tree *popup; | ||
55 | struct wlr_scene_tree *seat; | ||
56 | struct wlr_scene_tree *session_lock; | ||
57 | } layers; | ||
23 | 58 | ||
24 | // Includes disabled outputs | 59 | // Includes disabled outputs |
25 | struct wl_list all_outputs; // sway_output::link | 60 | struct wl_list all_outputs; // sway_output::link |
@@ -28,10 +63,11 @@ struct sway_root { | |||
28 | double width, height; | 63 | double width, height; |
29 | 64 | ||
30 | list_t *outputs; // struct sway_output | 65 | list_t *outputs; // struct sway_output |
66 | list_t *non_desktop_outputs; // struct sway_output_non_desktop | ||
31 | list_t *scratchpad; // struct sway_container | 67 | list_t *scratchpad; // struct sway_container |
32 | 68 | ||
33 | // For when there's no connected outputs | 69 | // For when there's no connected outputs |
34 | struct sway_output *noop_output; | 70 | struct sway_output *fallback_output; |
35 | 71 | ||
36 | struct sway_container *fullscreen_global; | 72 | struct sway_container *fullscreen_global; |
37 | 73 | ||
@@ -40,7 +76,7 @@ struct sway_root { | |||
40 | } events; | 76 | } events; |
41 | }; | 77 | }; |
42 | 78 | ||
43 | struct sway_root *root_create(void); | 79 | struct sway_root *root_create(struct wl_display *display); |
44 | 80 | ||
45 | void root_destroy(struct sway_root *root); | 81 | void root_destroy(struct sway_root *root); |
46 | 82 | ||
@@ -68,12 +104,6 @@ void root_scratchpad_show(struct sway_container *con); | |||
68 | */ | 104 | */ |
69 | void root_scratchpad_hide(struct sway_container *con); | 105 | void root_scratchpad_hide(struct sway_container *con); |
70 | 106 | ||
71 | struct sway_workspace *root_workspace_for_pid(pid_t pid); | ||
72 | |||
73 | void root_record_workspace_pid(pid_t pid); | ||
74 | |||
75 | void root_remove_workspace_pid(pid_t pid); | ||
76 | |||
77 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), | 107 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), |
78 | void *data); | 108 | void *data); |
79 | 109 | ||
@@ -91,6 +121,4 @@ struct sway_container *root_find_container( | |||
91 | 121 | ||
92 | void root_get_box(struct sway_root *root, struct wlr_box *box); | 122 | void root_get_box(struct sway_root *root, struct wlr_box *box); |
93 | 123 | ||
94 | void root_rename_pid_workspaces(const char *old_name, const char *new_name); | ||
95 | |||
96 | #endif | 124 | #endif |
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index e071e6c9..7faacdcc 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -1,8 +1,9 @@ | |||
1 | #ifndef _SWAY_VIEW_H | 1 | #ifndef _SWAY_VIEW_H |
2 | #define _SWAY_VIEW_H | 2 | #define _SWAY_VIEW_H |
3 | #include <wayland-server-core.h> | 3 | #include <wayland-server-core.h> |
4 | #include <wlr/types/wlr_surface.h> | 4 | #include <wlr/types/wlr_compositor.h> |
5 | #include "config.h" | 5 | #include <wlr/types/wlr_scene.h> |
6 | #include "sway/config.h" | ||
6 | #if HAVE_XWAYLAND | 7 | #if HAVE_XWAYLAND |
7 | #include <wlr/xwayland.h> | 8 | #include <wlr/xwayland.h> |
8 | #endif | 9 | #endif |
@@ -45,10 +46,6 @@ struct sway_view_impl { | |||
45 | void (*set_fullscreen)(struct sway_view *view, bool fullscreen); | 46 | void (*set_fullscreen)(struct sway_view *view, bool fullscreen); |
46 | void (*set_resizing)(struct sway_view *view, bool resizing); | 47 | void (*set_resizing)(struct sway_view *view, bool resizing); |
47 | bool (*wants_floating)(struct sway_view *view); | 48 | bool (*wants_floating)(struct sway_view *view); |
48 | void (*for_each_surface)(struct sway_view *view, | ||
49 | wlr_surface_iterator_func_t iterator, void *user_data); | ||
50 | void (*for_each_popup_surface)(struct sway_view *view, | ||
51 | wlr_surface_iterator_func_t iterator, void *user_data); | ||
52 | bool (*is_transient_for)(struct sway_view *child, | 49 | bool (*is_transient_for)(struct sway_view *child, |
53 | struct sway_view *ancestor); | 50 | struct sway_view *ancestor); |
54 | void (*close)(struct sway_view *view); | 51 | void (*close)(struct sway_view *view); |
@@ -56,24 +53,20 @@ struct sway_view_impl { | |||
56 | void (*destroy)(struct sway_view *view); | 53 | void (*destroy)(struct sway_view *view); |
57 | }; | 54 | }; |
58 | 55 | ||
59 | struct sway_saved_buffer { | ||
60 | struct wlr_client_buffer *buffer; | ||
61 | int x, y; | ||
62 | int width, height; | ||
63 | enum wl_output_transform transform; | ||
64 | struct wlr_fbox source_box; | ||
65 | struct wl_list link; // sway_view::saved_buffers | ||
66 | }; | ||
67 | |||
68 | struct sway_view { | 56 | struct sway_view { |
69 | enum sway_view_type type; | 57 | enum sway_view_type type; |
70 | const struct sway_view_impl *impl; | 58 | const struct sway_view_impl *impl; |
71 | 59 | ||
60 | struct wlr_scene_tree *scene_tree; | ||
61 | struct wlr_scene_tree *content_tree; | ||
62 | struct wlr_scene_tree *saved_surface_tree; | ||
63 | |||
72 | struct sway_container *container; // NULL if unmapped and transactions finished | 64 | struct sway_container *container; // NULL if unmapped and transactions finished |
73 | struct wlr_surface *surface; // NULL for unmapped views | 65 | struct wlr_surface *surface; // NULL for unmapped views |
74 | struct sway_xdg_decoration *xdg_decoration; | 66 | struct sway_xdg_decoration *xdg_decoration; |
75 | 67 | ||
76 | pid_t pid; | 68 | pid_t pid; |
69 | struct launcher_ctx *ctx; | ||
77 | 70 | ||
78 | // The size the view would want to be if it weren't tiled. | 71 | // The size the view would want to be if it weren't tiled. |
79 | // Used when changing a view from tiled to floating. | 72 | // Used when changing a view from tiled to floating. |
@@ -87,15 +80,11 @@ struct sway_view { | |||
87 | bool allow_request_urgent; | 80 | bool allow_request_urgent; |
88 | struct wl_event_source *urgent_timer; | 81 | struct wl_event_source *urgent_timer; |
89 | 82 | ||
90 | struct wl_list saved_buffers; // sway_saved_buffer::link | ||
91 | |||
92 | // The geometry for whatever the client is committing, regardless of | 83 | // The geometry for whatever the client is committing, regardless of |
93 | // transaction state. Updated on every commit. | 84 | // transaction state. Updated on every commit. |
94 | struct wlr_box geometry; | 85 | struct wlr_box geometry; |
95 | 86 | ||
96 | // The "old" geometry during a transaction. Used to damage the old location | 87 | struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel; |
97 | // when a transaction is applied. | ||
98 | struct wlr_box saved_geometry; | ||
99 | 88 | ||
100 | struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; | 89 | struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; |
101 | struct wl_listener foreign_activate_request; | 90 | struct wl_listener foreign_activate_request; |
@@ -108,19 +97,16 @@ struct sway_view { | |||
108 | list_t *executed_criteria; // struct criteria * | 97 | list_t *executed_criteria; // struct criteria * |
109 | 98 | ||
110 | union { | 99 | union { |
111 | struct wlr_xdg_surface *wlr_xdg_surface; | 100 | struct wlr_xdg_toplevel *wlr_xdg_toplevel; |
112 | #if HAVE_XWAYLAND | 101 | #if HAVE_XWAYLAND |
113 | struct wlr_xwayland_surface *wlr_xwayland_surface; | 102 | struct wlr_xwayland_surface *wlr_xwayland_surface; |
114 | #endif | 103 | #endif |
115 | struct wlr_wl_shell_surface *wlr_wl_shell_surface; | ||
116 | }; | 104 | }; |
117 | 105 | ||
118 | struct { | 106 | struct { |
119 | struct wl_signal unmap; | 107 | struct wl_signal unmap; |
120 | } events; | 108 | } events; |
121 | 109 | ||
122 | struct wl_listener surface_new_subsurface; | ||
123 | |||
124 | int max_render_time; // In milliseconds | 110 | int max_render_time; // In milliseconds |
125 | 111 | ||
126 | enum seat_config_shortcuts_inhibit shortcuts_inhibit; | 112 | enum seat_config_shortcuts_inhibit shortcuts_inhibit; |
@@ -145,6 +131,8 @@ struct sway_xdg_shell_view { | |||
145 | struct sway_xwayland_view { | 131 | struct sway_xwayland_view { |
146 | struct sway_view view; | 132 | struct sway_view view; |
147 | 133 | ||
134 | struct wlr_scene_tree *surface_tree; | ||
135 | |||
148 | struct wl_listener commit; | 136 | struct wl_listener commit; |
149 | struct wl_listener request_move; | 137 | struct wl_listener request_move; |
150 | struct wl_listener request_resize; | 138 | struct wl_listener request_resize; |
@@ -156,71 +144,55 @@ struct sway_xwayland_view { | |||
156 | struct wl_listener set_title; | 144 | struct wl_listener set_title; |
157 | struct wl_listener set_class; | 145 | struct wl_listener set_class; |
158 | struct wl_listener set_role; | 146 | struct wl_listener set_role; |
147 | struct wl_listener set_startup_id; | ||
159 | struct wl_listener set_window_type; | 148 | struct wl_listener set_window_type; |
160 | struct wl_listener set_hints; | 149 | struct wl_listener set_hints; |
161 | struct wl_listener set_decorations; | 150 | struct wl_listener set_decorations; |
151 | struct wl_listener associate; | ||
152 | struct wl_listener dissociate; | ||
162 | struct wl_listener map; | 153 | struct wl_listener map; |
163 | struct wl_listener unmap; | 154 | struct wl_listener unmap; |
164 | struct wl_listener destroy; | 155 | struct wl_listener destroy; |
165 | struct wl_listener override_redirect; | 156 | struct wl_listener override_redirect; |
157 | |||
158 | struct wl_listener surface_tree_destroy; | ||
166 | }; | 159 | }; |
167 | 160 | ||
168 | struct sway_xwayland_unmanaged { | 161 | struct sway_xwayland_unmanaged { |
169 | struct wlr_xwayland_surface *wlr_xwayland_surface; | 162 | struct wlr_xwayland_surface *wlr_xwayland_surface; |
170 | struct wl_list link; | ||
171 | 163 | ||
172 | int lx, ly; | 164 | struct wlr_scene_surface *surface_scene; |
173 | 165 | ||
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_root_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; |
186 | |||
187 | struct wlr_scene_tree *scene_tree; | ||
188 | struct wlr_scene_tree *xdg_surface_tree; | ||
189 | struct wlr_xdg_popup *wlr_xdg_popup; | ||
220 | 190 | ||
221 | struct wlr_xdg_surface *wlr_xdg_surface; | 191 | struct sway_popup_desc desc; |
222 | 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,42 +265,31 @@ 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); |
311 | 274 | ||
312 | void view_begin_destroy(struct sway_view *view); | 275 | void view_begin_destroy(struct sway_view *view); |
313 | 276 | ||
277 | /** | ||
278 | * Map a view, ie. make it visible in the tree. | ||
279 | * | ||
280 | * `fullscreen` should be set to true (and optionally `fullscreen_output` | ||
281 | * should be populated) if the view should be made fullscreen immediately. | ||
282 | * | ||
283 | * `decoration` should be set to true if the client prefers CSD. The client's | ||
284 | * preference may be ignored. | ||
285 | */ | ||
314 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | 286 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, |
315 | bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); | 287 | bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); |
316 | 288 | ||
317 | void view_unmap(struct sway_view *view); | 289 | void view_unmap(struct sway_view *view); |
318 | 290 | ||
319 | void view_update_size(struct sway_view *view, int width, int height); | 291 | void view_update_size(struct sway_view *view); |
320 | 292 | void view_center_and_clip_surface(struct sway_view *view); | |
321 | void view_child_init(struct sway_view_child *child, | ||
322 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
323 | struct wlr_surface *surface); | ||
324 | |||
325 | void view_child_destroy(struct sway_view_child *child); | ||
326 | |||
327 | 293 | ||
328 | struct sway_view *view_from_wlr_xdg_surface( | 294 | struct sway_view *view_from_wlr_xdg_surface( |
329 | struct wlr_xdg_surface *xdg_surface); | 295 | struct wlr_xdg_surface *xdg_surface); |
@@ -333,6 +299,8 @@ struct sway_view *view_from_wlr_xwayland_surface( | |||
333 | #endif | 299 | #endif |
334 | struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); | 300 | struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); |
335 | 301 | ||
302 | void view_update_app_id(struct sway_view *view); | ||
303 | |||
336 | /** | 304 | /** |
337 | * Re-read the view's title property and update any relevant title bars. | 305 | * Re-read the view's title property and update any relevant title bars. |
338 | * The force argument makes it recreate the title bars even if the title hasn't | 306 | * The force argument makes it recreate the title bars even if the title hasn't |
@@ -362,4 +330,8 @@ void view_save_buffer(struct sway_view *view); | |||
362 | 330 | ||
363 | bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); | 331 | bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); |
364 | 332 | ||
333 | void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx); | ||
334 | |||
335 | void view_send_frame_done(struct sway_view *view); | ||
336 | |||
365 | #endif | 337 | #endif |
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index fdd92f64..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; |
@@ -60,20 +68,20 @@ void workspace_consider_destroy(struct sway_workspace *ws); | |||
60 | 68 | ||
61 | char *workspace_next_name(const char *output_name); | 69 | char *workspace_next_name(const char *output_name); |
62 | 70 | ||
63 | bool workspace_switch(struct sway_workspace *workspace, | 71 | struct sway_workspace *workspace_auto_back_and_forth( |
64 | bool no_auto_back_and_forth); | 72 | struct sway_workspace *workspace); |
73 | |||
74 | bool workspace_switch(struct sway_workspace *workspace); | ||
65 | 75 | ||
66 | struct sway_workspace *workspace_by_number(const char* name); | 76 | struct sway_workspace *workspace_by_number(const char* name); |
67 | 77 | ||
68 | struct sway_workspace *workspace_by_name(const char*); | 78 | struct sway_workspace *workspace_by_name(const char*); |
69 | 79 | ||
70 | struct sway_workspace *workspace_output_next( | 80 | struct sway_workspace *workspace_output_next(struct sway_workspace *current); |
71 | struct sway_workspace *current, bool create); | ||
72 | 81 | ||
73 | struct sway_workspace *workspace_next(struct sway_workspace *current); | 82 | struct sway_workspace *workspace_next(struct sway_workspace *current); |
74 | 83 | ||
75 | struct sway_workspace *workspace_output_prev( | 84 | struct sway_workspace *workspace_output_prev(struct sway_workspace *current); |
76 | struct sway_workspace *current, bool create); | ||
77 | 85 | ||
78 | struct sway_workspace *workspace_prev(struct sway_workspace *current); | 86 | struct sway_workspace *workspace_prev(struct sway_workspace *current); |
79 | 87 | ||
diff --git a/include/sway/xdg_decoration.h b/include/sway/xdg_decoration.h index 8bef4c6d..2388ebcb 100644 --- a/include/sway/xdg_decoration.h +++ b/include/sway/xdg_decoration.h | |||
@@ -16,4 +16,6 @@ struct sway_xdg_decoration { | |||
16 | struct sway_xdg_decoration *xdg_decoration_from_surface( | 16 | struct sway_xdg_decoration *xdg_decoration_from_surface( |
17 | struct wlr_surface *surface); | 17 | struct wlr_surface *surface); |
18 | 18 | ||
19 | void set_xdg_decoration_mode(struct sway_xdg_decoration *deco); | ||
20 | |||
19 | #endif | 21 | #endif |
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 545a66a8..197d2190 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "config.h" | 4 | #include "config.h" |
5 | #include "input.h" | 5 | #include "input.h" |
6 | #include "pool-buffer.h" | 6 | #include "pool-buffer.h" |
7 | #include "cursor-shape-v1-client-protocol.h" | ||
7 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 8 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
8 | #include "xdg-output-unstable-v1-client-protocol.h" | 9 | #include "xdg-output-unstable-v1-client-protocol.h" |
9 | 10 | ||
@@ -30,6 +31,7 @@ struct swaybar { | |||
30 | struct wl_compositor *compositor; | 31 | struct wl_compositor *compositor; |
31 | struct zwlr_layer_shell_v1 *layer_shell; | 32 | struct zwlr_layer_shell_v1 *layer_shell; |
32 | struct zxdg_output_manager_v1 *xdg_output_manager; | 33 | struct zxdg_output_manager_v1 *xdg_output_manager; |
34 | struct wp_cursor_shape_manager_v1 *cursor_shape_manager; | ||
33 | struct wl_shm *shm; | 35 | struct wl_shm *shm; |
34 | 36 | ||
35 | struct swaybar_config *config; | 37 | struct swaybar_config *config; |
@@ -58,7 +60,6 @@ struct swaybar_output { | |||
58 | struct zxdg_output_v1 *xdg_output; | 60 | struct zxdg_output_v1 *xdg_output; |
59 | struct wl_surface *surface; | 61 | struct wl_surface *surface; |
60 | struct zwlr_layer_surface_v1 *layer_surface; | 62 | struct zwlr_layer_surface_v1 *layer_surface; |
61 | struct wl_region *input_region; | ||
62 | uint32_t wl_name; | 63 | uint32_t wl_name; |
63 | 64 | ||
64 | struct wl_list workspaces; // swaybar_workspace::link | 65 | struct wl_list workspaces; // swaybar_workspace::link |
diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 4cacd21a..361acd99 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h | |||
@@ -6,6 +6,7 @@ | |||
6 | #include "../include/config.h" | 6 | #include "../include/config.h" |
7 | #include "list.h" | 7 | #include "list.h" |
8 | #include "util.h" | 8 | #include "util.h" |
9 | #include <pango/pangocairo.h> | ||
9 | 10 | ||
10 | struct box_colors { | 11 | struct box_colors { |
11 | uint32_t border; | 12 | uint32_t border; |
@@ -28,7 +29,7 @@ struct swaybar_config { | |||
28 | char *status_command; | 29 | char *status_command; |
29 | bool pango_markup; | 30 | bool pango_markup; |
30 | uint32_t position; // zwlr_layer_surface_v1_anchor | 31 | uint32_t position; // zwlr_layer_surface_v1_anchor |
31 | char *font; | 32 | PangoFontDescription *font_description; |
32 | char *sep_symbol; | 33 | char *sep_symbol; |
33 | char *mode; | 34 | char *mode; |
34 | char *hidden_state; | 35 | char *hidden_state; |
diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h index df8cdd09..dced2a6c 100644 --- a/include/swaybar/i3bar.h +++ b/include/swaybar/i3bar.h | |||
@@ -19,6 +19,7 @@ struct i3bar_block { | |||
19 | // Airblader features | 19 | // Airblader features |
20 | uint32_t background; | 20 | uint32_t background; |
21 | uint32_t border; | 21 | uint32_t border; |
22 | bool border_set; | ||
22 | int border_top; | 23 | int border_top; |
23 | int border_bottom; | 24 | int border_bottom; |
24 | int border_left; | 25 | int border_left; |
@@ -29,6 +30,6 @@ void i3bar_block_unref(struct i3bar_block *block); | |||
29 | bool i3bar_handle_readable(struct status_line *status); | 30 | bool i3bar_handle_readable(struct status_line *status); |
30 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, | 31 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, |
31 | struct i3bar_block *block, double x, double y, double rx, double ry, | 32 | struct i3bar_block *block, double x, double y, double rx, double ry, |
32 | double w, double h, int scale, uint32_t button); | 33 | double w, double h, int scale, uint32_t button, bool released); |
33 | 34 | ||
34 | #endif | 35 | #endif |
diff --git a/include/swaybar/image.h b/include/swaybar/image.h new file mode 100644 index 00000000..53a210dd --- /dev/null +++ b/include/swaybar/image.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef _SWAYBAR_IMAGE_H | ||
2 | #define _SWAYBAR_IMAGE_H | ||
3 | #include <cairo.h> | ||
4 | |||
5 | cairo_surface_t *load_image(const char *path); | ||
6 | |||
7 | #endif | ||
diff --git a/include/swaybar/input.h b/include/swaybar/input.h index e8735d88..8ea88a69 100644 --- a/include/swaybar/input.h +++ b/include/swaybar/input.h | |||
@@ -49,7 +49,7 @@ struct swaybar_hotspot { | |||
49 | int x, y, width, height; | 49 | int x, y, width, height; |
50 | enum hotspot_event_handling (*callback)(struct swaybar_output *output, | 50 | enum hotspot_event_handling (*callback)(struct swaybar_output *output, |
51 | struct swaybar_hotspot *hotspot, double x, double y, uint32_t button, | 51 | struct swaybar_hotspot *hotspot, double x, double y, uint32_t button, |
52 | void *data); | 52 | bool released, void *data); |
53 | void (*destroy)(void *data); | 53 | void (*destroy)(void *data); |
54 | void *data; | 54 | void *data; |
55 | }; | 55 | }; |
diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h index c02a5582..73937a0c 100644 --- a/include/swaybar/tray/item.h +++ b/include/swaybar/tray/item.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <cairo.h> | 4 | #include <cairo.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <stdint.h> | 6 | #include <stdint.h> |
7 | #include <wayland-util.h> | ||
7 | #include "swaybar/tray/tray.h" | 8 | #include "swaybar/tray/tray.h" |
8 | #include "list.h" | 9 | #include "list.h" |
9 | 10 | ||
diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h index 9e39e716..fb9e9c21 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h | |||
@@ -4,8 +4,9 @@ | |||
4 | #include <strings.h> | 4 | #include <strings.h> |
5 | #include "list.h" | 5 | #include "list.h" |
6 | #include "pool-buffer.h" | 6 | #include "pool-buffer.h" |
7 | #include "cursor-shape-v1-client-protocol.h" | ||
8 | |||
7 | #include "swaynag/types.h" | 9 | #include "swaynag/types.h" |
8 | #include "xdg-output-unstable-v1-client-protocol.h" | ||
9 | 10 | ||
10 | #define SWAYNAG_MAX_HEIGHT 500 | 11 | #define SWAYNAG_MAX_HEIGHT 500 |
11 | 12 | ||
@@ -59,6 +60,7 @@ struct swaynag_button { | |||
59 | struct swaynag_details { | 60 | struct swaynag_details { |
60 | bool visible; | 61 | bool visible; |
61 | char *message; | 62 | char *message; |
63 | char *details_text; | ||
62 | 64 | ||
63 | int x; | 65 | int x; |
64 | int y; | 66 | int y; |
@@ -75,18 +77,17 @@ struct swaynag_details { | |||
75 | 77 | ||
76 | struct swaynag { | 78 | struct swaynag { |
77 | bool run_display; | 79 | bool run_display; |
78 | int querying_outputs; | ||
79 | 80 | ||
80 | struct wl_display *display; | 81 | struct wl_display *display; |
81 | struct wl_compositor *compositor; | 82 | struct wl_compositor *compositor; |
82 | struct wl_seat *seat; | 83 | struct wl_seat *seat; |
83 | struct wl_shm *shm; | 84 | struct wl_shm *shm; |
84 | struct zxdg_output_manager_v1 *xdg_output_manager; | ||
85 | struct wl_list outputs; // swaynag_output::link | 85 | struct wl_list outputs; // swaynag_output::link |
86 | struct wl_list seats; // swaynag_seat::link | 86 | struct wl_list seats; // swaynag_seat::link |
87 | struct swaynag_output *output; | 87 | struct swaynag_output *output; |
88 | struct zwlr_layer_shell_v1 *layer_shell; | 88 | struct zwlr_layer_shell_v1 *layer_shell; |
89 | struct zwlr_layer_surface_v1 *layer_surface; | 89 | struct zwlr_layer_surface_v1 *layer_surface; |
90 | struct wp_cursor_shape_manager_v1 *cursor_shape_manager; | ||
90 | struct wl_surface *surface; | 91 | struct wl_surface *surface; |
91 | 92 | ||
92 | uint32_t width; | 93 | uint32_t width; |
diff --git a/include/swaynag/types.h b/include/swaynag/types.h index 24da9418..9c3c50db 100644 --- a/include/swaynag/types.h +++ b/include/swaynag/types.h | |||
@@ -1,12 +1,17 @@ | |||
1 | #ifndef _SWAYNAG_TYPES_H | 1 | #ifndef _SWAYNAG_TYPES_H |
2 | #define _SWAYNAG_TYPES_H | 2 | #define _SWAYNAG_TYPES_H |
3 | 3 | ||
4 | #include <stdint.h> | ||
5 | #include <pango/pangocairo.h> | ||
6 | #include "list.h" | ||
7 | |||
4 | struct swaynag_type { | 8 | struct swaynag_type { |
5 | char *name; | 9 | char *name; |
6 | 10 | ||
7 | char *font; | 11 | PangoFontDescription *font_description; |
8 | char *output; | 12 | char *output; |
9 | uint32_t anchors; | 13 | uint32_t anchors; |
14 | int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset | ||
10 | 15 | ||
11 | // Colors | 16 | // Colors |
12 | uint32_t button_text; | 17 | uint32_t button_text; |
diff --git a/include/util.h b/include/util.h index c80da1cb..f887d489 100644 --- a/include/util.h +++ b/include/util.h | |||
@@ -30,12 +30,6 @@ int parse_movement_amount(int argc, char **argv, | |||
30 | struct movement_amount *amount); | 30 | struct movement_amount *amount); |
31 | 31 | ||
32 | /** | 32 | /** |
33 | * Get the current time, in milliseconds. | ||
34 | */ | ||
35 | |||
36 | uint32_t get_current_time_msec(void); | ||
37 | |||
38 | /** | ||
39 | * Wrap i into the range [0, max] | 33 | * Wrap i into the range [0, max] |
40 | */ | 34 | */ |
41 | int wrap(int i, int max); | 35 | int wrap(int i, int max); |
diff --git a/meson.build b/meson.build index 38a55678..1043e4ba 100644 --- a/meson.build +++ b/meson.build | |||
@@ -1,9 +1,9 @@ | |||
1 | project( | 1 | project( |
2 | 'sway', | 2 | 'sway', |
3 | 'c', | 3 | 'c', |
4 | version: '1.5', #release_version | 4 | version: '1.10-dev', |
5 | license: 'MIT', | 5 | license: 'MIT', |
6 | meson_version: '>=0.53.0', | 6 | meson_version: '>=0.60.0', |
7 | default_options: [ | 7 | default_options: [ |
8 | 'c_std=c11', | 8 | 'c_std=c11', |
9 | 'warning_level=2', | 9 | 'warning_level=2', |
@@ -14,10 +14,12 @@ project( | |||
14 | add_project_arguments( | 14 | add_project_arguments( |
15 | [ | 15 | [ |
16 | '-DWLR_USE_UNSTABLE', | 16 | '-DWLR_USE_UNSTABLE', |
17 | '-D_POSIX_C_SOURCE=200809L', | ||
17 | 18 | ||
18 | '-Wno-unused-parameter', | 19 | '-Wno-unused-parameter', |
19 | '-Wno-unused-result', | 20 | '-Wno-unused-result', |
20 | '-Wno-missing-braces', | 21 | '-Wno-missing-braces', |
22 | '-Wno-format-zero-length', | ||
21 | '-Wundef', | 23 | '-Wundef', |
22 | '-Wvla', | 24 | '-Wvla', |
23 | ], | 25 | ], |
@@ -35,79 +37,65 @@ if is_freebsd | |||
35 | add_project_arguments('-D_C11_SOURCE', language: 'c') | 37 | add_project_arguments('-D_C11_SOURCE', language: 'c') |
36 | endif | 38 | endif |
37 | 39 | ||
38 | jsonc = dependency('json-c', version: '>=0.13') | 40 | # Execute the wlroots subproject, if any |
39 | pcre = dependency('libpcre') | 41 | wlroots_version = ['>=0.18.0', '<0.19.0'] |
40 | wayland_server = dependency('wayland-server') | 42 | subproject( |
41 | wayland_client = dependency('wayland-client') | ||
42 | wayland_cursor = dependency('wayland-cursor') | ||
43 | wayland_egl = dependency('wayland-egl') | ||
44 | wayland_protos = dependency('wayland-protocols', version: '>=1.14') | ||
45 | xkbcommon = dependency('xkbcommon') | ||
46 | cairo = dependency('cairo') | ||
47 | pango = dependency('pango') | ||
48 | pangocairo = dependency('pangocairo') | ||
49 | gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) | ||
50 | pixman = dependency('pixman-1') | ||
51 | glesv2 = dependency('glesv2') | ||
52 | libevdev = dependency('libevdev') | ||
53 | libinput = dependency('libinput', version: '>=1.6.0') | ||
54 | xcb = dependency('xcb', required: get_option('xwayland')) | ||
55 | bash_comp = dependency('bash-completion', required: false) | ||
56 | fish_comp = dependency('fish', required: false) | ||
57 | math = cc.find_library('m') | ||
58 | rt = cc.find_library('rt') | ||
59 | |||
60 | # Try first to find wlroots as a subproject, then as a system dependency | ||
61 | wlroots_version = ['>=0.12.0', '<0.13.0'] | ||
62 | wlroots_proj = subproject( | ||
63 | 'wlroots', | 43 | 'wlroots', |
64 | default_options: ['examples=false'], | 44 | default_options: ['examples=false'], |
65 | required: false, | 45 | required: false, |
66 | version: wlroots_version, | 46 | version: wlroots_version, |
67 | ) | 47 | ) |
48 | wlroots = dependency('wlroots', version: wlroots_version) | ||
68 | wlroots_features = { | 49 | wlroots_features = { |
69 | 'xwayland': false, | 50 | 'xwayland': false, |
70 | 'systemd': false, | 51 | 'libinput_backend': false, |
71 | 'elogind': false, | 52 | 'session': false, |
72 | 'libseat': false, | ||
73 | } | 53 | } |
74 | if wlroots_proj.found() | 54 | foreach name, _ : wlroots_features |
75 | wlroots = wlroots_proj.get_variable('wlroots') | 55 | var_name = 'have_' + name.underscorify() |
76 | wlroots_conf = wlroots_proj.get_variable('conf_data') | 56 | have = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true' |
77 | foreach name, _ : wlroots_features | 57 | wlroots_features += { name: have } |
78 | has = wlroots_conf.get('WLR_HAS_' + name.to_upper()) == 1 | 58 | endforeach |
79 | wlroots_features += { name: has } | ||
80 | endforeach | ||
81 | else | ||
82 | wlroots = dependency('wlroots', version: wlroots_version) | ||
83 | foreach name, _ : wlroots_features | ||
84 | has = cc.get_define('WLR_HAS_' + name.to_upper(), prefix: '#include <wlr/config.h>', dependencies: wlroots) == '1' | ||
85 | wlroots_features += { name: has } | ||
86 | endforeach | ||
87 | endif | ||
88 | 59 | ||
89 | if get_option('xwayland').enabled() and not wlroots_features['xwayland'] | 60 | if get_option('xwayland').enabled() and not wlroots_features['xwayland'] |
90 | error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') | 61 | error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') |
91 | endif | 62 | endif |
92 | have_xwayland = xcb.found() and wlroots_features['xwayland'] | 63 | |
64 | null_dep = dependency('', required: false) | ||
65 | |||
66 | jsonc = dependency('json-c', version: '>=0.13') | ||
67 | pcre2 = dependency('libpcre2-8') | ||
68 | wayland_server = dependency('wayland-server', version: '>=1.21.0') | ||
69 | wayland_client = dependency('wayland-client') | ||
70 | wayland_cursor = dependency('wayland-cursor') | ||
71 | wayland_protos = dependency('wayland-protocols', version: '>=1.24') | ||
72 | xkbcommon = dependency('xkbcommon', version: '>=1.5.0') | ||
73 | cairo = dependency('cairo') | ||
74 | pango = dependency('pango') | ||
75 | pangocairo = dependency('pangocairo') | ||
76 | gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) | ||
77 | pixman = dependency('pixman-1') | ||
78 | libevdev = dependency('libevdev') | ||
79 | libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.21.0') : null_dep | ||
80 | xcb = dependency('xcb', required: get_option('xwayland')) | ||
81 | drm = dependency('libdrm') | ||
82 | libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep | ||
83 | math = cc.find_library('m') | ||
84 | rt = cc.find_library('rt') | ||
85 | xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) | ||
86 | threads = dependency('threads') # for pthread_setschedparam | ||
87 | |||
88 | have_xwayland = xcb.found() and xcb_icccm.found() and wlroots_features['xwayland'] | ||
93 | 89 | ||
94 | if get_option('sd-bus-provider') == 'auto' | 90 | if get_option('sd-bus-provider') == 'auto' |
95 | if not get_option('tray').disabled() | 91 | if not get_option('tray').disabled() |
96 | assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto') | 92 | assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto') |
97 | endif | 93 | endif |
98 | sdbus = dependency('libsystemd', | 94 | sdbus = dependency(['libsystemd', 'libelogind'], |
99 | required: false, | 95 | required: false, |
100 | version: '>=239', | 96 | version: '>=239', |
101 | not_found_message: 'libsystemd not found, trying libelogind', | ||
102 | ) | 97 | ) |
103 | if not sdbus.found() | 98 | if not sdbus.found() |
104 | sdbus = dependency('libelogind', | ||
105 | required: false, | ||
106 | version: '>=239', | ||
107 | not_found_message: 'libelogind not found, trying basu', | ||
108 | ) | ||
109 | endif | ||
110 | if not sdbus.found() | ||
111 | sdbus = dependency('basu', required: false) | 99 | sdbus = dependency('basu', required: false) |
112 | endif | 100 | endif |
113 | else | 101 | else |
@@ -128,11 +116,15 @@ conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd | |||
128 | conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') | 116 | conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') |
129 | conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') | 117 | conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') |
130 | 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 | )) | ||
131 | 124 | ||
132 | 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')) |
133 | if scdoc.found() | 126 | if scdoc.found() |
134 | scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) | 127 | scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true) |
135 | sh = find_program('sh', native: true) | ||
136 | mandir = get_option('mandir') | 128 | mandir = get_option('mandir') |
137 | man_files = [ | 129 | man_files = [ |
138 | 'sway/sway.1.scd', | 130 | 'sway/sway.1.scd', |
@@ -143,9 +135,15 @@ if scdoc.found() | |||
143 | 'sway/sway-output.5.scd', | 135 | 'sway/sway-output.5.scd', |
144 | 'swaybar/swaybar-protocol.7.scd', | 136 | 'swaybar/swaybar-protocol.7.scd', |
145 | 'swaymsg/swaymsg.1.scd', | 137 | 'swaymsg/swaymsg.1.scd', |
146 | 'swaynag/swaynag.1.scd', | ||
147 | 'swaynag/swaynag.5.scd', | ||
148 | ] | 138 | ] |
139 | |||
140 | if get_option('swaynag') | ||
141 | man_files += [ | ||
142 | 'swaynag/swaynag.1.scd', | ||
143 | 'swaynag/swaynag.5.scd', | ||
144 | ] | ||
145 | endif | ||
146 | |||
149 | foreach filename : man_files | 147 | foreach filename : man_files |
150 | topic = filename.split('.')[-3].split('/')[-1] | 148 | topic = filename.split('.')[-3].split('/')[-1] |
151 | section = filename.split('.')[-2] | 149 | section = filename.split('.')[-2] |
@@ -155,10 +153,10 @@ if scdoc.found() | |||
155 | output, | 153 | output, |
156 | input: filename, | 154 | input: filename, |
157 | output: output, | 155 | output: output, |
158 | command: [ | 156 | command: scdoc_prog, |
159 | sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output) | ||
160 | ], | ||
161 | install: true, | 157 | install: true, |
158 | feed: true, | ||
159 | capture: true, | ||
162 | install_dir: '@0@/man@1@'.format(mandir, section) | 160 | install_dir: '@0@/man@1@'.format(mandir, section) |
163 | ) | 161 | ) |
164 | endforeach | 162 | endforeach |
@@ -169,8 +167,8 @@ add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir | |||
169 | version = '"@0@"'.format(meson.project_version()) | 167 | version = '"@0@"'.format(meson.project_version()) |
170 | git = find_program('git', native: true, required: false) | 168 | git = find_program('git', native: true, required: false) |
171 | if git.found() | 169 | if git.found() |
172 | git_commit = run_command([git, 'rev-parse', '--short', 'HEAD']) | 170 | git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false) |
173 | git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD']) | 171 | git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) |
174 | if git_commit.returncode() == 0 and git_branch.returncode() == 0 | 172 | if git_commit.returncode() == 0 and git_branch.returncode() == 0 |
175 | version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( | 173 | version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( |
176 | meson.project_version(), | 174 | meson.project_version(), |
@@ -183,7 +181,7 @@ add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') | |||
183 | 181 | ||
184 | # Compute the relative path used by compiler invocations. | 182 | # Compute the relative path used by compiler invocations. |
185 | source_root = meson.current_source_dir().split('/') | 183 | source_root = meson.current_source_dir().split('/') |
186 | build_root = meson.build_root().split('/') | 184 | build_root = meson.global_build_root().split('/') |
187 | relative_dir_parts = [] | 185 | relative_dir_parts = [] |
188 | i = 0 | 186 | i = 0 |
189 | in_prefix = true | 187 | in_prefix = true |
@@ -227,9 +225,15 @@ subdir('common') | |||
227 | subdir('sway') | 225 | subdir('sway') |
228 | subdir('swaymsg') | 226 | subdir('swaymsg') |
229 | 227 | ||
230 | subdir('client') | 228 | if get_option('swaybar') or get_option('swaynag') |
231 | subdir('swaybar') | 229 | subdir('client') |
232 | subdir('swaynag') | 230 | endif |
231 | if get_option('swaybar') | ||
232 | subdir('swaybar') | ||
233 | endif | ||
234 | if get_option('swaynag') | ||
235 | subdir('swaynag') | ||
236 | endif | ||
233 | 237 | ||
234 | config = configuration_data() | 238 | config = configuration_data() |
235 | config.set('datadir', join_paths(prefix, datadir)) | 239 | config.set('datadir', join_paths(prefix, datadir)) |
@@ -264,61 +268,11 @@ if get_option('default-wallpaper') | |||
264 | install_data(wallpaper_files, install_dir: wallpaper_install_dir) | 268 | install_data(wallpaper_files, install_dir: wallpaper_install_dir) |
265 | endif | 269 | endif |
266 | 270 | ||
267 | if get_option('zsh-completions') | 271 | subdir('completions') |
268 | zsh_files = files( | ||
269 | 'completions/zsh/_sway', | ||
270 | 'completions/zsh/_swaymsg', | ||
271 | ) | ||
272 | zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions') | ||
273 | |||
274 | install_data(zsh_files, install_dir: zsh_install_dir) | ||
275 | endif | ||
276 | |||
277 | if get_option('bash-completions') | ||
278 | bash_files = files( | ||
279 | 'completions/bash/sway', | ||
280 | 'completions/bash/swaybar', | ||
281 | 'completions/bash/swaymsg', | ||
282 | ) | ||
283 | if bash_comp.found() | ||
284 | bash_install_dir = bash_comp.get_pkgconfig_variable( | ||
285 | 'completionsdir', | ||
286 | define_variable: ['datadir', datadir] | ||
287 | ) | ||
288 | else | ||
289 | bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') | ||
290 | endif | ||
291 | |||
292 | install_data(bash_files, install_dir: bash_install_dir) | ||
293 | endif | ||
294 | |||
295 | if get_option('fish-completions') | ||
296 | fish_files = files( | ||
297 | 'completions/fish/sway.fish', | ||
298 | 'completions/fish/swaymsg.fish', | ||
299 | 'completions/fish/swaynag.fish', | ||
300 | ) | ||
301 | if fish_comp.found() | ||
302 | fish_install_dir = fish_comp.get_pkgconfig_variable( | ||
303 | 'completionsdir', | ||
304 | define_variable: ['datadir', datadir] | ||
305 | ) | ||
306 | else | ||
307 | fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') | ||
308 | endif | ||
309 | |||
310 | install_data(fish_files, install_dir: fish_install_dir) | ||
311 | endif | ||
312 | 272 | ||
313 | summary({ | 273 | summary({ |
314 | 'xwayland': have_xwayland, | 274 | 'xwayland': have_xwayland, |
315 | 'gdk-pixbuf': gdk_pixbuf.found(), | 275 | 'gdk-pixbuf': gdk_pixbuf.found(), |
316 | 'sd-bus': sdbus.found(), | ||
317 | 'tray': have_tray, | 276 | 'tray': have_tray, |
318 | 'man-pages': scdoc.found(), | 277 | 'man-pages': scdoc.found(), |
319 | }, bool_yn: true) | 278 | }, bool_yn: true) |
320 | |||
321 | if not wlroots_features['systemd'] and not wlroots_features['elogind'] and not wlroots_features['libseat'] | ||
322 | warning('The sway binary must be setuid when compiled without (e)logind or libseat') | ||
323 | warning('You must do this manually post-install: chmod a+s /path/to/sway') | ||
324 | endif | ||
diff --git a/meson_options.txt b/meson_options.txt index e36900b6..8d0d6509 100644 --- a/meson_options.txt +++ b/meson_options.txt | |||
@@ -2,8 +2,10 @@ option('default-wallpaper', type: 'boolean', value: true, description: 'Install | |||
2 | option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') | 2 | option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') |
3 | option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.') | 3 | option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.') |
4 | option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.') | 4 | option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.') |
5 | option('swaybar', type: 'boolean', value: true, description: 'Enable support for swaybar') | ||
6 | option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag') | ||
5 | 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') |
6 | 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') |
7 | 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') |
8 | 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') |
9 | 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 124e9777..81edb584 100644 --- a/protocols/meson.build +++ b/protocols/meson.build | |||
@@ -1,80 +1,43 @@ | |||
1 | wl_protocol_dir = wayland_protos.get_pkgconfig_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_pkgconfig_variable('wayland_scanner'), | 6 | native: true, |
7 | native: true, | 7 | ) |
8 | ) | ||
9 | else | ||
10 | wayland_scanner = find_program('wayland-scanner', native: true) | ||
11 | endif | ||
12 | 8 | ||
13 | protocols = [ | 9 | protocols = [ |
14 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | 10 | wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', |
15 | [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], | 11 | wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', |
16 | [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], | 12 | wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', |
17 | [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'], | 13 | wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml', |
18 | ['wlr-layer-shell-unstable-v1.xml'], | 14 | wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', |
19 | ['idle.xml'], | 15 | wl_protocol_dir / 'staging/content-type/content-type-v1.xml', |
20 | ['wlr-input-inhibitor-unstable-v1.xml'], | 16 | wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', |
21 | ['wlr-output-power-management-unstable-v1.xml'], | 17 | 'wlr-layer-shell-unstable-v1.xml', |
22 | ] | 18 | 'idle.xml', |
23 | 19 | 'wlr-output-power-management-unstable-v1.xml', | |
24 | client_protocols = [ | ||
25 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | ||
26 | [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], | ||
27 | ['wlr-layer-shell-unstable-v1.xml'], | ||
28 | ['wlr-input-inhibitor-unstable-v1.xml'], | ||
29 | ] | 20 | ] |
30 | 21 | ||
31 | wl_protos_src = [] | 22 | wl_protos_src = [] |
32 | wl_protos_headers = [] | ||
33 | 23 | ||
34 | foreach p : protocols | 24 | foreach xml : protocols |
35 | xml = join_paths(p) | ||
36 | wl_protos_src += custom_target( | 25 | wl_protos_src += custom_target( |
37 | xml.underscorify() + '_server_c', | 26 | xml.underscorify() + '_c', |
38 | input: xml, | 27 | input: xml, |
39 | output: '@BASENAME@-protocol.c', | 28 | output: '@BASENAME@-protocol.c', |
40 | command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], | 29 | command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], |
41 | ) | 30 | ) |
42 | wl_protos_headers += custom_target( | 31 | wl_protos_src += custom_target( |
43 | xml.underscorify() + '_server_h', | 32 | xml.underscorify() + '_server_h', |
44 | input: xml, | 33 | input: xml, |
45 | output: '@BASENAME@-protocol.h', | 34 | output: '@BASENAME@-protocol.h', |
46 | command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], | 35 | command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], |
47 | ) | 36 | ) |
48 | endforeach | 37 | wl_protos_src += custom_target( |
49 | |||
50 | foreach p : client_protocols | ||
51 | xml = join_paths(p) | ||
52 | wl_protos_headers += custom_target( | ||
53 | xml.underscorify() + '_client_h', | 38 | xml.underscorify() + '_client_h', |
54 | input: xml, | 39 | input: xml, |
55 | output: '@BASENAME@-client-protocol.h', | 40 | output: '@BASENAME@-client-protocol.h', |
56 | command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], | 41 | command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], |
57 | ) | 42 | ) |
58 | endforeach | 43 | endforeach |
59 | |||
60 | lib_client_protos = static_library( | ||
61 | 'client_protos', | ||
62 | wl_protos_src + wl_protos_headers, | ||
63 | dependencies: wayland_client.partial_dependency(compile_args: true), | ||
64 | ) | ||
65 | |||
66 | client_protos = declare_dependency( | ||
67 | link_with: lib_client_protos, | ||
68 | sources: wl_protos_headers, | ||
69 | ) | ||
70 | |||
71 | lib_server_protos = static_library( | ||
72 | 'server_protos', | ||
73 | wl_protos_src + wl_protos_headers, | ||
74 | dependencies: wayland_server.partial_dependency(compile_args: true), | ||
75 | ) | ||
76 | |||
77 | server_protos = declare_dependency( | ||
78 | link_with: lib_server_protos, | ||
79 | sources: wl_protos_headers, | ||
80 | ) | ||
diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml deleted file mode 100644 index b62d1bb4..00000000 --- a/protocols/wlr-input-inhibitor-unstable-v1.xml +++ /dev/null | |||
@@ -1,67 +0,0 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8"?> | ||
2 | <protocol name="wlr_input_inhibit_unstable_v1"> | ||
3 | <copyright> | ||
4 | Copyright © 2018 Drew DeVault | ||
5 | |||
6 | Permission to use, copy, modify, distribute, and sell this | ||
7 | software and its documentation for any purpose is hereby granted | ||
8 | without fee, provided that the above copyright notice appear in | ||
9 | all copies and that both that copyright notice and this permission | ||
10 | notice appear in supporting documentation, and that the name of | ||
11 | the copyright holders not be used in advertising or publicity | ||
12 | pertaining to distribution of the software without specific, | ||
13 | written prior permission. The copyright holders make no | ||
14 | representations about the suitability of this software for any | ||
15 | purpose. It is provided "as is" without express or implied | ||
16 | warranty. | ||
17 | |||
18 | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | ||
19 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||
20 | FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
21 | SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
22 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN | ||
23 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | ||
24 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF | ||
25 | THIS SOFTWARE. | ||
26 | </copyright> | ||
27 | |||
28 | <interface name="zwlr_input_inhibit_manager_v1" version="1"> | ||
29 | <description summary="inhibits input events to other clients"> | ||
30 | Clients can use this interface to prevent input events from being sent to | ||
31 | any surfaces but its own, which is useful for example in lock screen | ||
32 | software. It is assumed that access to this interface will be locked down | ||
33 | to whitelisted clients by the compositor. | ||
34 | </description> | ||
35 | |||
36 | <request name="get_inhibitor"> | ||
37 | <description summary="inhibit input to other clients"> | ||
38 | Activates the input inhibitor. As long as the inhibitor is active, the | ||
39 | compositor will not send input events to other clients. | ||
40 | </description> | ||
41 | <arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/> | ||
42 | </request> | ||
43 | |||
44 | <enum name="error"> | ||
45 | <entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/> | ||
46 | </enum> | ||
47 | </interface> | ||
48 | |||
49 | <interface name="zwlr_input_inhibitor_v1" version="1"> | ||
50 | <description summary="inhibits input to other clients"> | ||
51 | While this resource exists, input to clients other than the owner of the | ||
52 | inhibitor resource will not receive input events. The client that owns | ||
53 | this resource will receive all input events normally. The compositor will | ||
54 | also disable all of its own input processing (such as keyboard shortcuts) | ||
55 | while the inhibitor is active. | ||
56 | |||
57 | The compositor may continue to send input events to selected clients, | ||
58 | such as an on-screen keyboard (via the input-method protocol). | ||
59 | </description> | ||
60 | |||
61 | <request name="destroy" type="destructor"> | ||
62 | <description summary="destroy the input inhibitor object"> | ||
63 | Destroy the inhibitor and allow other clients to receive input. | ||
64 | </description> | ||
65 | </request> | ||
66 | </interface> | ||
67 | </protocol> | ||
diff --git a/release.sh b/release.sh new file mode 100755 index 00000000..62baf415 --- /dev/null +++ b/release.sh | |||
@@ -0,0 +1,31 @@ | |||
1 | #!/bin/sh -eu | ||
2 | |||
3 | prev=$(git describe --tags --abbrev=0) | ||
4 | next=$(meson rewrite kwargs info project / 2>&1 >/dev/null | jq -r '.kwargs["project#/"].version') | ||
5 | |||
6 | case "$next" in | ||
7 | *-dev) | ||
8 | echo "This is a development version" | ||
9 | exit 1 | ||
10 | ;; | ||
11 | esac | ||
12 | |||
13 | if [ "$prev" = "$next" ]; then | ||
14 | echo "Version not bumped in meson.build" | ||
15 | exit 1 | ||
16 | fi | ||
17 | |||
18 | if ! git diff-index --quiet HEAD -- meson.build; then | ||
19 | echo "meson.build not committed" | ||
20 | exit 1 | ||
21 | fi | ||
22 | |||
23 | shortlog="$(git shortlog --no-merges "$prev..")" | ||
24 | (echo "sway $next"; echo ""; echo "$shortlog") | git tag "$next" -ase -F - | ||
25 | |||
26 | prefix=sway-$next | ||
27 | archive=$prefix.tar.gz | ||
28 | git archive --prefix="$prefix/" -o "$archive" "$next" | ||
29 | gpg --output "$archive".sig --detach-sig "$archive" | ||
30 | |||
31 | gh release create "sway $next" -t "$next" -n "" -d "$archive" "$archive.sig" | ||
diff --git a/sway/commands.c b/sway/commands.c index fe1e98b5..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> |
@@ -42,15 +41,17 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type | |||
42 | } | 41 | } |
43 | 42 | ||
44 | /* Keep alphabetized */ | 43 | /* Keep alphabetized */ |
45 | static struct cmd_handler handlers[] = { | 44 | static const struct cmd_handler handlers[] = { |
46 | { "assign", cmd_assign }, | 45 | { "assign", cmd_assign }, |
47 | { "bar", cmd_bar }, | 46 | { "bar", cmd_bar }, |
48 | { "bindcode", cmd_bindcode }, | 47 | { "bindcode", cmd_bindcode }, |
48 | { "bindgesture", cmd_bindgesture }, | ||
49 | { "bindswitch", cmd_bindswitch }, | 49 | { "bindswitch", cmd_bindswitch }, |
50 | { "bindsym", cmd_bindsym }, | 50 | { "bindsym", cmd_bindsym }, |
51 | { "client.background", cmd_client_noop }, | 51 | { "client.background", cmd_client_noop }, |
52 | { "client.focused", cmd_client_focused }, | 52 | { "client.focused", cmd_client_focused }, |
53 | { "client.focused_inactive", cmd_client_focused_inactive }, | 53 | { "client.focused_inactive", cmd_client_focused_inactive }, |
54 | { "client.focused_tab_title", cmd_client_focused_tab_title }, | ||
54 | { "client.placeholder", cmd_client_noop }, | 55 | { "client.placeholder", cmd_client_noop }, |
55 | { "client.unfocused", cmd_client_unfocused }, | 56 | { "client.unfocused", cmd_client_unfocused }, |
56 | { "client.urgent", cmd_client_urgent }, | 57 | { "client.urgent", cmd_client_urgent }, |
@@ -91,6 +92,7 @@ static struct cmd_handler handlers[] = { | |||
91 | { "titlebar_border_thickness", cmd_titlebar_border_thickness }, | 92 | { "titlebar_border_thickness", cmd_titlebar_border_thickness }, |
92 | { "titlebar_padding", cmd_titlebar_padding }, | 93 | { "titlebar_padding", cmd_titlebar_padding }, |
93 | { "unbindcode", cmd_unbindcode }, | 94 | { "unbindcode", cmd_unbindcode }, |
95 | { "unbindgesture", cmd_unbindgesture }, | ||
94 | { "unbindswitch", cmd_unbindswitch }, | 96 | { "unbindswitch", cmd_unbindswitch }, |
95 | { "unbindsym", cmd_unbindsym }, | 97 | { "unbindsym", cmd_unbindsym }, |
96 | { "workspace", cmd_workspace }, | 98 | { "workspace", cmd_workspace }, |
@@ -98,9 +100,10 @@ static struct cmd_handler handlers[] = { | |||
98 | }; | 100 | }; |
99 | 101 | ||
100 | /* Config-time only commands. Keep alphabetized */ | 102 | /* Config-time only commands. Keep alphabetized */ |
101 | static struct cmd_handler config_handlers[] = { | 103 | static const struct cmd_handler config_handlers[] = { |
102 | { "default_orientation", cmd_default_orientation }, | 104 | { "default_orientation", cmd_default_orientation }, |
103 | { "include", cmd_include }, | 105 | { "include", cmd_include }, |
106 | { "primary_selection", cmd_primary_selection }, | ||
104 | { "swaybg_command", cmd_swaybg_command }, | 107 | { "swaybg_command", cmd_swaybg_command }, |
105 | { "swaynag_command", cmd_swaynag_command }, | 108 | { "swaynag_command", cmd_swaynag_command }, |
106 | { "workspace_layout", cmd_workspace_layout }, | 109 | { "workspace_layout", cmd_workspace_layout }, |
@@ -108,7 +111,7 @@ static struct cmd_handler config_handlers[] = { | |||
108 | }; | 111 | }; |
109 | 112 | ||
110 | /* Runtime-only commands. Keep alphabetized */ | 113 | /* Runtime-only commands. Keep alphabetized */ |
111 | static struct cmd_handler command_handlers[] = { | 114 | static const struct cmd_handler command_handlers[] = { |
112 | { "border", cmd_border }, | 115 | { "border", cmd_border }, |
113 | { "create_output", cmd_create_output }, | 116 | { "create_output", cmd_create_output }, |
114 | { "exit", cmd_exit }, | 117 | { "exit", cmd_exit }, |
@@ -144,22 +147,22 @@ static int handler_compare(const void *_a, const void *_b) { | |||
144 | return strcasecmp(a->command, b->command); | 147 | return strcasecmp(a->command, b->command); |
145 | } | 148 | } |
146 | 149 | ||
147 | struct cmd_handler *find_handler(char *line, struct cmd_handler *handlers, | 150 | const struct cmd_handler *find_handler(const char *line, |
148 | size_t handlers_size) { | 151 | const struct cmd_handler *handlers, size_t handlers_size) { |
149 | if (!handlers || !handlers_size) { | 152 | if (!handlers || !handlers_size) { |
150 | return NULL; | 153 | return NULL; |
151 | } | 154 | } |
152 | struct cmd_handler query = { .command = line }; | 155 | const struct cmd_handler query = { .command = line }; |
153 | return bsearch(&query, handlers, | 156 | return bsearch(&query, handlers, |
154 | handlers_size / sizeof(struct cmd_handler), | 157 | handlers_size / sizeof(struct cmd_handler), |
155 | sizeof(struct cmd_handler), handler_compare); | 158 | sizeof(struct cmd_handler), handler_compare); |
156 | } | 159 | } |
157 | 160 | ||
158 | static struct cmd_handler *find_handler_ex(char *line, | 161 | static const struct cmd_handler *find_handler_ex(char *line, |
159 | struct cmd_handler *config_handlers, size_t config_handlers_size, | 162 | const struct cmd_handler *config_handlers, size_t config_handlers_size, |
160 | struct cmd_handler *command_handlers, size_t command_handlers_size, | 163 | const struct cmd_handler *command_handlers, size_t command_handlers_size, |
161 | struct cmd_handler *handlers, size_t handlers_size) { | 164 | const struct cmd_handler *handlers, size_t handlers_size) { |
162 | struct cmd_handler *handler = NULL; | 165 | const struct cmd_handler *handler = NULL; |
163 | if (config->reading) { | 166 | if (config->reading) { |
164 | handler = find_handler(line, config_handlers, config_handlers_size); | 167 | handler = find_handler(line, config_handlers, config_handlers_size); |
165 | } else if (config->active) { | 168 | } else if (config->active) { |
@@ -168,16 +171,17 @@ static struct cmd_handler *find_handler_ex(char *line, | |||
168 | return handler ? handler : find_handler(line, handlers, handlers_size); | 171 | return handler ? handler : find_handler(line, handlers, handlers_size); |
169 | } | 172 | } |
170 | 173 | ||
171 | static struct cmd_handler *find_core_handler(char *line) { | 174 | static const struct cmd_handler *find_core_handler(char *line) { |
172 | return find_handler_ex(line, config_handlers, sizeof(config_handlers), | 175 | return find_handler_ex(line, config_handlers, sizeof(config_handlers), |
173 | command_handlers, sizeof(command_handlers), | 176 | command_handlers, sizeof(command_handlers), |
174 | handlers, sizeof(handlers)); | 177 | handlers, sizeof(handlers)); |
175 | } | 178 | } |
176 | 179 | ||
177 | static void set_config_node(struct sway_node *node) { | 180 | static void set_config_node(struct sway_node *node, bool node_overridden) { |
178 | config->handler_context.node = node; | 181 | config->handler_context.node = node; |
179 | config->handler_context.container = NULL; | 182 | config->handler_context.container = NULL; |
180 | config->handler_context.workspace = NULL; | 183 | config->handler_context.workspace = NULL; |
184 | config->handler_context.node_overridden = node_overridden; | ||
181 | 185 | ||
182 | if (node == NULL) { | 186 | if (node == NULL) { |
183 | return; | 187 | return; |
@@ -186,7 +190,7 @@ static void set_config_node(struct sway_node *node) { | |||
186 | switch (node->type) { | 190 | switch (node->type) { |
187 | case N_CONTAINER: | 191 | case N_CONTAINER: |
188 | config->handler_context.container = node->sway_container; | 192 | config->handler_context.container = node->sway_container; |
189 | config->handler_context.workspace = node->sway_container->workspace; | 193 | config->handler_context.workspace = node->sway_container->pending.workspace; |
190 | break; | 194 | break; |
191 | case N_WORKSPACE: | 195 | case N_WORKSPACE: |
192 | config->handler_context.workspace = node->sway_workspace; | 196 | config->handler_context.workspace = node->sway_workspace; |
@@ -202,6 +206,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
202 | char *cmd; | 206 | char *cmd; |
203 | char matched_delim = ';'; | 207 | char matched_delim = ';'; |
204 | list_t *containers = NULL; | 208 | list_t *containers = NULL; |
209 | bool using_criteria = false; | ||
205 | 210 | ||
206 | if (seat == NULL) { | 211 | if (seat == NULL) { |
207 | // passing a NULL seat means we just pick the default seat | 212 | // passing a NULL seat means we just pick the default seat |
@@ -225,7 +230,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
225 | for (; isspace(*head); ++head) {} | 230 | for (; isspace(*head); ++head) {} |
226 | // Extract criteria (valid for this command list only). | 231 | // Extract criteria (valid for this command list only). |
227 | if (matched_delim == ';') { | 232 | if (matched_delim == ';') { |
228 | config->handler_context.using_criteria = false; | 233 | using_criteria = false; |
229 | if (*head == '[') { | 234 | if (*head == '[') { |
230 | char *error = NULL; | 235 | char *error = NULL; |
231 | struct criteria *criteria = criteria_parse(head, &error); | 236 | struct criteria *criteria = criteria_parse(head, &error); |
@@ -239,7 +244,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
239 | containers = criteria_get_containers(criteria); | 244 | containers = criteria_get_containers(criteria); |
240 | head += strlen(criteria->raw); | 245 | head += strlen(criteria->raw); |
241 | criteria_destroy(criteria); | 246 | criteria_destroy(criteria); |
242 | config->handler_context.using_criteria = true; | 247 | using_criteria = true; |
243 | // Skip leading whitespace | 248 | // Skip leading whitespace |
244 | for (; isspace(*head); ++head) {} | 249 | for (; isspace(*head); ++head) {} |
245 | } | 250 | } |
@@ -265,7 +270,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
265 | } | 270 | } |
266 | } | 271 | } |
267 | } | 272 | } |
268 | struct cmd_handler *handler = find_core_handler(argv[0]); | 273 | const struct cmd_handler *handler = find_core_handler(argv[0]); |
269 | if (!handler) { | 274 | if (!handler) { |
270 | list_add(res_list, cmd_results_new(CMD_INVALID, | 275 | list_add(res_list, cmd_results_new(CMD_INVALID, |
271 | "Unknown/invalid command '%s'", argv[0])); | 276 | "Unknown/invalid command '%s'", argv[0])); |
@@ -278,11 +283,14 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
278 | argv[i] = do_var_replacement(argv[i]); | 283 | argv[i] = do_var_replacement(argv[i]); |
279 | } | 284 | } |
280 | 285 | ||
281 | if (!config->handler_context.using_criteria) { | 286 | |
282 | // The container or workspace which this command will run on. | 287 | if (!using_criteria) { |
283 | struct sway_node *node = con ? &con->node : | 288 | if (con) { |
284 | seat_get_focus_inactive(seat, &root->node); | 289 | set_config_node(&con->node, true); |
285 | set_config_node(node); | 290 | } else { |
291 | set_config_node(seat_get_focus_inactive(seat, &root->node), | ||
292 | false); | ||
293 | } | ||
286 | struct cmd_results *res = handler->handle(argc-1, argv+1); | 294 | struct cmd_results *res = handler->handle(argc-1, argv+1); |
287 | list_add(res_list, res); | 295 | list_add(res_list, res); |
288 | if (res->status == CMD_INVALID) { | 296 | if (res->status == CMD_INVALID) { |
@@ -296,7 +304,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
296 | struct cmd_results *fail_res = NULL; | 304 | struct cmd_results *fail_res = NULL; |
297 | for (int i = 0; i < containers->length; ++i) { | 305 | for (int i = 0; i < containers->length; ++i) { |
298 | struct sway_container *container = containers->items[i]; | 306 | struct sway_container *container = containers->items[i]; |
299 | set_config_node(&container->node); | 307 | set_config_node(&container->node, true); |
300 | struct cmd_results *res = handler->handle(argc-1, argv+1); | 308 | struct cmd_results *res = handler->handle(argc-1, argv+1); |
301 | if (res->status == CMD_SUCCESS) { | 309 | if (res->status == CMD_SUCCESS) { |
302 | free_cmd_results(res); | 310 | free_cmd_results(res); |
@@ -370,12 +378,15 @@ struct cmd_results *config_command(char *exec, char **new_block) { | |||
370 | 378 | ||
371 | // Determine the command handler | 379 | // Determine the command handler |
372 | sway_log(SWAY_INFO, "Config command: %s", exec); | 380 | sway_log(SWAY_INFO, "Config command: %s", exec); |
373 | struct cmd_handler *handler = find_core_handler(argv[0]); | 381 | const struct cmd_handler *handler = find_core_handler(argv[0]); |
374 | if (!handler || !handler->handle) { | 382 | if (!handler || !handler->handle) { |
375 | const char *error = handler | 383 | if (handler) { |
376 | ? "Command '%s' is shimmed, but unimplemented" | 384 | results = cmd_results_new(CMD_INVALID, |
377 | : "Unknown/invalid command '%s'"; | 385 | "Command '%s' is shimmed, but unimplemented", argv[0]); |
378 | 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 | } | ||
379 | goto cleanup; | 390 | goto cleanup; |
380 | } | 391 | } |
381 | 392 | ||
@@ -401,6 +412,7 @@ struct cmd_results *config_command(char *exec, char **new_block) { | |||
401 | && handler->handle != cmd_bindsym | 412 | && handler->handle != cmd_bindsym |
402 | && handler->handle != cmd_bindcode | 413 | && handler->handle != cmd_bindcode |
403 | && handler->handle != cmd_bindswitch | 414 | && handler->handle != cmd_bindswitch |
415 | && handler->handle != cmd_bindgesture | ||
404 | && handler->handle != cmd_set | 416 | && handler->handle != cmd_set |
405 | && handler->handle != cmd_for_window | 417 | && handler->handle != cmd_for_window |
406 | && (*argv[i] == '\"' || *argv[i] == '\'')) { | 418 | && (*argv[i] == '\"' || *argv[i] == '\'')) { |
@@ -418,12 +430,12 @@ cleanup: | |||
418 | } | 430 | } |
419 | 431 | ||
420 | struct cmd_results *config_subcommand(char **argv, int argc, | 432 | struct cmd_results *config_subcommand(char **argv, int argc, |
421 | struct cmd_handler *handlers, size_t handlers_size) { | 433 | const struct cmd_handler *handlers, size_t handlers_size) { |
422 | char *command = join_args(argv, argc); | 434 | char *command = join_args(argv, argc); |
423 | sway_log(SWAY_DEBUG, "Subcommand: %s", command); | 435 | sway_log(SWAY_DEBUG, "Subcommand: %s", command); |
424 | free(command); | 436 | free(command); |
425 | 437 | ||
426 | struct cmd_handler *handler = find_handler(argv[0], handlers, | 438 | const struct cmd_handler *handler = find_handler(argv[0], handlers, |
427 | handlers_size); | 439 | handlers_size); |
428 | if (!handler) { | 440 | if (!handler) { |
429 | return cmd_results_new(CMD_INVALID, | 441 | return cmd_results_new(CMD_INVALID, |
@@ -453,41 +465,13 @@ struct cmd_results *config_commands_command(char *exec) { | |||
453 | goto cleanup; | 465 | goto cleanup; |
454 | } | 466 | } |
455 | 467 | ||
456 | struct cmd_handler *handler = find_handler(cmd, NULL, 0); | 468 | const struct cmd_handler *handler = find_handler(cmd, NULL, 0); |
457 | if (!handler && strcmp(cmd, "*") != 0) { | 469 | if (!handler && strcmp(cmd, "*") != 0) { |
458 | results = cmd_results_new(CMD_INVALID, | 470 | results = cmd_results_new(CMD_INVALID, |
459 | "Unknown/invalid command '%s'", cmd); | 471 | "Unknown/invalid command '%s'", cmd); |
460 | goto cleanup; | 472 | goto cleanup; |
461 | } | 473 | } |
462 | 474 | ||
463 | enum command_context context = 0; | ||
464 | |||
465 | struct { | ||
466 | char *name; | ||
467 | enum command_context context; | ||
468 | } context_names[] = { | ||
469 | { "config", CONTEXT_CONFIG }, | ||
470 | { "binding", CONTEXT_BINDING }, | ||
471 | { "ipc", CONTEXT_IPC }, | ||
472 | { "criteria", CONTEXT_CRITERIA }, | ||
473 | { "all", CONTEXT_ALL }, | ||
474 | }; | ||
475 | |||
476 | for (int i = 1; i < argc; ++i) { | ||
477 | size_t j; | ||
478 | for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) { | ||
479 | if (strcmp(context_names[j].name, argv[i]) == 0) { | ||
480 | break; | ||
481 | } | ||
482 | } | ||
483 | if (j == sizeof(context_names) / sizeof(context_names[0])) { | ||
484 | results = cmd_results_new(CMD_INVALID, | ||
485 | "Invalid command context %s", argv[i]); | ||
486 | goto cleanup; | ||
487 | } | ||
488 | context |= context_names[j].context; | ||
489 | } | ||
490 | |||
491 | results = cmd_results_new(CMD_SUCCESS, NULL); | 475 | results = cmd_results_new(CMD_SUCCESS, NULL); |
492 | 476 | ||
493 | cleanup: | 477 | cleanup: |
@@ -504,14 +488,10 @@ struct cmd_results *cmd_results_new(enum cmd_status status, | |||
504 | } | 488 | } |
505 | results->status = status; | 489 | results->status = status; |
506 | if (format) { | 490 | if (format) { |
507 | char *error = malloc(256); | ||
508 | va_list args; | 491 | va_list args; |
509 | va_start(args, format); | 492 | va_start(args, format); |
510 | if (error) { | 493 | results->error = vformat_str(format, args); |
511 | vsnprintf(error, 256, format, args); | ||
512 | } | ||
513 | va_end(args); | 494 | va_end(args); |
514 | results->error = error; | ||
515 | } else { | 495 | } else { |
516 | results->error = NULL; | 496 | results->error = NULL; |
517 | } | 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 d42b7fc2..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> |
@@ -8,7 +7,7 @@ | |||
8 | #include "log.h" | 7 | #include "log.h" |
9 | 8 | ||
10 | // Must be in alphabetical order for bsearch | 9 | // Must be in alphabetical order for bsearch |
11 | static struct cmd_handler bar_handlers[] = { | 10 | static const struct cmd_handler bar_handlers[] = { |
12 | { "bindcode", bar_cmd_bindcode }, | 11 | { "bindcode", bar_cmd_bindcode }, |
13 | { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, | 12 | { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, |
14 | { "bindsym", bar_cmd_bindsym }, | 13 | { "bindsym", bar_cmd_bindsym }, |
@@ -41,7 +40,7 @@ static struct cmd_handler bar_handlers[] = { | |||
41 | }; | 40 | }; |
42 | 41 | ||
43 | // Must be in alphabetical order for bsearch | 42 | // Must be in alphabetical order for bsearch |
44 | static struct cmd_handler bar_config_handlers[] = { | 43 | static const struct cmd_handler bar_config_handlers[] = { |
45 | { "id", bar_cmd_id }, | 44 | { "id", bar_cmd_id }, |
46 | { "swaybar_command", bar_cmd_swaybar_command }, | 45 | { "swaybar_command", bar_cmd_swaybar_command }, |
47 | }; | 46 | }; |
@@ -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])) { |
@@ -116,6 +113,7 @@ struct cmd_results *cmd_bar(int argc, char **argv) { | |||
116 | if (res && res->status != CMD_SUCCESS) { | 113 | if (res && res->status != CMD_SUCCESS) { |
117 | if (id) { | 114 | if (id) { |
118 | free_bar_config(config->current_bar); | 115 | free_bar_config(config->current_bar); |
116 | config->current_bar = NULL; | ||
119 | id = NULL; | 117 | id = NULL; |
120 | } | 118 | } |
121 | return res; | 119 | return res; |
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/colors.c b/sway/commands/bar/colors.c index 2d5b22bf..275fa3c6 100644 --- a/sway/commands/bar/colors.c +++ b/sway/commands/bar/colors.c | |||
@@ -4,7 +4,7 @@ | |||
4 | #include "util.h" | 4 | #include "util.h" |
5 | 5 | ||
6 | // Must be in alphabetical order for bsearch | 6 | // Must be in alphabetical order for bsearch |
7 | static struct cmd_handler bar_colors_handlers[] = { | 7 | static const struct cmd_handler bar_colors_handlers[] = { |
8 | { "active_workspace", bar_colors_cmd_active_workspace }, | 8 | { "active_workspace", bar_colors_cmd_active_workspace }, |
9 | { "background", bar_colors_cmd_background }, | 9 | { "background", bar_colors_cmd_background }, |
10 | { "binding_mode", bar_colors_cmd_binding_mode }, | 10 | { "binding_mode", bar_colors_cmd_binding_mode }, |
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index 62987f3e..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" |
@@ -11,7 +10,20 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) { | |||
11 | } | 10 | } |
12 | char *font = join_args(argv, argc); | 11 | char *font = join_args(argv, argc); |
13 | free(config->current_bar->font); | 12 | free(config->current_bar->font); |
14 | config->current_bar->font = font; | 13 | |
14 | if (strncmp(font, "pango:", 6) == 0) { | ||
15 | if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) { | ||
16 | config->current_bar->pango_markup = true; | ||
17 | } | ||
18 | config->current_bar->font = strdup(font + 6); | ||
19 | } else { | ||
20 | if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) { | ||
21 | config->current_bar->pango_markup = false; | ||
22 | } | ||
23 | config->current_bar->font = strdup(font); | ||
24 | } | ||
25 | |||
26 | free(font); | ||
15 | sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s", | 27 | sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s", |
16 | config->current_bar->font, config->current_bar->id); | 28 | config->current_bar->font, config->current_bar->id); |
17 | return cmd_results_new(CMD_SUCCESS, NULL); | 29 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c index 1f08a5d2..7b38831e 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -54,7 +53,7 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { | |||
54 | } | 53 | } |
55 | 54 | ||
56 | const char *state = argv[0]; | 55 | const char *state = argv[0]; |
57 | if (config->reading) { | 56 | if (config->current_bar) { |
58 | error = bar_set_hidden_state(config->current_bar, state); | 57 | error = bar_set_hidden_state(config->current_bar, state); |
59 | } else { | 58 | } else { |
60 | const char *id = argc == 2 ? argv[1] : NULL; | 59 | const char *id = argc == 2 ? argv[1] : NULL; |
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c index 6ac07843..fee21709 100644 --- a/sway/commands/bar/icon_theme.c +++ b/sway/commands/bar/icon_theme.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "config.h" | 2 | #include "config.h" |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c index a9a61743..46cf4ca9 100644 --- a/sway/commands/bar/id.c +++ b/sway/commands/bar/id.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 8b3fb275..d69e910b 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -58,7 +57,7 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) { | |||
58 | } | 57 | } |
59 | 58 | ||
60 | const char *mode = argv[0]; | 59 | const char *mode = argv[0]; |
61 | if (config->reading) { | 60 | if (config->current_bar) { |
62 | error = bar_set_mode(config->current_bar, mode); | 61 | error = bar_set_mode(config->current_bar, mode); |
63 | } else { | 62 | } else { |
64 | const char *id = argc == 2 ? argv[1] : NULL; | 63 | const char *id = argc == 2 ? argv[1] : NULL; |
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c index cac1d056..51730176 100644 --- a/sway/commands/bar/output.c +++ b/sway/commands/bar/output.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c index b207de0b..94f530ec 100644 --- a/sway/commands/bar/position.c +++ b/sway/commands/bar/position.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c index 6737d4d2..50e9a873 100644 --- a/sway/commands/bar/separator_symbol.c +++ b/sway/commands/bar/separator_symbol.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/bar/tray_bind.c b/sway/commands/bar/tray_bind.c index 243834ba..3dc9bc4c 100644 --- a/sway/commands/bar/tray_bind.c +++ b/sway/commands/bar/tray_bind.c | |||
@@ -26,7 +26,7 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) { | |||
26 | } | 26 | } |
27 | if (message) { | 27 | if (message) { |
28 | free(binding); | 28 | free(binding); |
29 | error = cmd_results_new(CMD_INVALID, message); | 29 | error = cmd_results_new(CMD_INVALID, "%s", message); |
30 | free(message); | 30 | free(message); |
31 | return error; | 31 | return error; |
32 | } else if (!binding->button) { | 32 | } else if (!binding->button) { |
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c index eb3b486e..679facf7 100644 --- a/sway/commands/bar/tray_output.c +++ b/sway/commands/bar/tray_output.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "config.h" | 2 | #include "config.h" |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index f6e58d99..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> |
@@ -8,6 +7,7 @@ | |||
8 | #include <wlr/types/wlr_cursor.h> | 7 | #include <wlr/types/wlr_cursor.h> |
9 | #include "sway/commands.h" | 8 | #include "sway/commands.h" |
10 | #include "sway/config.h" | 9 | #include "sway/config.h" |
10 | #include "sway/desktop/transaction.h" | ||
11 | #include "sway/input/cursor.h" | 11 | #include "sway/input/cursor.h" |
12 | #include "sway/input/keyboard.h" | 12 | #include "sway/input/keyboard.h" |
13 | #include "sway/ipc-server.h" | 13 | #include "sway/ipc-server.h" |
@@ -46,7 +46,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a, | |||
46 | if (binding_a->type != binding_b->type) { | 46 | if (binding_a->type != binding_b->type) { |
47 | return false; | 47 | return false; |
48 | } | 48 | } |
49 | if (binding_a->state != binding_b->state) { | 49 | if (binding_a->trigger != binding_b->trigger) { |
50 | return false; | 50 | return false; |
51 | } | 51 | } |
52 | if ((binding_a->flags & BINDING_LOCKED) != | 52 | if ((binding_a->flags & BINDING_LOCKED) != |
@@ -126,7 +126,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, | |||
126 | if (!button) { | 126 | if (!button) { |
127 | if (message) { | 127 | if (message) { |
128 | struct cmd_results *error = | 128 | struct cmd_results *error = |
129 | cmd_results_new(CMD_INVALID, message); | 129 | cmd_results_new(CMD_INVALID, "%s", message); |
130 | free(message); | 130 | free(message); |
131 | return error; | 131 | return error; |
132 | } else { | 132 | } else { |
@@ -142,7 +142,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, | |||
142 | if (!button) { | 142 | if (!button) { |
143 | if (message) { | 143 | if (message) { |
144 | struct cmd_results *error = | 144 | struct cmd_results *error = |
145 | cmd_results_new(CMD_INVALID, message); | 145 | cmd_results_new(CMD_INVALID, "%s", message); |
146 | free(message); | 146 | free(message); |
147 | return error; | 147 | return error; |
148 | } else { | 148 | } else { |
@@ -181,7 +181,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, | |||
181 | uint32_t button = get_mouse_bindsym(name, &message); | 181 | uint32_t button = get_mouse_bindsym(name, &message); |
182 | if (message) { | 182 | if (message) { |
183 | struct cmd_results *error = | 183 | struct cmd_results *error = |
184 | cmd_results_new(CMD_INVALID, message); | 184 | cmd_results_new(CMD_INVALID, "%s", message); |
185 | free(message); | 185 | free(message); |
186 | return error; | 186 | return error; |
187 | } else if (button) { | 187 | } else if (button) { |
@@ -371,6 +371,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | |||
371 | strlen("--input-device=")) == 0) { | 371 | strlen("--input-device=")) == 0) { |
372 | free(binding->input); | 372 | free(binding->input); |
373 | binding->input = strdup(argv[0] + strlen("--input-device=")); | 373 | binding->input = strdup(argv[0] + strlen("--input-device=")); |
374 | strip_quotes(binding->input); | ||
374 | } else if (strcmp("--no-warn", argv[0]) == 0) { | 375 | } else if (strcmp("--no-warn", argv[0]) == 0) { |
375 | warn = false; | 376 | warn = false; |
376 | } else if (strcmp("--no-repeat", argv[0]) == 0) { | 377 | } else if (strcmp("--no-repeat", argv[0]) == 0) { |
@@ -537,7 +538,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, | |||
537 | free_switch_binding(binding); | 538 | free_switch_binding(binding); |
538 | return cmd_results_new(CMD_FAILURE, | 539 | return cmd_results_new(CMD_FAILURE, |
539 | "Invalid %s command (expected binding with the form " | 540 | "Invalid %s command (expected binding with the form " |
540 | "<switch>:<state>)", bindtype, argc); | 541 | "<switch>:<state>)", bindtype); |
541 | } | 542 | } |
542 | if (strcmp(split->items[0], "tablet") == 0) { | 543 | if (strcmp(split->items[0], "tablet") == 0) { |
543 | binding->type = WLR_SWITCH_TYPE_TABLET_MODE; | 544 | binding->type = WLR_SWITCH_TYPE_TABLET_MODE; |
@@ -547,20 +548,21 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, | |||
547 | free_switch_binding(binding); | 548 | free_switch_binding(binding); |
548 | return cmd_results_new(CMD_FAILURE, | 549 | return cmd_results_new(CMD_FAILURE, |
549 | "Invalid %s command (expected switch binding: " | 550 | "Invalid %s command (expected switch binding: " |
550 | "unknown switch %s)", bindtype, split->items[0]); | 551 | "unknown switch %s)", bindtype, |
552 | (const char *)split->items[0]); | ||
551 | } | 553 | } |
552 | if (strcmp(split->items[1], "on") == 0) { | 554 | if (strcmp(split->items[1], "on") == 0) { |
553 | binding->state = WLR_SWITCH_STATE_ON; | 555 | binding->trigger = SWAY_SWITCH_TRIGGER_ON; |
554 | } else if (strcmp(split->items[1], "off") == 0) { | 556 | } else if (strcmp(split->items[1], "off") == 0) { |
555 | binding->state = WLR_SWITCH_STATE_OFF; | 557 | binding->trigger = SWAY_SWITCH_TRIGGER_OFF; |
556 | } else if (strcmp(split->items[1], "toggle") == 0) { | 558 | } else if (strcmp(split->items[1], "toggle") == 0) { |
557 | binding->state = WLR_SWITCH_STATE_TOGGLE; | 559 | binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE; |
558 | } else { | 560 | } else { |
559 | free_switch_binding(binding); | 561 | free_switch_binding(binding); |
560 | return cmd_results_new(CMD_FAILURE, | 562 | return cmd_results_new(CMD_FAILURE, |
561 | "Invalid %s command " | 563 | "Invalid %s command " |
562 | "(expected switch state: unknown state %d)", | 564 | "(expected switch state: unknown state %s)", |
563 | bindtype, split->items[0]); | 565 | bindtype, (const char *)split->items[1]); |
564 | } | 566 | } |
565 | list_free_items_and_destroy(split); | 567 | list_free_items_and_destroy(split); |
566 | 568 | ||
@@ -642,6 +644,8 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) | |||
642 | if (success) { | 644 | if (success) { |
643 | ipc_event_binding(binding); | 645 | ipc_event_binding(binding); |
644 | } | 646 | } |
647 | |||
648 | transaction_commit_dirty(); | ||
645 | } | 649 | } |
646 | 650 | ||
647 | /** | 651 | /** |
diff --git a/sway/commands/border.c b/sway/commands/border.c index 647663ac..7818fc96 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c | |||
@@ -19,11 +19,11 @@ static void set_border(struct sway_container *con, | |||
19 | view_set_csd_from_server(con->view, false); | 19 | view_set_csd_from_server(con->view, false); |
20 | } else if (!con->view->using_csd && new_border == B_CSD) { | 20 | } else if (!con->view->using_csd && new_border == B_CSD) { |
21 | view_set_csd_from_server(con->view, true); | 21 | view_set_csd_from_server(con->view, true); |
22 | con->saved_border = con->border; | 22 | con->saved_border = con->pending.border; |
23 | } | 23 | } |
24 | } | 24 | } |
25 | if (new_border != B_CSD || container_is_floating(con)) { | 25 | if (new_border != B_CSD || container_is_floating(con)) { |
26 | con->border = new_border; | 26 | con->pending.border = new_border; |
27 | } | 27 | } |
28 | if (con->view) { | 28 | if (con->view) { |
29 | con->view->using_csd = new_border == B_CSD; | 29 | con->view->using_csd = new_border == B_CSD; |
@@ -35,7 +35,7 @@ static void border_toggle(struct sway_container *con) { | |||
35 | set_border(con, B_NONE); | 35 | set_border(con, B_NONE); |
36 | return; | 36 | return; |
37 | } | 37 | } |
38 | switch (con->border) { | 38 | switch (con->pending.border) { |
39 | case B_NONE: | 39 | case B_NONE: |
40 | set_border(con, B_PIXEL); | 40 | set_border(con, B_PIXEL); |
41 | break; | 41 | break; |
@@ -88,7 +88,7 @@ struct cmd_results *cmd_border(int argc, char **argv) { | |||
88 | "or 'border pixel <px>'"); | 88 | "or 'border pixel <px>'"); |
89 | } | 89 | } |
90 | if (argc == 2) { | 90 | if (argc == 2) { |
91 | container->border_thickness = atoi(argv[1]); | 91 | container->pending.border_thickness = atoi(argv[1]); |
92 | } | 92 | } |
93 | 93 | ||
94 | if (container_is_floating(container)) { | 94 | if (container_is_floating(container)) { |
diff --git a/sway/commands/client.c b/sway/commands/client.c index dd0694df..fd2ac7a8 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c | |||
@@ -5,9 +5,8 @@ | |||
5 | #include "sway/tree/container.h" | 5 | #include "sway/tree/container.h" |
6 | #include "util.h" | 6 | #include "util.h" |
7 | 7 | ||
8 | static void rebuild_textures_iterator(struct sway_container *con, void *data) { | 8 | static void container_update_iterator(struct sway_container *con, void *data) { |
9 | container_update_marks_textures(con); | 9 | container_update(con); |
10 | container_update_title_textures(con); | ||
11 | } | 10 | } |
12 | 11 | ||
13 | static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | 12 | static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, |
@@ -18,6 +17,12 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | |||
18 | return error; | 17 | return error; |
19 | } | 18 | } |
20 | 19 | ||
20 | if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) { | ||
21 | sway_log(SWAY_ERROR, | ||
22 | "Warning: indicator and child_border colors have no effect for %s", | ||
23 | cmd_name); | ||
24 | } | ||
25 | |||
21 | struct border_colors colors = {0}; | 26 | struct border_colors colors = {0}; |
22 | const char *ind_hex = argc > 3 ? argv[3] : default_indicator; | 27 | const char *ind_hex = argc > 3 ? argv[3] : default_indicator; |
23 | const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background | 28 | const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background |
@@ -45,12 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | |||
45 | memcpy(class, &colors, sizeof(struct border_colors)); | 50 | memcpy(class, &colors, sizeof(struct border_colors)); |
46 | 51 | ||
47 | if (config->active) { | 52 | if (config->active) { |
48 | root_for_each_container(rebuild_textures_iterator, NULL); | 53 | root_for_each_container(container_update_iterator, NULL); |
49 | |||
50 | for (int i = 0; i < root->outputs->length; ++i) { | ||
51 | struct sway_output *output = root->outputs->items[i]; | ||
52 | output_damage_whole(output); | ||
53 | } | ||
54 | } | 54 | } |
55 | 55 | ||
56 | return cmd_results_new(CMD_SUCCESS, NULL); | 56 | return cmd_results_new(CMD_SUCCESS, NULL); |
@@ -80,3 +80,13 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) { | |||
80 | sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); | 80 | sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); |
81 | return cmd_results_new(CMD_SUCCESS, NULL); | 81 | return cmd_results_new(CMD_SUCCESS, NULL); |
82 | } | 82 | } |
83 | |||
84 | struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) { | ||
85 | struct cmd_results *result = handle_command(argc, argv, | ||
86 | "client.focused_tab_title", | ||
87 | &config->border_colors.focused_tab_title, "#2e9ef4ff"); | ||
88 | if (result && result->status == CMD_SUCCESS) { | ||
89 | config->has_focused_tab_title = true; | ||
90 | } | ||
91 | return result; | ||
92 | } | ||
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 39e48a44..8bc1048c 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <stdint.h> | 2 | #include <stdint.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -7,6 +6,8 @@ | |||
7 | #include <signal.h> | 6 | #include <signal.h> |
8 | #include "sway/commands.h" | 7 | #include "sway/commands.h" |
9 | #include "sway/config.h" | 8 | #include "sway/config.h" |
9 | #include "sway/server.h" | ||
10 | #include "sway/desktop/launcher.h" | ||
10 | #include "sway/tree/container.h" | 11 | #include "sway/tree/container.h" |
11 | #include "sway/tree/root.h" | 12 | #include "sway/tree/root.h" |
12 | #include "sway/tree/workspace.h" | 13 | #include "sway/tree/workspace.h" |
@@ -24,11 +25,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) { | |||
24 | return error; | 25 | return error; |
25 | } | 26 | } |
26 | 27 | ||
28 | static void export_xdga_token(struct launcher_ctx *ctx) { | ||
29 | const char *token = launcher_ctx_get_token_name(ctx); | ||
30 | setenv("XDG_ACTIVATION_TOKEN", token, 1); | ||
31 | } | ||
32 | |||
33 | static void export_startup_id(struct launcher_ctx *ctx) { | ||
34 | const char *token = launcher_ctx_get_token_name(ctx); | ||
35 | setenv("DESKTOP_STARTUP_ID", token, 1); | ||
36 | } | ||
37 | |||
27 | struct cmd_results *cmd_exec_process(int argc, char **argv) { | 38 | struct cmd_results *cmd_exec_process(int argc, char **argv) { |
28 | struct cmd_results *error = NULL; | 39 | struct cmd_results *error = NULL; |
29 | char *tmp = NULL; | 40 | char *cmd = NULL; |
41 | bool no_startup_id = false; | ||
30 | if (strcmp(argv[0], "--no-startup-id") == 0) { | 42 | if (strcmp(argv[0], "--no-startup-id") == 0) { |
31 | sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); | 43 | no_startup_id = true; |
32 | --argc; ++argv; | 44 | --argc; ++argv; |
33 | if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { | 45 | if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { |
34 | return error; | 46 | return error; |
@@ -36,17 +48,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
36 | } | 48 | } |
37 | 49 | ||
38 | if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { | 50 | if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { |
39 | tmp = strdup(argv[0]); | 51 | cmd = strdup(argv[0]); |
40 | strip_quotes(tmp); | 52 | strip_quotes(cmd); |
41 | } else { | 53 | } else { |
42 | tmp = join_args(argv, argc); | 54 | cmd = join_args(argv, argc); |
43 | } | 55 | } |
44 | 56 | ||
45 | // Put argument into cmd array | ||
46 | char cmd[4096]; | ||
47 | strncpy(cmd, tmp, sizeof(cmd) - 1); | ||
48 | cmd[sizeof(cmd) - 1] = 0; | ||
49 | free(tmp); | ||
50 | sway_log(SWAY_DEBUG, "Executing %s", cmd); | 57 | sway_log(SWAY_DEBUG, "Executing %s", cmd); |
51 | 58 | ||
52 | int fd[2]; | 59 | int fd[2]; |
@@ -55,18 +62,28 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
55 | } | 62 | } |
56 | 63 | ||
57 | pid_t pid, child; | 64 | pid_t pid, child; |
65 | struct launcher_ctx *ctx = launcher_ctx_create_internal(); | ||
58 | // Fork process | 66 | // Fork process |
59 | if ((pid = fork()) == 0) { | 67 | if ((pid = fork()) == 0) { |
60 | // Fork child process again | 68 | // Fork child process again |
69 | restore_nofile_limit(); | ||
61 | setsid(); | 70 | setsid(); |
62 | sigset_t set; | 71 | sigset_t set; |
63 | sigemptyset(&set); | 72 | sigemptyset(&set); |
64 | sigprocmask(SIG_SETMASK, &set, NULL); | 73 | sigprocmask(SIG_SETMASK, &set, NULL); |
74 | signal(SIGPIPE, SIG_DFL); | ||
65 | close(fd[0]); | 75 | close(fd[0]); |
66 | if ((child = fork()) == 0) { | 76 | if ((child = fork()) == 0) { |
67 | close(fd[1]); | 77 | close(fd[1]); |
68 | execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); | 78 | if (ctx) { |
69 | _exit(0); | 79 | export_xdga_token(ctx); |
80 | } | ||
81 | if (ctx && !no_startup_id) { | ||
82 | export_startup_id(ctx); | ||
83 | } | ||
84 | execlp("sh", "sh", "-c", cmd, (void *)NULL); | ||
85 | sway_log_errno(SWAY_ERROR, "execlp failed"); | ||
86 | _exit(1); | ||
70 | } | 87 | } |
71 | ssize_t s = 0; | 88 | ssize_t s = 0; |
72 | while ((size_t)s < sizeof(pid_t)) { | 89 | while ((size_t)s < sizeof(pid_t)) { |
@@ -75,10 +92,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
75 | close(fd[1]); | 92 | close(fd[1]); |
76 | _exit(0); // Close child process | 93 | _exit(0); // Close child process |
77 | } else if (pid < 0) { | 94 | } else if (pid < 0) { |
95 | free(cmd); | ||
78 | close(fd[0]); | 96 | close(fd[0]); |
79 | close(fd[1]); | 97 | close(fd[1]); |
80 | return cmd_results_new(CMD_FAILURE, "fork() failed"); | 98 | return cmd_results_new(CMD_FAILURE, "fork() failed"); |
81 | } | 99 | } |
100 | free(cmd); | ||
82 | close(fd[1]); // close write | 101 | close(fd[1]); // close write |
83 | ssize_t s = 0; | 102 | ssize_t s = 0; |
84 | while ((size_t)s < sizeof(pid_t)) { | 103 | while ((size_t)s < sizeof(pid_t)) { |
@@ -89,8 +108,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
89 | waitpid(pid, NULL, 0); | 108 | waitpid(pid, NULL, 0); |
90 | if (child > 0) { | 109 | if (child > 0) { |
91 | sway_log(SWAY_DEBUG, "Child process created with pid %d", child); | 110 | sway_log(SWAY_DEBUG, "Child process created with pid %d", child); |
92 | 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 | } | ||
93 | } else { | 115 | } else { |
116 | launcher_ctx_destroy(ctx); | ||
94 | return cmd_results_new(CMD_FAILURE, "Second fork() failed"); | 117 | return cmd_results_new(CMD_FAILURE, "Second fork() failed"); |
95 | } | 118 | } |
96 | 119 | ||
diff --git a/sway/commands/floating.c b/sway/commands/floating.c index ce123345..74f6522c 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c | |||
@@ -40,8 +40,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) { | |||
40 | // If the container is in a floating split container, | 40 | // If the container is in a floating split container, |
41 | // operate on the split container instead of the child. | 41 | // operate on the split container instead of the child. |
42 | if (container_is_floating_or_child(container)) { | 42 | if (container_is_floating_or_child(container)) { |
43 | while (container->parent) { | 43 | while (container->pending.parent) { |
44 | container = container->parent; | 44 | container = container->pending.parent; |
45 | } | 45 | } |
46 | } | 46 | } |
47 | 47 | ||
@@ -51,8 +51,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) { | |||
51 | container_set_floating(container, wants_floating); | 51 | container_set_floating(container, wants_floating); |
52 | 52 | ||
53 | // Floating containers in the scratchpad should be ignored | 53 | // Floating containers in the scratchpad should be ignored |
54 | if (container->workspace) { | 54 | if (container->pending.workspace) { |
55 | arrange_workspace(container->workspace); | 55 | arrange_workspace(container->pending.workspace); |
56 | } | 56 | } |
57 | 57 | ||
58 | return cmd_results_new(CMD_SUCCESS, NULL); | 58 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c index 3a1d606a..e8c24ace 100644 --- a/sway/commands/floating_minmax_size.c +++ b/sway/commands/floating_minmax_size.c | |||
@@ -23,16 +23,16 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | |||
23 | char *err; | 23 | char *err; |
24 | int width = (int)strtol(argv[0], &err, 10); | 24 | int width = (int)strtol(argv[0], &err, 10); |
25 | if (*err) { | 25 | if (*err) { |
26 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | 26 | return cmd_results_new(CMD_INVALID, "%s", usage); |
27 | } | 27 | } |
28 | 28 | ||
29 | if (strcmp(argv[1], "x") != 0) { | 29 | if (strcmp(argv[1], "x") != 0) { |
30 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | 30 | return cmd_results_new(CMD_INVALID, "%s", usage); |
31 | } | 31 | } |
32 | 32 | ||
33 | int height = (int)strtol(argv[2], &err, 10); | 33 | int height = (int)strtol(argv[2], &err, 10); |
34 | if (*err) { | 34 | if (*err) { |
35 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | 35 | return cmd_results_new(CMD_INVALID, "%s", usage); |
36 | } | 36 | } |
37 | 37 | ||
38 | *config_width = width; | 38 | *config_width = width; |
diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 79b7aed5..facd82de 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c | |||
@@ -54,7 +54,7 @@ static bool get_direction_from_next_prev(struct sway_container *container, | |||
54 | } else { | 54 | } else { |
55 | return false; | 55 | return false; |
56 | } | 56 | } |
57 | 57 | ||
58 | return true; | 58 | return true; |
59 | } | 59 | } |
60 | 60 | ||
@@ -141,9 +141,9 @@ static struct sway_node *node_get_in_direction_tiling( | |||
141 | struct sway_container *wrap_candidate = NULL; | 141 | struct sway_container *wrap_candidate = NULL; |
142 | struct sway_container *current = container; | 142 | struct sway_container *current = container; |
143 | while (current) { | 143 | while (current) { |
144 | if (current->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 144 | if (current->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
145 | // Fullscreen container with a direction - go straight to outputs | 145 | // Fullscreen container with a direction - go straight to outputs |
146 | struct sway_output *output = current->workspace->output; | 146 | struct sway_output *output = current->pending.workspace->output; |
147 | struct sway_output *new_output = | 147 | struct sway_output *new_output = |
148 | output_get_in_direction(output, dir); | 148 | output_get_in_direction(output, dir); |
149 | if (!new_output) { | 149 | if (!new_output) { |
@@ -151,7 +151,7 @@ static struct sway_node *node_get_in_direction_tiling( | |||
151 | } | 151 | } |
152 | return get_node_in_output_direction(new_output, dir); | 152 | return get_node_in_output_direction(new_output, dir); |
153 | } | 153 | } |
154 | if (current->fullscreen_mode == FULLSCREEN_GLOBAL) { | 154 | if (current->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
155 | return NULL; | 155 | return NULL; |
156 | } | 156 | } |
157 | 157 | ||
@@ -202,11 +202,11 @@ static struct sway_node *node_get_in_direction_tiling( | |||
202 | } | 202 | } |
203 | } | 203 | } |
204 | 204 | ||
205 | current = current->parent; | 205 | current = current->pending.parent; |
206 | } | 206 | } |
207 | 207 | ||
208 | // Check a different output | 208 | // Check a different output |
209 | struct sway_output *output = container->workspace->output; | 209 | struct sway_output *output = container->pending.workspace->output; |
210 | struct sway_output *new_output = output_get_in_direction(output, dir); | 210 | struct sway_output *new_output = output_get_in_direction(output, dir); |
211 | if ((config->focus_wrapping != WRAP_WORKSPACE || | 211 | if ((config->focus_wrapping != WRAP_WORKSPACE || |
212 | container->node.type == N_WORKSPACE) && new_output) { | 212 | container->node.type == N_WORKSPACE) && new_output) { |
@@ -226,23 +226,23 @@ static struct sway_node *node_get_in_direction_tiling( | |||
226 | static struct sway_node *node_get_in_direction_floating( | 226 | static struct sway_node *node_get_in_direction_floating( |
227 | struct sway_container *con, struct sway_seat *seat, | 227 | struct sway_container *con, struct sway_seat *seat, |
228 | enum wlr_direction dir) { | 228 | enum wlr_direction dir) { |
229 | double ref_lx = con->x + con->width / 2; | 229 | double ref_lx = con->pending.x + con->pending.width / 2; |
230 | double ref_ly = con->y + con->height / 2; | 230 | double ref_ly = con->pending.y + con->pending.height / 2; |
231 | double closest_distance = DBL_MAX; | 231 | double closest_distance = DBL_MAX; |
232 | struct sway_container *closest_con = NULL; | 232 | struct sway_container *closest_con = NULL; |
233 | 233 | ||
234 | if (!con->workspace) { | 234 | if (!con->pending.workspace) { |
235 | return NULL; | 235 | return NULL; |
236 | } | 236 | } |
237 | 237 | ||
238 | for (int i = 0; i < con->workspace->floating->length; i++) { | 238 | for (int i = 0; i < con->pending.workspace->floating->length; i++) { |
239 | struct sway_container *floater = con->workspace->floating->items[i]; | 239 | struct sway_container *floater = con->pending.workspace->floating->items[i]; |
240 | if (floater == con) { | 240 | if (floater == con) { |
241 | continue; | 241 | continue; |
242 | } | 242 | } |
243 | float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT | 243 | float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT |
244 | ? (floater->x + floater->width / 2) - ref_lx | 244 | ? (floater->pending.x + floater->pending.width / 2) - ref_lx |
245 | : (floater->y + floater->height / 2) - ref_ly; | 245 | : (floater->pending.y + floater->pending.height / 2) - ref_ly; |
246 | if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) { | 246 | if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) { |
247 | distance = -distance; | 247 | distance = -distance; |
248 | } | 248 | } |
@@ -267,6 +267,11 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws, | |||
267 | new_focus = seat_get_focus_inactive_tiling(seat, ws); | 267 | new_focus = seat_get_focus_inactive_tiling(seat, ws); |
268 | } | 268 | } |
269 | if (new_focus) { | 269 | if (new_focus) { |
270 | struct sway_container *new_focus_view = | ||
271 | seat_get_focus_inactive_view(seat, &new_focus->node); | ||
272 | if (new_focus_view) { | ||
273 | new_focus = new_focus_view; | ||
274 | } | ||
270 | seat_set_focus_container(seat, new_focus); | 275 | seat_set_focus_container(seat, new_focus); |
271 | 276 | ||
272 | // If we're on the floating layer and the floating container area | 277 | // If we're on the floating layer and the floating container area |
@@ -280,7 +285,7 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws, | |||
280 | } | 285 | } |
281 | } else { | 286 | } else { |
282 | return cmd_results_new(CMD_FAILURE, | 287 | return cmd_results_new(CMD_FAILURE, |
283 | "Failed to find a %s container in workspace", | 288 | "Failed to find a %s container in workspace.", |
284 | floating ? "floating" : "tiling"); | 289 | floating ? "floating" : "tiling"); |
285 | } | 290 | } |
286 | return cmd_results_new(CMD_SUCCESS, NULL); | 291 | return cmd_results_new(CMD_SUCCESS, NULL); |
@@ -290,7 +295,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat, | |||
290 | int argc, char **argv) { | 295 | int argc, char **argv) { |
291 | if (!argc) { | 296 | if (!argc) { |
292 | return cmd_results_new(CMD_INVALID, | 297 | return cmd_results_new(CMD_INVALID, |
293 | "Expected 'focus output <direction|name>'"); | 298 | "Expected 'focus output <direction|name>'."); |
294 | } | 299 | } |
295 | char *identifier = join_args(argv, argc); | 300 | char *identifier = join_args(argv, argc); |
296 | struct sway_output *output = output_by_name_or_id(identifier); | 301 | struct sway_output *output = output_by_name_or_id(identifier); |
@@ -300,13 +305,13 @@ static struct cmd_results *focus_output(struct sway_seat *seat, | |||
300 | if (!parse_direction(identifier, &direction)) { | 305 | if (!parse_direction(identifier, &direction)) { |
301 | free(identifier); | 306 | free(identifier); |
302 | return cmd_results_new(CMD_INVALID, | 307 | return cmd_results_new(CMD_INVALID, |
303 | "There is no output with that name"); | 308 | "There is no output with that name."); |
304 | } | 309 | } |
305 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | 310 | struct sway_workspace *ws = seat_get_focused_workspace(seat); |
306 | if (!ws) { | 311 | if (!ws) { |
307 | free(identifier); | 312 | free(identifier); |
308 | return cmd_results_new(CMD_FAILURE, | 313 | return cmd_results_new(CMD_FAILURE, |
309 | "No focused workspace to base directions off of"); | 314 | "No focused workspace to base directions off of."); |
310 | } | 315 | } |
311 | output = output_get_in_direction(ws->output, direction); | 316 | output = output_get_in_direction(ws->output, direction); |
312 | 317 | ||
@@ -334,7 +339,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat, | |||
334 | static struct cmd_results *focus_parent(void) { | 339 | static struct cmd_results *focus_parent(void) { |
335 | struct sway_seat *seat = config->handler_context.seat; | 340 | struct sway_seat *seat = config->handler_context.seat; |
336 | struct sway_container *con = config->handler_context.container; | 341 | struct sway_container *con = config->handler_context.container; |
337 | if (!con || con->fullscreen_mode) { | 342 | if (!con || con->pending.fullscreen_mode) { |
338 | return cmd_results_new(CMD_SUCCESS, NULL); | 343 | return cmd_results_new(CMD_SUCCESS, NULL); |
339 | } | 344 | } |
340 | struct sway_node *parent = node_get_parent(&con->node); | 345 | struct sway_node *parent = node_get_parent(&con->node); |
@@ -370,13 +375,24 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
370 | struct sway_seat *seat = config->handler_context.seat; | 375 | struct sway_seat *seat = config->handler_context.seat; |
371 | if (node->type < N_WORKSPACE) { | 376 | if (node->type < N_WORKSPACE) { |
372 | return cmd_results_new(CMD_FAILURE, | 377 | return cmd_results_new(CMD_FAILURE, |
373 | "Command 'focus' cannot be used above the workspace level"); | 378 | "Command 'focus' cannot be used above the workspace level."); |
374 | } | 379 | } |
375 | 380 | ||
376 | if (argc == 0 && container) { | 381 | if (argc == 0) { |
382 | if (!container) { | ||
383 | return cmd_results_new(CMD_FAILURE, "No container to focus was specified."); | ||
384 | } | ||
385 | |||
377 | if (container_is_scratchpad_hidden_or_child(container)) { | 386 | if (container_is_scratchpad_hidden_or_child(container)) { |
378 | root_scratchpad_show(container); | 387 | root_scratchpad_show(container); |
379 | } | 388 | } |
389 | // if we are switching to a container under a fullscreen window, we first | ||
390 | // need to exit fullscreen so that the newly focused container becomes visible | ||
391 | struct sway_container *obstructing = container_obstructing_fullscreen_container(container); | ||
392 | if (obstructing) { | ||
393 | container_fullscreen_disable(obstructing); | ||
394 | arrange_root(); | ||
395 | } | ||
380 | seat_set_focus_container(seat, container); | 396 | seat_set_focus_container(seat, container); |
381 | seat_consider_warp_to_focus(seat); | 397 | seat_consider_warp_to_focus(seat); |
382 | container_raise_floating(container); | 398 | container_raise_floating(container); |
@@ -439,7 +455,8 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
439 | return cmd_results_new(CMD_FAILURE, ""); | 455 | return cmd_results_new(CMD_FAILURE, ""); |
440 | } | 456 | } |
441 | struct sway_node *next_focus = NULL; | 457 | struct sway_node *next_focus = NULL; |
442 | if (container_is_floating(container)) { | 458 | if (container_is_floating(container) && |
459 | container->pending.fullscreen_mode == FULLSCREEN_NONE) { | ||
443 | next_focus = node_get_in_direction_floating(container, seat, direction); | 460 | next_focus = node_get_in_direction_floating(container, seat, direction); |
444 | } else { | 461 | } else { |
445 | next_focus = node_get_in_direction_tiling(container, seat, direction, descend); | 462 | next_focus = node_get_in_direction_tiling(container, seat, direction, descend); |
diff --git a/sway/commands/font.c b/sway/commands/font.c index c54365b5..9920d03e 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c | |||
@@ -1,9 +1,9 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
5 | #include "log.h" | 4 | #include "log.h" |
6 | #include "stringop.h" | 5 | #include "stringop.h" |
6 | #include <pango/pangocairo.h> | ||
7 | 7 | ||
8 | struct cmd_results *cmd_font(int argc, char **argv) { | 8 | struct cmd_results *cmd_font(int argc, char **argv) { |
9 | struct cmd_results *error = NULL; | 9 | struct cmd_results *error = NULL; |
@@ -16,12 +16,34 @@ struct cmd_results *cmd_font(int argc, char **argv) { | |||
16 | if (strncmp(font, "pango:", 6) == 0) { | 16 | if (strncmp(font, "pango:", 6) == 0) { |
17 | config->pango_markup = true; | 17 | config->pango_markup = true; |
18 | config->font = strdup(font + 6); | 18 | config->font = strdup(font + 6); |
19 | free(font); | ||
19 | } else { | 20 | } else { |
20 | config->pango_markup = false; | 21 | config->pango_markup = false; |
21 | config->font = strdup(font); | 22 | config->font = font; |
22 | } | 23 | } |
23 | 24 | ||
24 | free(font); | 25 | // Parse the font early so we can reject it if it's not valid for pango. |
25 | config_update_font_height(true); | 26 | // Also avoids re-parsing each time we render text. |
27 | PangoFontDescription *font_description = pango_font_description_from_string(config->font); | ||
28 | |||
29 | const char *family = pango_font_description_get_family(font_description); | ||
30 | if (family == NULL) { | ||
31 | pango_font_description_free(font_description); | ||
32 | return cmd_results_new(CMD_FAILURE, "Invalid font family."); | ||
33 | } | ||
34 | |||
35 | const gint size = pango_font_description_get_size(font_description); | ||
36 | if (size == 0) { | ||
37 | pango_font_description_free(font_description); | ||
38 | return cmd_results_new(CMD_FAILURE, "Invalid font size."); | ||
39 | } | ||
40 | |||
41 | if (config->font_description != NULL) { | ||
42 | pango_font_description_free(config->font_description); | ||
43 | } | ||
44 | |||
45 | config->font_description = font_description; | ||
46 | config_update_font_height(); | ||
47 | |||
26 | return cmd_results_new(CMD_SUCCESS, NULL); | 48 | return cmd_results_new(CMD_SUCCESS, NULL); |
27 | } | 49 | } |
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c index ee9f4647..905e6776 100644 --- a/sway/commands/for_window.c +++ b/sway/commands/for_window.c | |||
@@ -14,7 +14,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) { | |||
14 | char *err_str = NULL; | 14 | char *err_str = NULL; |
15 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | 15 | struct criteria *criteria = criteria_parse(argv[0], &err_str); |
16 | if (!criteria) { | 16 | if (!criteria) { |
17 | error = cmd_results_new(CMD_INVALID, err_str); | 17 | error = cmd_results_new(CMD_INVALID, "%s", err_str); |
18 | free(err_str); | 18 | free(err_str); |
19 | return error; | 19 | return error; |
20 | } | 20 | } |
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 3392a7f7..21c1e9a0 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c | |||
@@ -18,30 +18,19 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { | |||
18 | return cmd_results_new(CMD_FAILURE, | 18 | return cmd_results_new(CMD_FAILURE, |
19 | "Can't run this command while there's no outputs connected."); | 19 | "Can't run this command while there's no outputs connected."); |
20 | } | 20 | } |
21 | struct sway_node *node = config->handler_context.node; | ||
22 | struct sway_container *container = config->handler_context.container; | 21 | struct sway_container *container = config->handler_context.container; |
23 | struct sway_workspace *workspace = config->handler_context.workspace; | ||
24 | if (node->type == N_WORKSPACE && workspace->tiling->length == 0) { | ||
25 | return cmd_results_new(CMD_FAILURE, | ||
26 | "Can't fullscreen an empty workspace"); | ||
27 | } | ||
28 | 22 | ||
29 | // If in the scratchpad, operate on the highest container | 23 | if (!container) { |
30 | if (container && !container->workspace) { | 24 | // If the focus is not a container, do nothing successfully |
31 | while (container->parent) { | 25 | return cmd_results_new(CMD_SUCCESS, NULL); |
32 | container = container->parent; | 26 | } else if (!container->pending.workspace) { |
33 | } | 27 | // If in the scratchpad, operate on the highest container |
34 | } | 28 | while (container->pending.parent) { |
35 | 29 | container = container->pending.parent; | |
36 | bool is_fullscreen = false; | ||
37 | for (struct sway_container *curr = container; curr; curr = curr->parent) { | ||
38 | if (curr->fullscreen_mode != FULLSCREEN_NONE) { | ||
39 | container = curr; | ||
40 | is_fullscreen = true; | ||
41 | break; | ||
42 | } | 30 | } |
43 | } | 31 | } |
44 | 32 | ||
33 | bool is_fullscreen = container->pending.fullscreen_mode != FULLSCREEN_NONE; | ||
45 | bool global = false; | 34 | bool global = false; |
46 | bool enable = !is_fullscreen; | 35 | bool enable = !is_fullscreen; |
47 | 36 | ||
@@ -57,13 +46,6 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { | |||
57 | global = strcasecmp(argv[1], "global") == 0; | 46 | global = strcasecmp(argv[1], "global") == 0; |
58 | } | 47 | } |
59 | 48 | ||
60 | if (enable && node->type == N_WORKSPACE) { | ||
61 | // Wrap the workspace's children in a container so we can fullscreen it | ||
62 | container = workspace_wrap_children(workspace); | ||
63 | workspace->layout = L_HORIZ; | ||
64 | seat_set_focus_container(config->handler_context.seat, container); | ||
65 | } | ||
66 | |||
67 | enum sway_fullscreen_mode mode = FULLSCREEN_NONE; | 49 | enum sway_fullscreen_mode mode = FULLSCREEN_NONE; |
68 | if (enable) { | 50 | if (enable) { |
69 | mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE; | 51 | mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE; |
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 021df843..1deeb56e 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c | |||
@@ -11,7 +11,8 @@ | |||
11 | enum gaps_op { | 11 | enum gaps_op { |
12 | GAPS_OP_SET, | 12 | GAPS_OP_SET, |
13 | GAPS_OP_ADD, | 13 | GAPS_OP_ADD, |
14 | GAPS_OP_SUBTRACT | 14 | GAPS_OP_SUBTRACT, |
15 | GAPS_OP_TOGGLE | ||
15 | }; | 16 | }; |
16 | 17 | ||
17 | struct gaps_data { | 18 | struct gaps_data { |
@@ -102,6 +103,9 @@ static void apply_gaps_op(int *prop, enum gaps_op op, int amount) { | |||
102 | case GAPS_OP_SUBTRACT: | 103 | case GAPS_OP_SUBTRACT: |
103 | *prop -= amount; | 104 | *prop -= amount; |
104 | break; | 105 | break; |
106 | case GAPS_OP_TOGGLE: | ||
107 | *prop = *prop ? 0 : amount; | ||
108 | break; | ||
105 | } | 109 | } |
106 | } | 110 | } |
107 | 111 | ||
@@ -133,9 +137,9 @@ static void configure_gaps(struct sway_workspace *ws, void *_data) { | |||
133 | } | 137 | } |
134 | 138 | ||
135 | // gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all | 139 | // gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all |
136 | // set|plus|minus <px> | 140 | // set|plus|minus|toggle <px> |
137 | static const char expected_runtime[] = "'gaps inner|outer|horizontal|vertical|" | 141 | static const char expected_runtime[] = "'gaps inner|outer|horizontal|vertical|" |
138 | "top|right|bottom|left current|all set|plus|minus <px>'"; | 142 | "top|right|bottom|left current|all set|plus|minus|toggle <px>'"; |
139 | static struct cmd_results *gaps_set_runtime(int argc, char **argv) { | 143 | static struct cmd_results *gaps_set_runtime(int argc, char **argv) { |
140 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); | 144 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); |
141 | if (error) { | 145 | if (error) { |
@@ -180,6 +184,8 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { | |||
180 | data.operation = GAPS_OP_ADD; | 184 | data.operation = GAPS_OP_ADD; |
181 | } else if (strcasecmp(argv[2], "minus") == 0) { | 185 | } else if (strcasecmp(argv[2], "minus") == 0) { |
182 | data.operation = GAPS_OP_SUBTRACT; | 186 | data.operation = GAPS_OP_SUBTRACT; |
187 | } else if (strcasecmp(argv[2], "toggle") == 0) { | ||
188 | data.operation = GAPS_OP_TOGGLE; | ||
183 | } else { | 189 | } else { |
184 | return cmd_results_new(CMD_INVALID, "Expected %s", expected_runtime); | 190 | return cmd_results_new(CMD_INVALID, "Expected %s", expected_runtime); |
185 | } | 191 | } |
@@ -200,7 +206,7 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { | |||
200 | } | 206 | } |
201 | 207 | ||
202 | // gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces | 208 | // gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces |
203 | // gaps inner|outer|<dir>|<side> current|all set|plus|minus <px> - runtime only | 209 | // gaps inner|outer|<dir>|<side> current|all set|plus|minus|toggle <px> - runtime only |
204 | // <dir> = horizontal|vertical | 210 | // <dir> = horizontal|vertical |
205 | // <side> = top|right|bottom|left | 211 | // <side> = top|right|bottom|left |
206 | struct cmd_results *cmd_gaps(int argc, char **argv) { | 212 | struct cmd_results *cmd_gaps(int argc, char **argv) { |
diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c new file mode 100644 index 00000000..90a20716 --- /dev/null +++ b/sway/commands/gesture.c | |||
@@ -0,0 +1,165 @@ | |||
1 | #include "sway/config.h" | ||
2 | |||
3 | #include "gesture.h" | ||
4 | #include "log.h" | ||
5 | #include "stringop.h" | ||
6 | #include "sway/commands.h" | ||
7 | |||
8 | void free_gesture_binding(struct sway_gesture_binding *binding) { | ||
9 | if (!binding) { | ||
10 | return; | ||
11 | } | ||
12 | free(binding->input); | ||
13 | free(binding->command); | ||
14 | free(binding); | ||
15 | } | ||
16 | |||
17 | /** | ||
18 | * Returns true if the bindings have the same gesture type, direction, etc | ||
19 | */ | ||
20 | static bool binding_gesture_equal(struct sway_gesture_binding *binding_a, | ||
21 | struct sway_gesture_binding *binding_b) { | ||
22 | if (strcmp(binding_a->input, binding_b->input) != 0) { | ||
23 | return false; | ||
24 | } | ||
25 | |||
26 | if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) { | ||
27 | return false; | ||
28 | } | ||
29 | |||
30 | if ((binding_a->flags & BINDING_EXACT) != | ||
31 | (binding_b->flags & BINDING_EXACT)) { | ||
32 | return false; | ||
33 | } | ||
34 | return true; | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | * Add gesture binding to config | ||
39 | */ | ||
40 | static struct cmd_results *gesture_binding_add( | ||
41 | struct sway_gesture_binding *binding, | ||
42 | const char *gesturecombo, bool warn) { | ||
43 | list_t *mode_bindings = config->current_mode->gesture_bindings; | ||
44 | // overwrite the binding if it already exists | ||
45 | bool overwritten = false; | ||
46 | for (int i = 0; i < mode_bindings->length; ++i) { | ||
47 | struct sway_gesture_binding *config_binding = mode_bindings->items[i]; | ||
48 | if (binding_gesture_equal(binding, config_binding)) { | ||
49 | sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", | ||
50 | gesturecombo, binding->command, config_binding->command); | ||
51 | if (warn) { | ||
52 | config_add_swaynag_warning("Overwriting binding" | ||
53 | "'%s' to `%s` from `%s`", | ||
54 | gesturecombo, binding->command, | ||
55 | config_binding->command); | ||
56 | } | ||
57 | free_gesture_binding(config_binding); | ||
58 | mode_bindings->items[i] = binding; | ||
59 | overwritten = true; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | if (!overwritten) { | ||
64 | list_add(mode_bindings, binding); | ||
65 | sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`", | ||
66 | gesturecombo, binding->command); | ||
67 | } | ||
68 | |||
69 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * Remove gesture binding from config | ||
74 | */ | ||
75 | static struct cmd_results *gesture_binding_remove( | ||
76 | struct sway_gesture_binding *binding, const char *gesturecombo) { | ||
77 | list_t *mode_bindings = config->current_mode->gesture_bindings; | ||
78 | for (int i = 0; i < mode_bindings->length; ++i) { | ||
79 | struct sway_gesture_binding *config_binding = mode_bindings->items[i]; | ||
80 | if (binding_gesture_equal(binding, config_binding)) { | ||
81 | free_gesture_binding(config_binding); | ||
82 | free_gesture_binding(binding); | ||
83 | list_del(mode_bindings, i); | ||
84 | sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture", | ||
85 | gesturecombo); | ||
86 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | free_gesture_binding(binding); | ||
91 | return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`", | ||
92 | gesturecombo); | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * Parse and execute bindgesture or unbindgesture command. | ||
97 | */ | ||
98 | static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) { | ||
99 | int minargs = 2; | ||
100 | char *bindtype = "bindgesture"; | ||
101 | if (unbind) { | ||
102 | minargs--; | ||
103 | bindtype = "unbindgesture"; | ||
104 | } | ||
105 | |||
106 | struct cmd_results *error = NULL; | ||
107 | if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { | ||
108 | return error; | ||
109 | } | ||
110 | struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding)); | ||
111 | if (!binding) { | ||
112 | return cmd_results_new(CMD_FAILURE, "Unable to allocate binding"); | ||
113 | } | ||
114 | binding->input = strdup("*"); | ||
115 | |||
116 | bool warn = true; | ||
117 | |||
118 | // Handle flags | ||
119 | while (argc > 0) { | ||
120 | if (strcmp("--exact", argv[0]) == 0) { | ||
121 | binding->flags |= BINDING_EXACT; | ||
122 | } else if (strcmp("--no-warn", argv[0]) == 0) { | ||
123 | warn = false; | ||
124 | } else if (strncmp("--input-device=", argv[0], | ||
125 | strlen("--input-device=")) == 0) { | ||
126 | free(binding->input); | ||
127 | binding->input = strdup(argv[0] + strlen("--input-device=")); | ||
128 | } else { | ||
129 | break; | ||
130 | } | ||
131 | argv++; | ||
132 | argc--; | ||
133 | } | ||
134 | |||
135 | if (argc < minargs) { | ||
136 | free(binding); | ||
137 | return cmd_results_new(CMD_FAILURE, | ||
138 | "Invalid %s command (expected at least %d " | ||
139 | "non-option arguments, got %d)", bindtype, minargs, argc); | ||
140 | } | ||
141 | |||
142 | char* errmsg = NULL; | ||
143 | if ((errmsg = gesture_parse(argv[0], &binding->gesture))) { | ||
144 | free(binding); | ||
145 | struct cmd_results *final = cmd_results_new(CMD_FAILURE, | ||
146 | "Invalid %s command (%s)", | ||
147 | bindtype, errmsg); | ||
148 | free(errmsg); | ||
149 | return final; | ||
150 | } | ||
151 | |||
152 | if (unbind) { | ||
153 | return gesture_binding_remove(binding, argv[0]); | ||
154 | } | ||
155 | binding->command = join_args(argv + 1, argc - 1); | ||
156 | return gesture_binding_add(binding, argv[0], warn); | ||
157 | } | ||
158 | |||
159 | struct cmd_results *cmd_bindgesture(int argc, char **argv) { | ||
160 | return cmd_bind_or_unbind_gesture(argc, argv, false); | ||
161 | } | ||
162 | |||
163 | struct cmd_results *cmd_unbindgesture(int argc, char **argv) { | ||
164 | return cmd_bind_or_unbind_gesture(argc, argv, true); | ||
165 | } | ||
diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index 9a1d8445..43bd6dc8 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c | |||
@@ -20,7 +20,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { | |||
20 | } | 20 | } |
21 | 21 | ||
22 | if (!argc) { | 22 | if (!argc) { |
23 | return cmd_results_new(CMD_INVALID, expected_syntax); | 23 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
24 | } | 24 | } |
25 | 25 | ||
26 | if (strcmp(argv[0], "none") == 0) { | 26 | if (strcmp(argv[0], "none") == 0) { |
@@ -38,7 +38,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { | |||
38 | config->hide_edge_borders = E_NONE; | 38 | config->hide_edge_borders = E_NONE; |
39 | config->hide_edge_borders_smart = ESMART_NO_GAPS; | 39 | config->hide_edge_borders_smart = ESMART_NO_GAPS; |
40 | } else { | 40 | } else { |
41 | return cmd_results_new(CMD_INVALID, expected_syntax); | 41 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
42 | } | 42 | } |
43 | config->hide_lone_tab = hide_lone_tab; | 43 | config->hide_lone_tab = hide_lone_tab; |
44 | 44 | ||
diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c index aebc2bf9..6125736a 100644 --- a/sway/commands/inhibit_idle.c +++ b/sway/commands/inhibit_idle.c | |||
@@ -41,7 +41,7 @@ struct cmd_results *cmd_inhibit_idle(int argc, char **argv) { | |||
41 | sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); | 41 | sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); |
42 | } else { | 42 | } else { |
43 | inhibitor->mode = mode; | 43 | inhibitor->mode = mode; |
44 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | 44 | sway_idle_inhibit_v1_check_active(); |
45 | } | 45 | } |
46 | } else if (!clear) { | 46 | } else if (!clear) { |
47 | sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); | 47 | sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); |
diff --git a/sway/commands/input.c b/sway/commands/input.c index c9bb8e06..306c40f7 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c | |||
@@ -7,13 +7,14 @@ | |||
7 | #include "stringop.h" | 7 | #include "stringop.h" |
8 | 8 | ||
9 | // must be in order for the bsearch | 9 | // must be in order for the bsearch |
10 | static struct cmd_handler input_handlers[] = { | 10 | static const struct cmd_handler input_handlers[] = { |
11 | { "accel_profile", input_cmd_accel_profile }, | 11 | { "accel_profile", input_cmd_accel_profile }, |
12 | { "calibration_matrix", input_cmd_calibration_matrix }, | 12 | { "calibration_matrix", input_cmd_calibration_matrix }, |
13 | { "click_method", input_cmd_click_method }, | 13 | { "click_method", input_cmd_click_method }, |
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 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 }, |
@@ -40,7 +43,7 @@ static struct cmd_handler input_handlers[] = { | |||
40 | }; | 43 | }; |
41 | 44 | ||
42 | // must be in order for the bsearch | 45 | // must be in order for the bsearch |
43 | static struct cmd_handler input_config_handlers[] = { | 46 | static const struct cmd_handler input_config_handlers[] = { |
44 | { "xkb_capslock", input_cmd_xkb_capslock }, | 47 | { "xkb_capslock", input_cmd_xkb_capslock }, |
45 | { "xkb_numlock", input_cmd_xkb_numlock }, | 48 | { "xkb_numlock", input_cmd_xkb_numlock }, |
46 | }; | 49 | }; |
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 e85495e5..9087c589 100644 --- a/sway/commands/input/map_to_region.c +++ b/sway/commands/input/map_to_region.c | |||
@@ -1,7 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <wlr/types/wlr_box.h> | ||
5 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
6 | #include "sway/config.h" | 4 | #include "sway/config.h" |
7 | 5 | ||
@@ -50,5 +48,5 @@ struct cmd_results *input_cmd_map_to_region(int argc, char **argv) { | |||
50 | error: | 48 | error: |
51 | free(ic->mapped_to_region); | 49 | free(ic->mapped_to_region); |
52 | ic->mapped_to_region = NULL; | 50 | ic->mapped_to_region = NULL; |
53 | return cmd_results_new(CMD_FAILURE, errstr); | 51 | return cmd_results_new(CMD_FAILURE, "%s", errstr); |
54 | } | 52 | } |
diff --git a/sway/commands/input/rotation_angle.c b/sway/commands/input/rotation_angle.c new file mode 100644 index 00000000..5e278fff --- /dev/null +++ b/sway/commands/input/rotation_angle.c | |||
@@ -0,0 +1,29 @@ | |||
1 | #include <math.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | #include "sway/config.h" | ||
5 | #include "sway/commands.h" | ||
6 | #include "sway/input/input-manager.h" | ||
7 | #include "util.h" | ||
8 | |||
9 | struct cmd_results *input_cmd_rotation_angle(int argc, char **argv) { | ||
10 | struct cmd_results *error = NULL; | ||
11 | if ((error = checkarg(argc, "rotation_angle", EXPECTED_AT_LEAST, 1))) { | ||
12 | return error; | ||
13 | } | ||
14 | struct input_config *ic = config->handler_context.input_config; | ||
15 | if (!ic) { | ||
16 | return cmd_results_new(CMD_FAILURE, "No input device defined."); | ||
17 | } | ||
18 | |||
19 | float rotation_angle = parse_float(argv[0]); | ||
20 | if (isnan(rotation_angle)) { | ||
21 | return cmd_results_new(CMD_INVALID, | ||
22 | "Invalid rotation_angle; expected float."); | ||
23 | } if (rotation_angle < 0 || rotation_angle > 360) { | ||
24 | return cmd_results_new(CMD_INVALID, "Input out of range [0, 360)"); | ||
25 | } | ||
26 | ic->rotation_angle = rotation_angle; | ||
27 | |||
28 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
29 | } | ||
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c index 6b331419..81f69a6d 100644 --- a/sway/commands/input/scroll_button.c +++ b/sway/commands/input/scroll_button.c | |||
@@ -21,7 +21,7 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { | |||
21 | char *message = NULL; | 21 | char *message = NULL; |
22 | uint32_t button = get_mouse_button(*argv, &message); | 22 | uint32_t button = get_mouse_button(*argv, &message); |
23 | if (message) { | 23 | if (message) { |
24 | error = cmd_results_new(CMD_INVALID, message); | 24 | error = cmd_results_new(CMD_INVALID, "%s", message); |
25 | free(message); | 25 | free(message); |
26 | return error; | 26 | return error; |
27 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN | 27 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN |
diff --git a/sway/commands/input/scroll_button_lock.c b/sway/commands/input/scroll_button_lock.c new file mode 100644 index 00000000..f96b6514 --- /dev/null +++ b/sway/commands/input/scroll_button_lock.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #include <libinput.h> | ||
2 | #include <string.h> | ||
3 | #include <strings.h> | ||
4 | #include "sway/config.h" | ||
5 | #include "sway/commands.h" | ||
6 | #include "sway/input/input-manager.h" | ||
7 | #include "util.h" | ||
8 | |||
9 | struct cmd_results *input_cmd_scroll_button_lock(int argc, char **argv) { | ||
10 | struct cmd_results *error = NULL; | ||
11 | if ((error = checkarg(argc, "scroll_button_lock", EXPECTED_AT_LEAST, 1))) { | ||
12 | return error; | ||
13 | } | ||
14 | struct input_config *ic = config->handler_context.input_config; | ||
15 | if (!ic) { | ||
16 | return cmd_results_new(CMD_FAILURE, "No input device defined."); | ||
17 | } | ||
18 | |||
19 | if (parse_boolean(argv[0], true)) { | ||
20 | ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED; | ||
21 | } else { | ||
22 | ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED; | ||
23 | } | ||
24 | |||
25 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
26 | } | ||
diff --git a/sway/commands/input/xkb_file.c b/sway/commands/input/xkb_file.c index 493f94fb..056f00e5 100644 --- a/sway/commands/input/xkb_file.c +++ b/sway/commands/input/xkb_file.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <unistd.h> | 1 | #include <unistd.h> |
3 | #include <errno.h> | 2 | #include <errno.h> |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c index 22626517..1d01886c 100644 --- a/sway/commands/input/xkb_layout.c +++ b/sway/commands/input/xkb_layout.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c index f4a33de3..a9144a8a 100644 --- a/sway/commands/input/xkb_model.c +++ b/sway/commands/input/xkb_model.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c index 87d3e60c..bbe848fe 100644 --- a/sway/commands/input/xkb_numlock.c +++ b/sway/commands/input/xkb_numlock.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "util.h" | 3 | #include "util.h" |
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c index d609293f..7ca20777 100644 --- a/sway/commands/input/xkb_options.c +++ b/sway/commands/input/xkb_options.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c index 3b59622c..8fbd26fb 100644 --- a/sway/commands/input/xkb_rules.c +++ b/sway/commands/input/xkb_rules.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c index d6548a68..ecac8e6c 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c | |||
@@ -1,10 +1,15 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include <wlr/interfaces/wlr_keyboard.h> | ||
3 | #include "sway/config.h" | 3 | #include "sway/config.h" |
4 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
5 | #include "sway/input/input-manager.h" | 5 | #include "sway/input/input-manager.h" |
6 | #include "log.h" | 6 | #include "log.h" |
7 | 7 | ||
8 | struct xkb_switch_layout_action { | ||
9 | struct wlr_keyboard *keyboard; | ||
10 | xkb_layout_index_t layout; | ||
11 | }; | ||
12 | |||
8 | static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { | 13 | static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { |
9 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); | 14 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); |
10 | if (idx >= num_layouts) { | 15 | if (idx >= num_layouts) { |
@@ -28,10 +33,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) { | |||
28 | return layout_idx; | 33 | return layout_idx; |
29 | } | 34 | } |
30 | 35 | ||
31 | static void switch_layout_relative(struct wlr_keyboard *kbd, int dir) { | 36 | static xkb_layout_index_t get_layout_relative(struct wlr_keyboard *kbd, int dir) { |
32 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); | 37 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); |
33 | xkb_layout_index_t idx = get_current_layout_index(kbd); | 38 | xkb_layout_index_t idx = get_current_layout_index(kbd); |
34 | switch_layout(kbd, (idx + num_layouts + dir) % num_layouts); | 39 | return (idx + num_layouts + dir) % num_layouts; |
35 | } | 40 | } |
36 | 41 | ||
37 | struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | 42 | struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { |
@@ -66,6 +71,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | |||
66 | relative = 0; | 71 | relative = 0; |
67 | } | 72 | } |
68 | 73 | ||
74 | struct xkb_switch_layout_action *actions = calloc( | ||
75 | wl_list_length(&server.input->devices), | ||
76 | sizeof(struct xkb_switch_layout_action)); | ||
77 | size_t actions_len = 0; | ||
78 | |||
79 | if (!actions) { | ||
80 | return cmd_results_new(CMD_FAILURE, "Unable to allocate actions"); | ||
81 | } | ||
82 | |||
83 | /* Calculate new indexes first because switching a layout in one | ||
84 | keyboard may result in a change on other keyboards as well because | ||
85 | of keyboard groups. */ | ||
69 | struct sway_input_device *dev; | 86 | struct sway_input_device *dev; |
70 | wl_list_for_each(dev, &server.input->devices, link) { | 87 | wl_list_for_each(dev, &server.input->devices, link) { |
71 | if (strcmp(ic->identifier, "*") != 0 && | 88 | if (strcmp(ic->identifier, "*") != 0 && |
@@ -76,12 +93,22 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | |||
76 | if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { | 93 | if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { |
77 | continue; | 94 | continue; |
78 | } | 95 | } |
96 | |||
97 | struct xkb_switch_layout_action *action = | ||
98 | &actions[actions_len++]; | ||
99 | |||
100 | action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device); | ||
79 | if (relative) { | 101 | if (relative) { |
80 | switch_layout_relative(dev->wlr_device->keyboard, relative); | 102 | action->layout = get_layout_relative(action->keyboard, relative); |
81 | } else { | 103 | } else { |
82 | switch_layout(dev->wlr_device->keyboard, layout); | 104 | action->layout = layout; |
83 | } | 105 | } |
84 | } | 106 | } |
85 | 107 | ||
108 | for (size_t i = 0; i < actions_len; i++) { | ||
109 | switch_layout(actions[i].keyboard, actions[i].layout); | ||
110 | } | ||
111 | free(actions); | ||
112 | |||
86 | return cmd_results_new(CMD_SUCCESS, NULL); | 113 | return cmd_results_new(CMD_SUCCESS, NULL); |
87 | } | 114 | } |
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c index d0e21d77..2d14ea9c 100644 --- a/sway/commands/input/xkb_variant.c +++ b/sway/commands/input/xkb_variant.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/layout.c b/sway/commands/layout.c index f2af183b..12ce4839 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c | |||
@@ -133,7 +133,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
133 | 133 | ||
134 | // Operate on parent container, like i3. | 134 | // Operate on parent container, like i3. |
135 | if (container) { | 135 | if (container) { |
136 | container = container->parent; | 136 | container = container->pending.parent; |
137 | } | 137 | } |
138 | 138 | ||
139 | // We could be working with a container OR a workspace. These are different | 139 | // We could be working with a container OR a workspace. These are different |
@@ -142,10 +142,10 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
142 | enum sway_container_layout new_layout = L_NONE; | 142 | enum sway_container_layout new_layout = L_NONE; |
143 | enum sway_container_layout old_layout = L_NONE; | 143 | enum sway_container_layout old_layout = L_NONE; |
144 | if (container) { | 144 | if (container) { |
145 | old_layout = container->layout; | 145 | old_layout = container->pending.layout; |
146 | new_layout = get_layout(argc, argv, | 146 | new_layout = get_layout(argc, argv, |
147 | container->layout, container->prev_split_layout, | 147 | container->pending.layout, container->prev_split_layout, |
148 | container->workspace->output); | 148 | container->pending.workspace->output); |
149 | } else { | 149 | } else { |
150 | old_layout = workspace->layout; | 150 | old_layout = workspace->layout; |
151 | new_layout = get_layout(argc, argv, | 151 | new_layout = get_layout(argc, argv, |
@@ -153,20 +153,20 @@ 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) { |
160 | if (old_layout != L_TABBED && old_layout != L_STACKED) { | 160 | if (old_layout != L_TABBED && old_layout != L_STACKED) { |
161 | container->prev_split_layout = old_layout; | 161 | container->prev_split_layout = old_layout; |
162 | } | 162 | } |
163 | container->layout = new_layout; | 163 | container->pending.layout = new_layout; |
164 | container_update_representation(container); | 164 | container_update_representation(container); |
165 | } else if (config->handler_context.container) { | 165 | } else if (config->handler_context.container) { |
166 | // i3 avoids changing workspace layouts with a new container | 166 | // i3 avoids changing workspace layouts with a new container |
167 | // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/con.c#L1817 | 167 | // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/con.c#L1817 |
168 | container = workspace_wrap_children(workspace); | 168 | container = workspace_wrap_children(workspace); |
169 | container->layout = new_layout; | 169 | container->pending.layout = new_layout; |
170 | container_update_representation(container); | 170 | container_update_representation(container); |
171 | } else { | 171 | } else { |
172 | if (old_layout != L_TABBED && old_layout != L_STACKED) { | 172 | if (old_layout != L_TABBED && old_layout != L_STACKED) { |
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 a5871dab..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" |
@@ -9,12 +8,14 @@ | |||
9 | #include "stringop.h" | 8 | #include "stringop.h" |
10 | 9 | ||
11 | // Must be in order for the bsearch | 10 | // Must be in order for the bsearch |
12 | static struct cmd_handler mode_handlers[] = { | 11 | static const struct cmd_handler mode_handlers[] = { |
13 | { "bindcode", cmd_bindcode }, | 12 | { "bindcode", cmd_bindcode }, |
13 | { "bindgesture", cmd_bindgesture }, | ||
14 | { "bindswitch", cmd_bindswitch }, | 14 | { "bindswitch", cmd_bindswitch }, |
15 | { "bindsym", cmd_bindsym }, | 15 | { "bindsym", cmd_bindsym }, |
16 | { "set", cmd_set }, | 16 | { "set", cmd_set }, |
17 | { "unbindcode", cmd_unbindcode }, | 17 | { "unbindcode", cmd_unbindcode }, |
18 | { "unbindgesture", cmd_unbindgesture }, | ||
18 | { "unbindswitch", cmd_unbindswitch }, | 19 | { "unbindswitch", cmd_unbindswitch }, |
19 | { "unbindsym", cmd_unbindsym }, | 20 | { "unbindsym", cmd_unbindsym }, |
20 | }; | 21 | }; |
@@ -59,6 +60,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { | |||
59 | mode->keycode_bindings = create_list(); | 60 | mode->keycode_bindings = create_list(); |
60 | mode->mouse_bindings = create_list(); | 61 | mode->mouse_bindings = create_list(); |
61 | mode->switch_bindings = create_list(); | 62 | mode->switch_bindings = create_list(); |
63 | mode->gesture_bindings = create_list(); | ||
62 | mode->pango = pango; | 64 | mode->pango = pango; |
63 | list_add(config->modes, mode); | 65 | list_add(config->modes, mode); |
64 | } | 66 | } |
diff --git a/sway/commands/move.c b/sway/commands/move.c index f8f89f18..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> |
@@ -113,8 +112,8 @@ static void container_move_to_container_from_direction( | |||
113 | struct sway_container *container, struct sway_container *destination, | 112 | struct sway_container *container, struct sway_container *destination, |
114 | enum wlr_direction move_dir) { | 113 | enum wlr_direction move_dir) { |
115 | if (destination->view) { | 114 | if (destination->view) { |
116 | if (destination->parent == container->parent && | 115 | if (destination->pending.parent == container->pending.parent && |
117 | destination->workspace == container->workspace) { | 116 | destination->pending.workspace == container->pending.workspace) { |
118 | sway_log(SWAY_DEBUG, "Swapping siblings"); | 117 | sway_log(SWAY_DEBUG, "Swapping siblings"); |
119 | list_t *siblings = container_get_siblings(container); | 118 | list_t *siblings = container_get_siblings(container); |
120 | int container_index = list_find(siblings, container); | 119 | int container_index = list_find(siblings, container); |
@@ -126,28 +125,28 @@ static void container_move_to_container_from_direction( | |||
126 | int offset = | 125 | int offset = |
127 | move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP; | 126 | move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP; |
128 | int index = container_sibling_index(destination) + offset; | 127 | int index = container_sibling_index(destination) + offset; |
129 | if (destination->parent) { | 128 | if (destination->pending.parent) { |
130 | container_insert_child(destination->parent, container, index); | 129 | container_insert_child(destination->pending.parent, container, index); |
131 | } else { | 130 | } else { |
132 | workspace_insert_tiling(destination->workspace, | 131 | workspace_insert_tiling(destination->pending.workspace, |
133 | container, index); | 132 | container, index); |
134 | } | 133 | } |
135 | container->width = container->height = 0; | 134 | container->pending.width = container->pending.height = 0; |
136 | container->width_fraction = container->height_fraction = 0; | 135 | container->width_fraction = container->height_fraction = 0; |
137 | workspace_squash(destination->workspace); | 136 | workspace_squash(destination->pending.workspace); |
138 | } | 137 | } |
139 | return; | 138 | return; |
140 | } | 139 | } |
141 | 140 | ||
142 | if (is_parallel(destination->layout, move_dir)) { | 141 | if (is_parallel(destination->pending.layout, move_dir)) { |
143 | sway_log(SWAY_DEBUG, "Reparenting container (parallel)"); | 142 | sway_log(SWAY_DEBUG, "Reparenting container (parallel)"); |
144 | int index = | 143 | int index = |
145 | move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ? | 144 | move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ? |
146 | 0 : destination->children->length; | 145 | 0 : destination->pending.children->length; |
147 | container_insert_child(destination, container, index); | 146 | container_insert_child(destination, container, index); |
148 | container->width = container->height = 0; | 147 | container->pending.width = container->pending.height = 0; |
149 | container->width_fraction = container->height_fraction = 0; | 148 | container->width_fraction = container->height_fraction = 0; |
150 | workspace_squash(destination->workspace); | 149 | workspace_squash(destination->pending.workspace); |
151 | return; | 150 | return; |
152 | } | 151 | } |
153 | 152 | ||
@@ -168,7 +167,7 @@ static void container_move_to_container_from_direction( | |||
168 | static void container_move_to_workspace_from_direction( | 167 | static void container_move_to_workspace_from_direction( |
169 | struct sway_container *container, struct sway_workspace *workspace, | 168 | struct sway_container *container, struct sway_workspace *workspace, |
170 | enum wlr_direction move_dir) { | 169 | enum wlr_direction move_dir) { |
171 | container->width = container->height = 0; | 170 | container->pending.width = container->pending.height = 0; |
172 | container->width_fraction = container->height_fraction = 0; | 171 | container->width_fraction = container->height_fraction = 0; |
173 | 172 | ||
174 | if (is_parallel(workspace->layout, move_dir)) { | 173 | if (is_parallel(workspace->layout, move_dir)) { |
@@ -188,8 +187,8 @@ static void container_move_to_workspace_from_direction( | |||
188 | workspace_add_tiling(workspace, container); | 187 | workspace_add_tiling(workspace, container); |
189 | return; | 188 | return; |
190 | } | 189 | } |
191 | while (focus_inactive->parent) { | 190 | while (focus_inactive->pending.parent) { |
192 | focus_inactive = focus_inactive->parent; | 191 | focus_inactive = focus_inactive->pending.parent; |
193 | } | 192 | } |
194 | container_move_to_container_from_direction(container, focus_inactive, | 193 | container_move_to_container_from_direction(container, focus_inactive, |
195 | move_dir); | 194 | move_dir); |
@@ -197,25 +196,33 @@ static void container_move_to_workspace_from_direction( | |||
197 | 196 | ||
198 | static void container_move_to_workspace(struct sway_container *container, | 197 | static void container_move_to_workspace(struct sway_container *container, |
199 | struct sway_workspace *workspace) { | 198 | struct sway_workspace *workspace) { |
200 | if (container->workspace == workspace) { | 199 | if (container->pending.workspace == workspace) { |
201 | return; | 200 | return; |
202 | } | 201 | } |
203 | struct sway_workspace *old_workspace = container->workspace; | 202 | struct sway_workspace *old_workspace = container->pending.workspace; |
204 | if (container_is_floating(container)) { | 203 | if (container_is_floating(container)) { |
205 | struct sway_output *old_output = container->workspace->output; | 204 | struct sway_output *old_output = container->pending.workspace->output; |
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->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); |
215 | if (workspace_is_empty(workspace) && container->children) { | 222 | if (workspace_is_empty(workspace) && container->pending.children) { |
216 | workspace_unwrap_children(workspace, container); | 223 | workspace_unwrap_children(workspace, container); |
217 | } else { | 224 | } else { |
218 | container->width = container->height = 0; | 225 | container->pending.width = container->pending.height = 0; |
219 | container->width_fraction = container->height_fraction = 0; | 226 | container->width_fraction = container->height_fraction = 0; |
220 | workspace_add_tiling(workspace, container); | 227 | workspace_add_tiling(workspace, container); |
221 | } | 228 | } |
@@ -237,13 +244,13 @@ static void container_move_to_container(struct sway_container *container, | |||
237 | return; | 244 | return; |
238 | } | 245 | } |
239 | if (container_is_floating(container)) { | 246 | if (container_is_floating(container)) { |
240 | container_move_to_workspace(container, destination->workspace); | 247 | container_move_to_workspace(container, destination->pending.workspace); |
241 | return; | 248 | return; |
242 | } | 249 | } |
243 | struct sway_workspace *old_workspace = container->workspace; | 250 | struct sway_workspace *old_workspace = container->pending.workspace; |
244 | 251 | ||
245 | container_detach(container); | 252 | container_detach(container); |
246 | container->width = container->height = 0; | 253 | container->pending.width = container->pending.height = 0; |
247 | container->width_fraction = container->height_fraction = 0; | 254 | container->width_fraction = container->height_fraction = 0; |
248 | 255 | ||
249 | if (destination->view) { | 256 | if (destination->view) { |
@@ -256,12 +263,12 @@ static void container_move_to_container(struct sway_container *container, | |||
256 | ipc_event_window(container, "move"); | 263 | ipc_event_window(container, "move"); |
257 | } | 264 | } |
258 | 265 | ||
259 | if (destination->workspace) { | 266 | if (destination->pending.workspace) { |
260 | workspace_focus_fullscreen(destination->workspace); | 267 | workspace_focus_fullscreen(destination->pending.workspace); |
261 | workspace_detect_urgent(destination->workspace); | 268 | workspace_detect_urgent(destination->pending.workspace); |
262 | } | 269 | } |
263 | 270 | ||
264 | if (old_workspace && old_workspace != destination->workspace) { | 271 | if (old_workspace && old_workspace != destination->pending.workspace) { |
265 | workspace_detect_urgent(old_workspace); | 272 | workspace_detect_urgent(old_workspace); |
266 | } | 273 | } |
267 | } | 274 | } |
@@ -275,7 +282,7 @@ static bool container_move_to_next_output(struct sway_container *container, | |||
275 | if (!sway_assert(ws, "Expected output to have a workspace")) { | 282 | if (!sway_assert(ws, "Expected output to have a workspace")) { |
276 | return false; | 283 | return false; |
277 | } | 284 | } |
278 | switch (container->fullscreen_mode) { | 285 | switch (container->pending.fullscreen_mode) { |
279 | case FULLSCREEN_NONE: | 286 | case FULLSCREEN_NONE: |
280 | container_move_to_workspace_from_direction(container, ws, move_dir); | 287 | container_move_to_workspace_from_direction(container, ws, move_dir); |
281 | return true; | 288 | return true; |
@@ -293,12 +300,12 @@ static bool container_move_to_next_output(struct sway_container *container, | |||
293 | static bool container_move_in_direction(struct sway_container *container, | 300 | static bool container_move_in_direction(struct sway_container *container, |
294 | enum wlr_direction move_dir) { | 301 | enum wlr_direction move_dir) { |
295 | // If moving a fullscreen view, only consider outputs | 302 | // If moving a fullscreen view, only consider outputs |
296 | switch (container->fullscreen_mode) { | 303 | switch (container->pending.fullscreen_mode) { |
297 | case FULLSCREEN_NONE: | 304 | case FULLSCREEN_NONE: |
298 | break; | 305 | break; |
299 | case FULLSCREEN_WORKSPACE: | 306 | case FULLSCREEN_WORKSPACE: |
300 | return container_move_to_next_output(container, | 307 | return container_move_to_next_output(container, |
301 | container->workspace->output, move_dir); | 308 | container->pending.workspace->output, move_dir); |
302 | case FULLSCREEN_GLOBAL: | 309 | case FULLSCREEN_GLOBAL: |
303 | return false; | 310 | return false; |
304 | } | 311 | } |
@@ -317,26 +324,26 @@ static bool container_move_in_direction(struct sway_container *container, | |||
317 | while (!ancestor) { | 324 | while (!ancestor) { |
318 | // Don't allow containers to move out of their | 325 | // Don't allow containers to move out of their |
319 | // fullscreen or floating parent | 326 | // fullscreen or floating parent |
320 | if (current->fullscreen_mode || container_is_floating(current)) { | 327 | if (current->pending.fullscreen_mode || container_is_floating(current)) { |
321 | return false; | 328 | return false; |
322 | } | 329 | } |
323 | 330 | ||
324 | enum sway_container_layout parent_layout = container_parent_layout(current); | 331 | enum sway_container_layout parent_layout = container_parent_layout(current); |
325 | if (!is_parallel(parent_layout, move_dir)) { | 332 | if (!is_parallel(parent_layout, move_dir)) { |
326 | if (!current->parent) { | 333 | if (!current->pending.parent) { |
327 | // No parallel parent, so we reorient the workspace | 334 | // No parallel parent, so we reorient the workspace |
328 | current = workspace_wrap_children(current->workspace); | 335 | current = workspace_wrap_children(current->pending.workspace); |
329 | current->workspace->layout = | 336 | current->pending.workspace->layout = |
330 | move_dir == WLR_DIRECTION_LEFT || | 337 | move_dir == WLR_DIRECTION_LEFT || |
331 | move_dir == WLR_DIRECTION_RIGHT ? | 338 | move_dir == WLR_DIRECTION_RIGHT ? |
332 | L_HORIZ : L_VERT; | 339 | L_HORIZ : L_VERT; |
333 | container->height = container->width = 0; | 340 | container->pending.height = container->pending.width = 0; |
334 | container->height_fraction = container->width_fraction = 0; | 341 | container->height_fraction = container->width_fraction = 0; |
335 | workspace_update_representation(current->workspace); | 342 | workspace_update_representation(current->pending.workspace); |
336 | wrapped = true; | 343 | wrapped = true; |
337 | } else { | 344 | } else { |
338 | // Keep looking for a parallel parent | 345 | // Keep looking for a parallel parent |
339 | current = current->parent; | 346 | current = current->pending.parent; |
340 | } | 347 | } |
341 | continue; | 348 | continue; |
342 | } | 349 | } |
@@ -356,14 +363,14 @@ static bool container_move_in_direction(struct sway_container *container, | |||
356 | container_move_to_container_from_direction(container, | 363 | container_move_to_container_from_direction(container, |
357 | target, move_dir); | 364 | target, move_dir); |
358 | return true; | 365 | return true; |
359 | } else if (!container->parent) { | 366 | } else if (!container->pending.parent) { |
360 | // Container is at workspace level so we move it to the | 367 | // Container is at workspace level so we move it to the |
361 | // next workspace if possible | 368 | // next workspace if possible |
362 | return container_move_to_next_output(container, | 369 | return container_move_to_next_output(container, |
363 | current->workspace->output, move_dir); | 370 | current->pending.workspace->output, move_dir); |
364 | } else { | 371 | } else { |
365 | // Container has escaped its immediate parallel parent | 372 | // Container has escaped its immediate parallel parent |
366 | current = current->parent; | 373 | current = current->pending.parent; |
367 | continue; | 374 | continue; |
368 | } | 375 | } |
369 | } | 376 | } |
@@ -377,31 +384,31 @@ static bool container_move_in_direction(struct sway_container *container, | |||
377 | container_move_to_container_from_direction(container, | 384 | container_move_to_container_from_direction(container, |
378 | target, move_dir); | 385 | target, move_dir); |
379 | return true; | 386 | return true; |
380 | } else if (!wrapped && !container->parent->parent && | 387 | } else if (!wrapped && !container->pending.parent->pending.parent && |
381 | container->parent->children->length == 1) { | 388 | container->pending.parent->pending.children->length == 1) { |
382 | // Treat singleton children as if they are at workspace level like i3 | 389 | // Treat singleton children as if they are at workspace level like i3 |
383 | // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367 | 390 | // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367 |
384 | return container_move_to_next_output(container, | 391 | return container_move_to_next_output(container, |
385 | ancestor->workspace->output, move_dir); | 392 | ancestor->pending.workspace->output, move_dir); |
386 | } else { | 393 | } else { |
387 | // Container will be promoted | 394 | // Container will be promoted |
388 | struct sway_container *old_parent = container->parent; | 395 | struct sway_container *old_parent = container->pending.parent; |
389 | if (ancestor->parent) { | 396 | if (ancestor->pending.parent) { |
390 | // Container will move in with its parent | 397 | // Container will move in with its parent |
391 | container_insert_child(ancestor->parent, container, | 398 | container_insert_child(ancestor->pending.parent, container, |
392 | index + (offs < 0 ? 0 : 1)); | 399 | index + (offs < 0 ? 0 : 1)); |
393 | } else { | 400 | } else { |
394 | // Container will move to workspace level, | 401 | // Container will move to workspace level, |
395 | // may be re-split by workspace_layout | 402 | // may be re-split by workspace_layout |
396 | workspace_insert_tiling(ancestor->workspace, container, | 403 | workspace_insert_tiling(ancestor->pending.workspace, container, |
397 | index + (offs < 0 ? 0 : 1)); | 404 | index + (offs < 0 ? 0 : 1)); |
398 | } | 405 | } |
399 | ancestor->height = ancestor->width = 0; | 406 | ancestor->pending.height = ancestor->pending.width = 0; |
400 | ancestor->height_fraction = ancestor->width_fraction = 0; | 407 | ancestor->height_fraction = ancestor->width_fraction = 0; |
401 | if (old_parent) { | 408 | if (old_parent) { |
402 | container_reap_empty(old_parent); | 409 | container_reap_empty(old_parent); |
403 | } | 410 | } |
404 | workspace_squash(container->workspace); | 411 | workspace_squash(container->pending.workspace); |
405 | return true; | 412 | return true; |
406 | } | 413 | } |
407 | } | 414 | } |
@@ -427,14 +434,14 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, | |||
427 | container = workspace_wrap_children(workspace); | 434 | container = workspace_wrap_children(workspace); |
428 | } | 435 | } |
429 | 436 | ||
430 | if (container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 437 | if (container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
431 | return cmd_results_new(CMD_FAILURE, | 438 | return cmd_results_new(CMD_FAILURE, |
432 | "Can't move fullscreen global container"); | 439 | "Can't move fullscreen global container"); |
433 | } | 440 | } |
434 | 441 | ||
435 | struct sway_seat *seat = config->handler_context.seat; | 442 | struct sway_seat *seat = config->handler_context.seat; |
436 | struct sway_container *old_parent = container->parent; | 443 | struct sway_container *old_parent = container->pending.parent; |
437 | struct sway_workspace *old_ws = container->workspace; | 444 | struct sway_workspace *old_ws = container->pending.workspace; |
438 | struct sway_output *old_output = old_ws ? old_ws->output : NULL; | 445 | struct sway_output *old_output = old_ws ? old_ws->output : NULL; |
439 | struct sway_node *destination = NULL; | 446 | struct sway_node *destination = NULL; |
440 | 447 | ||
@@ -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, |
@@ -508,7 +515,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, | |||
508 | destination = dst ? &dst->node : &ws->node; | 515 | destination = dst ? &dst->node : &ws->node; |
509 | } else if (strcasecmp(argv[0], "output") == 0) { | 516 | } else if (strcasecmp(argv[0], "output") == 0) { |
510 | struct sway_output *new_output = output_in_direction(argv[1], | 517 | struct sway_output *new_output = output_in_direction(argv[1], |
511 | old_output, container->x, container->y); | 518 | old_output, container->pending.x, container->pending.y); |
512 | if (!new_output) { | 519 | if (!new_output) { |
513 | return cmd_results_new(CMD_FAILURE, | 520 | return cmd_results_new(CMD_FAILURE, |
514 | "Can't find output with name/direction '%s'", argv[1]); | 521 | "Can't find output with name/direction '%s'", argv[1]); |
@@ -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 | ||
@@ -706,12 +716,12 @@ static struct cmd_results *cmd_move_in_direction( | |||
706 | "Cannot move workspaces in a direction"); | 716 | "Cannot move workspaces in a direction"); |
707 | } | 717 | } |
708 | if (container_is_floating(container)) { | 718 | if (container_is_floating(container)) { |
709 | if (container->fullscreen_mode) { | 719 | if (container->pending.fullscreen_mode) { |
710 | return cmd_results_new(CMD_FAILURE, | 720 | return cmd_results_new(CMD_FAILURE, |
711 | "Cannot move fullscreen floating container"); | 721 | "Cannot move fullscreen floating container"); |
712 | } | 722 | } |
713 | double lx = container->x; | 723 | double lx = container->pending.x; |
714 | double ly = container->y; | 724 | double ly = container->pending.y; |
715 | switch (direction) { | 725 | switch (direction) { |
716 | case WLR_DIRECTION_LEFT: | 726 | case WLR_DIRECTION_LEFT: |
717 | lx -= move_amt; | 727 | lx -= move_amt; |
@@ -729,8 +739,8 @@ static struct cmd_results *cmd_move_in_direction( | |||
729 | container_floating_move_to(container, lx, ly); | 739 | container_floating_move_to(container, lx, ly); |
730 | return cmd_results_new(CMD_SUCCESS, NULL); | 740 | return cmd_results_new(CMD_SUCCESS, NULL); |
731 | } | 741 | } |
732 | struct sway_workspace *old_ws = container->workspace; | 742 | struct sway_workspace *old_ws = container->pending.workspace; |
733 | struct sway_container *old_parent = container->parent; | 743 | struct sway_container *old_parent = container->pending.parent; |
734 | 744 | ||
735 | if (!container_move_in_direction(container, direction)) { | 745 | if (!container_move_in_direction(container, direction)) { |
736 | // Container didn't move | 746 | // Container didn't move |
@@ -744,7 +754,7 @@ static struct cmd_results *cmd_move_in_direction( | |||
744 | workspace_consider_destroy(old_ws); | 754 | workspace_consider_destroy(old_ws); |
745 | } | 755 | } |
746 | 756 | ||
747 | struct sway_workspace *new_ws = container->workspace; | 757 | struct sway_workspace *new_ws = container->pending.workspace; |
748 | 758 | ||
749 | if (root->fullscreen_global) { | 759 | if (root->fullscreen_global) { |
750 | arrange_root(); | 760 | arrange_root(); |
@@ -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); |
@@ -781,22 +782,22 @@ static struct cmd_results *cmd_move_to_position_pointer( | |||
781 | } | 782 | } |
782 | struct wlr_cursor *cursor = seat->cursor->cursor; | 783 | struct wlr_cursor *cursor = seat->cursor->cursor; |
783 | /* Determine where to put the window. */ | 784 | /* Determine where to put the window. */ |
784 | double lx = cursor->x - container->width / 2; | 785 | double lx = cursor->x - container->pending.width / 2; |
785 | double ly = cursor->y - container->height / 2; | 786 | double ly = cursor->y - container->pending.height / 2; |
786 | 787 | ||
787 | /* Correct target coordinates to be in bounds (on screen). */ | 788 | /* Correct target coordinates to be in bounds (on screen). */ |
788 | struct wlr_output *output = wlr_output_layout_output_at( | 789 | struct wlr_output *output = wlr_output_layout_output_at( |
789 | root->output_layout, cursor->x, cursor->y); | 790 | root->output_layout, cursor->x, cursor->y); |
790 | if (output) { | 791 | if (output) { |
791 | struct wlr_box *box = | 792 | struct wlr_box box; |
792 | wlr_output_layout_get_box(root->output_layout, output); | 793 | wlr_output_layout_get_box(root->output_layout, output, &box); |
793 | lx = fmax(lx, box->x); | 794 | lx = fmax(lx, box.x); |
794 | ly = fmax(ly, box->y); | 795 | ly = fmax(ly, box.y); |
795 | if (lx + container->width > box->x + box->width) { | 796 | if (lx + container->pending.width > box.x + box.width) { |
796 | lx = box->x + box->width - container->width; | 797 | lx = box.x + box.width - container->pending.width; |
797 | } | 798 | } |
798 | if (ly + container->height > box->y + box->height) { | 799 | if (ly + container->pending.height > box.y + box.height) { |
799 | ly = box->y + box->height - container->height; | 800 | ly = box.y + box.height - container->pending.height; |
800 | } | 801 | } |
801 | } | 802 | } |
802 | 803 | ||
@@ -818,7 +819,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
818 | } | 819 | } |
819 | 820 | ||
820 | if (!argc) { | 821 | if (!argc) { |
821 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 822 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
822 | } | 823 | } |
823 | 824 | ||
824 | bool absolute = false; | 825 | bool absolute = false; |
@@ -828,41 +829,41 @@ 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) { |
847 | double lx, ly; | 848 | double lx, ly; |
848 | if (absolute) { | 849 | if (absolute) { |
849 | lx = root->x + (root->width - container->width) / 2; | 850 | lx = root->x + (root->width - container->pending.width) / 2; |
850 | ly = root->y + (root->height - container->height) / 2; | 851 | ly = root->y + (root->height - container->pending.height) / 2; |
851 | } else { | 852 | } else { |
852 | struct sway_workspace *ws = container->workspace; | 853 | struct sway_workspace *ws = container->pending.workspace; |
853 | if (!ws) { | 854 | if (!ws) { |
854 | struct sway_seat *seat = config->handler_context.seat; | 855 | struct sway_seat *seat = config->handler_context.seat; |
855 | ws = seat_get_focused_workspace(seat); | 856 | ws = seat_get_focused_workspace(seat); |
856 | } | 857 | } |
857 | lx = ws->x + (ws->width - container->width) / 2; | 858 | lx = ws->x + (ws->width - container->pending.width) / 2; |
858 | ly = ws->y + (ws->height - container->height) / 2; | 859 | ly = ws->y + (ws->height - container->pending.height) / 2; |
859 | } | 860 | } |
860 | container_floating_move_to(container, lx, ly); | 861 | container_floating_move_to(container, lx, ly); |
861 | return cmd_results_new(CMD_SUCCESS, NULL); | 862 | return cmd_results_new(CMD_SUCCESS, NULL); |
862 | } | 863 | } |
863 | 864 | ||
864 | if (argc < 2) { | 865 | if (argc < 2) { |
865 | return cmd_results_new(CMD_FAILURE, expected_position_syntax); | 866 | return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); |
866 | } | 867 | } |
867 | 868 | ||
868 | struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; | 869 | struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; |
@@ -874,19 +875,23 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
874 | return cmd_results_new(CMD_INVALID, "Invalid x position specified"); | 875 | return cmd_results_new(CMD_INVALID, "Invalid x position specified"); |
875 | } | 876 | } |
876 | 877 | ||
878 | if (argc < 1) { | ||
879 | return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); | ||
880 | } | ||
881 | |||
877 | struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; | 882 | struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; |
878 | // Y direction | 883 | // Y direction |
879 | num_consumed_args = parse_movement_amount(argc, argv, &ly); | 884 | num_consumed_args = parse_movement_amount(argc, argv, &ly); |
880 | argc -= num_consumed_args; | 885 | argc -= num_consumed_args; |
881 | argv += num_consumed_args; | 886 | argv += num_consumed_args; |
882 | if (argc > 0) { | 887 | if (argc > 0) { |
883 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 888 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
884 | } | 889 | } |
885 | if (ly.unit == MOVEMENT_UNIT_INVALID) { | 890 | if (ly.unit == MOVEMENT_UNIT_INVALID) { |
886 | return cmd_results_new(CMD_INVALID, "Invalid y position specified"); | 891 | return cmd_results_new(CMD_INVALID, "Invalid y position specified"); |
887 | } | 892 | } |
888 | 893 | ||
889 | struct sway_workspace *ws = container->workspace; | 894 | struct sway_workspace *ws = container->pending.workspace; |
890 | if (!ws) { | 895 | if (!ws) { |
891 | struct sway_seat *seat = config->handler_context.seat; | 896 | struct sway_seat *seat = config->handler_context.seat; |
892 | ws = seat_get_focused_workspace(seat); | 897 | ws = seat_get_focused_workspace(seat); |
@@ -960,14 +965,14 @@ static struct cmd_results *cmd_move_to_scratchpad(void) { | |||
960 | // If the container is in a floating split container, | 965 | // If the container is in a floating split container, |
961 | // operate on the split container instead of the child. | 966 | // operate on the split container instead of the child. |
962 | if (container_is_floating_or_child(con)) { | 967 | if (container_is_floating_or_child(con)) { |
963 | while (con->parent) { | 968 | while (con->pending.parent) { |
964 | con = con->parent; | 969 | con = con->pending.parent; |
965 | } | 970 | } |
966 | } | 971 | } |
967 | 972 | ||
968 | if (!con->scratchpad) { | 973 | if (!con->scratchpad) { |
969 | root_scratchpad_add_container(con, NULL); | 974 | root_scratchpad_add_container(con, NULL); |
970 | } else if (con->workspace) { | 975 | } else if (con->pending.workspace) { |
971 | root_scratchpad_hide(con); | 976 | root_scratchpad_hide(con); |
972 | } | 977 | } |
973 | return cmd_results_new(CMD_SUCCESS, NULL); | 978 | return cmd_results_new(CMD_SUCCESS, NULL); |
@@ -1026,13 +1031,13 @@ struct cmd_results *cmd_move(int argc, char **argv) { | |||
1026 | } | 1031 | } |
1027 | 1032 | ||
1028 | if (!argc) { | 1033 | if (!argc) { |
1029 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1034 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1030 | } | 1035 | } |
1031 | 1036 | ||
1032 | // Only `move [window|container] [to] workspace` supports | 1037 | // Only `move [window|container] [to] workspace` supports |
1033 | // `--no-auto-back-and-forth` so treat others as invalid syntax | 1038 | // `--no-auto-back-and-forth` so treat others as invalid syntax |
1034 | if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { | 1039 | if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { |
1035 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1040 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1036 | } | 1041 | } |
1037 | 1042 | ||
1038 | if (strcasecmp(argv[0], "workspace") == 0 || | 1043 | if (strcasecmp(argv[0], "workspace") == 0 || |
@@ -1046,5 +1051,5 @@ struct cmd_results *cmd_move(int argc, char **argv) { | |||
1046 | strcasecmp(argv[1], "position") == 0)) { | 1051 | strcasecmp(argv[1], "position") == 0)) { |
1047 | return cmd_move_to_position(argc, argv); | 1052 | return cmd_move_to_position(argc, argv); |
1048 | } | 1053 | } |
1049 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1054 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1050 | } | 1055 | } |
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c index 2001e04f..ccfdec82 100644 --- a/sway/commands/no_focus.c +++ b/sway/commands/no_focus.c | |||
@@ -13,7 +13,7 @@ struct cmd_results *cmd_no_focus(int argc, char **argv) { | |||
13 | char *err_str = NULL; | 13 | char *err_str = NULL; |
14 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | 14 | struct criteria *criteria = criteria_parse(argv[0], &err_str); |
15 | if (!criteria) { | 15 | if (!criteria) { |
16 | error = cmd_results_new(CMD_INVALID, err_str); | 16 | error = cmd_results_new(CMD_INVALID, "%s", err_str); |
17 | free(err_str); | 17 | free(err_str); |
18 | return error; | 18 | return error; |
19 | } | 19 | } |
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index 96e6228e..610cecc6 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c | |||
@@ -37,6 +37,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { | |||
37 | } | 37 | } |
38 | 38 | ||
39 | con->alpha = val; | 39 | con->alpha = val; |
40 | container_damage_whole(con); | 40 | container_update(con); |
41 | |||
41 | return cmd_results_new(CMD_SUCCESS, NULL); | 42 | return cmd_results_new(CMD_SUCCESS, NULL); |
42 | } | 43 | } |
diff --git a/sway/commands/output.c b/sway/commands/output.c index 5186a2ba..5e5d31b3 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c | |||
@@ -6,7 +6,7 @@ | |||
6 | #include "log.h" | 6 | #include "log.h" |
7 | 7 | ||
8 | // must be in order for the bsearch | 8 | // must be in order for the bsearch |
9 | static struct cmd_handler output_handlers[] = { | 9 | static const struct cmd_handler output_handlers[] = { |
10 | { "adaptive_sync", output_cmd_adaptive_sync }, | 10 | { "adaptive_sync", output_cmd_adaptive_sync }, |
11 | { "background", output_cmd_background }, | 11 | { "background", output_cmd_background }, |
12 | { "bg", output_cmd_background }, | 12 | { "bg", output_cmd_background }, |
@@ -15,8 +15,11 @@ static struct cmd_handler output_handlers[] = { | |||
15 | { "enable", output_cmd_enable }, | 15 | { "enable", output_cmd_enable }, |
16 | { "max_render_time", output_cmd_max_render_time }, | 16 | { "max_render_time", output_cmd_max_render_time }, |
17 | { "mode", output_cmd_mode }, | 17 | { "mode", output_cmd_mode }, |
18 | { "modeline", output_cmd_modeline }, | ||
18 | { "pos", output_cmd_position }, | 19 | { "pos", output_cmd_position }, |
19 | { "position", output_cmd_position }, | 20 | { "position", output_cmd_position }, |
21 | { "power", output_cmd_power }, | ||
22 | { "render_bit_depth", output_cmd_render_bit_depth }, | ||
20 | { "res", output_cmd_mode }, | 23 | { "res", output_cmd_mode }, |
21 | { "resolution", output_cmd_mode }, | 24 | { "resolution", output_cmd_mode }, |
22 | { "scale", output_cmd_scale }, | 25 | { "scale", output_cmd_scale }, |
@@ -24,6 +27,7 @@ static struct cmd_handler output_handlers[] = { | |||
24 | { "subpixel", output_cmd_subpixel }, | 27 | { "subpixel", output_cmd_subpixel }, |
25 | { "toggle", output_cmd_toggle }, | 28 | { "toggle", output_cmd_toggle }, |
26 | { "transform", output_cmd_transform }, | 29 | { "transform", output_cmd_transform }, |
30 | { "unplug", output_cmd_unplug }, | ||
27 | }; | 31 | }; |
28 | 32 | ||
29 | struct cmd_results *cmd_output(int argc, char **argv) { | 33 | struct cmd_results *cmd_output(int argc, char **argv) { |
@@ -32,9 +36,9 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
32 | return error; | 36 | return error; |
33 | } | 37 | } |
34 | 38 | ||
35 | // The NOOP-1 output is a dummy output used when there's no outputs | 39 | // The HEADLESS-1 output is a dummy output used when there's no outputs |
36 | // connected. It should never be configured. | 40 | // connected. It should never be configured. |
37 | if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { | 41 | if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) { |
38 | return cmd_results_new(CMD_FAILURE, | 42 | return cmd_results_new(CMD_FAILURE, |
39 | "Refusing to configure the no op output"); | 43 | "Refusing to configure the no op output"); |
40 | } | 44 | } |
@@ -51,7 +55,7 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
51 | if (!sway_output) { | 55 | if (!sway_output) { |
52 | return cmd_results_new(CMD_FAILURE, "Unknown output"); | 56 | return cmd_results_new(CMD_FAILURE, "Unknown output"); |
53 | } | 57 | } |
54 | if (sway_output == root->noop_output) { | 58 | if (sway_output == root->fallback_output) { |
55 | return cmd_results_new(CMD_FAILURE, | 59 | return cmd_results_new(CMD_FAILURE, |
56 | "Refusing to configure the no op output"); | 60 | "Refusing to configure the no op output"); |
57 | } | 61 | } |
@@ -99,15 +103,18 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
99 | 103 | ||
100 | bool background = output->background; | 104 | bool background = output->background; |
101 | 105 | ||
102 | output = store_output_config(output); | 106 | store_output_config(output); |
103 | 107 | ||
104 | // If reloading, the output configs will be applied after reading the | 108 | // If reloading, the output configs will be applied after reading the |
105 | // entire config and before the deferred commands so that an auto generated | 109 | // entire config and before the deferred commands so that an auto generated |
106 | // workspace name is not given to re-enabled outputs. | 110 | // workspace name is not given to re-enabled outputs. |
107 | if (!config->reloading && !config->validating) { | 111 | if (!config->reloading && !config->validating) { |
108 | apply_output_config_to_outputs(output); | 112 | apply_all_output_configs(); |
109 | if (background) { | 113 | if (background) { |
110 | spawn_swaybg(); | 114 | if (!spawn_swaybg()) { |
115 | return cmd_results_new(CMD_FAILURE, | ||
116 | "Failed to apply background configuration"); | ||
117 | } | ||
111 | } | 118 | } |
112 | } | 119 | } |
113 | 120 | ||
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 68ee9fe1..55bd7671 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <libgen.h> | 1 | #include <libgen.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -102,19 +101,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { | |||
102 | } | 101 | } |
103 | 102 | ||
104 | char *conf_path = dirname(conf); | 103 | char *conf_path = dirname(conf); |
105 | char *rel_path = src; | 104 | char *real_src = malloc(strlen(conf_path) + strlen(src) + 2); |
106 | src = malloc(strlen(conf_path) + strlen(src) + 2); | 105 | if (!real_src) { |
107 | if (!src) { | 106 | free(src); |
108 | free(rel_path); | ||
109 | free(conf); | 107 | free(conf); |
110 | sway_log(SWAY_ERROR, "Unable to allocate memory"); | 108 | sway_log(SWAY_ERROR, "Unable to allocate memory"); |
111 | return cmd_results_new(CMD_FAILURE, | 109 | return cmd_results_new(CMD_FAILURE, |
112 | "Unable to allocate resources"); | 110 | "Unable to allocate resources"); |
113 | } | 111 | } |
114 | 112 | ||
115 | sprintf(src, "%s/%s", conf_path, rel_path); | 113 | snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src); |
116 | free(rel_path); | 114 | free(src); |
117 | free(conf); | 115 | free(conf); |
116 | src = real_src; | ||
118 | } | 117 | } |
119 | 118 | ||
120 | bool can_access = access(src, F_OK) != -1; | 119 | bool can_access = access(src, F_OK) != -1; |
@@ -123,7 +122,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { | |||
123 | src); | 122 | src); |
124 | config_add_swaynag_warning("Unable to access background file '%s'", | 123 | config_add_swaynag_warning("Unable to access background file '%s'", |
125 | src); | 124 | src); |
125 | struct cmd_results *result = cmd_results_new(CMD_FAILURE, | ||
126 | "unable to access background file '%s'", src); | ||
126 | free(src); | 127 | free(src); |
128 | return result; | ||
127 | } else { | 129 | } else { |
128 | output->background = src; | 130 | output->background = src; |
129 | output->background_option = strdup(mode); | 131 | output->background_option = strdup(mode); |
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c index 9d75a80e..c7adbd58 100644 --- a/sway/commands/output/dpms.c +++ b/sway/commands/output/dpms.c | |||
@@ -1,22 +1,8 @@ | |||
1 | #include "log.h" | ||
1 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
2 | #include "sway/config.h" | ||
3 | #include "util.h" | ||
4 | 3 | ||
5 | struct cmd_results *output_cmd_dpms(int argc, char **argv) { | 4 | struct cmd_results *output_cmd_dpms(int argc, char **argv) { |
6 | if (!config->handler_context.output_config) { | 5 | sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, " |
7 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | 6 | "use \"output power\" instead"); |
8 | } | 7 | return output_cmd_power(argc, argv); |
9 | if (!argc) { | ||
10 | return cmd_results_new(CMD_INVALID, "Missing dpms argument."); | ||
11 | } | ||
12 | |||
13 | if (parse_boolean(argv[0], true)) { | ||
14 | config->handler_context.output_config->dpms_state = DPMS_ON; | ||
15 | } else { | ||
16 | config->handler_context.output_config->dpms_state = DPMS_OFF; | ||
17 | } | ||
18 | |||
19 | config->handler_context.leftovers.argc = argc - 1; | ||
20 | config->handler_context.leftovers.argv = argv + 1; | ||
21 | return NULL; | ||
22 | } | 8 | } |
diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c index 5b710713..019d625a 100644 --- a/sway/commands/output/mode.c +++ b/sway/commands/output/mode.c | |||
@@ -20,6 +20,9 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) { | |||
20 | output->custom_mode = 0; | 20 | output->custom_mode = 0; |
21 | } | 21 | } |
22 | 22 | ||
23 | // Reset custom modeline, if any | ||
24 | output->drm_mode.type = 0; | ||
25 | |||
23 | char *end; | 26 | char *end; |
24 | output->width = strtol(*argv, &end, 10); | 27 | output->width = strtol(*argv, &end, 10); |
25 | if (*end) { | 28 | if (*end) { |
@@ -58,3 +61,58 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) { | |||
58 | return NULL; | 61 | return NULL; |
59 | } | 62 | } |
60 | 63 | ||
64 | static bool parse_modeline(char **argv, drmModeModeInfo *mode) { | ||
65 | mode->type = DRM_MODE_TYPE_USERDEF; | ||
66 | mode->clock = strtof(argv[0], NULL) * 1000; | ||
67 | mode->hdisplay = strtol(argv[1], NULL, 10); | ||
68 | mode->hsync_start = strtol(argv[2], NULL, 10); | ||
69 | mode->hsync_end = strtol(argv[3], NULL, 10); | ||
70 | mode->htotal = strtol(argv[4], NULL, 10); | ||
71 | mode->vdisplay = strtol(argv[5], NULL, 10); | ||
72 | mode->vsync_start = strtol(argv[6], NULL, 10); | ||
73 | mode->vsync_end = strtol(argv[7], NULL, 10); | ||
74 | mode->vtotal = strtol(argv[8], NULL, 10); | ||
75 | |||
76 | mode->vrefresh = mode->clock * 1000.0 * 1000.0 | ||
77 | / mode->htotal / mode->vtotal; | ||
78 | if (strcasecmp(argv[9], "+hsync") == 0) { | ||
79 | mode->flags |= DRM_MODE_FLAG_PHSYNC; | ||
80 | } else if (strcasecmp(argv[9], "-hsync") == 0) { | ||
81 | mode->flags |= DRM_MODE_FLAG_NHSYNC; | ||
82 | } else { | ||
83 | return false; | ||
84 | } | ||
85 | |||
86 | if (strcasecmp(argv[10], "+vsync") == 0) { | ||
87 | mode->flags |= DRM_MODE_FLAG_PVSYNC; | ||
88 | } else if (strcasecmp(argv[10], "-vsync") == 0) { | ||
89 | mode->flags |= DRM_MODE_FLAG_NVSYNC; | ||
90 | } else { | ||
91 | return false; | ||
92 | } | ||
93 | |||
94 | snprintf(mode->name, sizeof(mode->name), "%dx%d@%d", | ||
95 | mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000); | ||
96 | |||
97 | return true; | ||
98 | } | ||
99 | |||
100 | struct cmd_results *output_cmd_modeline(int argc, char **argv) { | ||
101 | if (!config->handler_context.output_config) { | ||
102 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
103 | } | ||
104 | if (!argc) { | ||
105 | return cmd_results_new(CMD_INVALID, "Missing modeline argument."); | ||
106 | } | ||
107 | |||
108 | struct output_config *output = config->handler_context.output_config; | ||
109 | |||
110 | if (argc != 11 || !parse_modeline(argv, &output->drm_mode)) { | ||
111 | return cmd_results_new(CMD_INVALID, "Invalid modeline"); | ||
112 | } | ||
113 | |||
114 | config->handler_context.leftovers.argc = argc - 12; | ||
115 | config->handler_context.leftovers.argv = argv + 12; | ||
116 | return NULL; | ||
117 | } | ||
118 | |||
diff --git a/sway/commands/output/power.c b/sway/commands/output/power.c new file mode 100644 index 00000000..e6ae2852 --- /dev/null +++ b/sway/commands/output/power.c | |||
@@ -0,0 +1,43 @@ | |||
1 | #include <strings.h> | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/output.h" | ||
5 | #include "util.h" | ||
6 | |||
7 | struct cmd_results *output_cmd_power(int argc, char **argv) { | ||
8 | if (!config->handler_context.output_config) { | ||
9 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
10 | } | ||
11 | if (argc == 0) { | ||
12 | return cmd_results_new(CMD_INVALID, "Missing power argument"); | ||
13 | } | ||
14 | |||
15 | bool current = true; | ||
16 | if (strcasecmp(argv[0], "toggle") == 0) { | ||
17 | const char *oc_name = config->handler_context.output_config->name; | ||
18 | if (strcmp(oc_name, "*") == 0) { | ||
19 | return cmd_results_new(CMD_INVALID, | ||
20 | "Cannot apply toggle to all outputs"); | ||
21 | } | ||
22 | |||
23 | struct sway_output *sway_output = all_output_by_name_or_id(oc_name); | ||
24 | if (!sway_output || !sway_output->wlr_output) { | ||
25 | return cmd_results_new(CMD_FAILURE, | ||
26 | "Cannot apply toggle to unknown output %s", oc_name); | ||
27 | } | ||
28 | |||
29 | if (sway_output->enabled && !sway_output->wlr_output->enabled) { | ||
30 | current = false; | ||
31 | } | ||
32 | } | ||
33 | |||
34 | if (parse_boolean(argv[0], current)) { | ||
35 | config->handler_context.output_config->power = 1; | ||
36 | } else { | ||
37 | config->handler_context.output_config->power = 0; | ||
38 | } | ||
39 | |||
40 | config->handler_context.leftovers.argc = argc - 1; | ||
41 | config->handler_context.leftovers.argv = argv + 1; | ||
42 | return NULL; | ||
43 | } | ||
diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c new file mode 100644 index 00000000..c419321e --- /dev/null +++ b/sway/commands/output/render_bit_depth.c | |||
@@ -0,0 +1,29 @@ | |||
1 | #include <drm_fourcc.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/config.h" | ||
5 | |||
6 | struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) { | ||
7 | if (!config->handler_context.output_config) { | ||
8 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
9 | } | ||
10 | if (!argc) { | ||
11 | return cmd_results_new(CMD_INVALID, "Missing bit depth argument."); | ||
12 | } | ||
13 | |||
14 | if (strcmp(*argv, "8") == 0) { | ||
15 | config->handler_context.output_config->render_bit_depth = | ||
16 | RENDER_BIT_DEPTH_8; | ||
17 | } else if (strcmp(*argv, "10") == 0) { | ||
18 | config->handler_context.output_config->render_bit_depth = | ||
19 | RENDER_BIT_DEPTH_10; | ||
20 | } else { | ||
21 | return cmd_results_new(CMD_INVALID, | ||
22 | "Invalid bit depth. Must be a value in (8|10)."); | ||
23 | } | ||
24 | |||
25 | config->handler_context.leftovers.argc = argc - 1; | ||
26 | config->handler_context.leftovers.argv = argv + 1; | ||
27 | return NULL; | ||
28 | } | ||
29 | |||
diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c index 6342d526..c6b72845 100644 --- a/sway/commands/output/toggle.c +++ b/sway/commands/output/toggle.c | |||
@@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) { | |||
29 | config->handler_context.output_config->enabled = 1; | 29 | config->handler_context.output_config->enabled = 1; |
30 | } | 30 | } |
31 | 31 | ||
32 | free(oc); | 32 | free_output_config(oc); |
33 | config->handler_context.leftovers.argc = argc; | 33 | config->handler_context.leftovers.argc = argc; |
34 | config->handler_context.leftovers.argv = argv; | 34 | config->handler_context.leftovers.argv = argv; |
35 | return NULL; | 35 | return NULL; |
diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c index f4fcc8c9..8db71bb3 100644 --- a/sway/commands/output/transform.c +++ b/sway/commands/output/transform.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <wlr/util/transform.h> | ||
2 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
3 | #include "sway/config.h" | 4 | #include "sway/config.h" |
4 | #include "log.h" | 5 | #include "log.h" |
diff --git a/sway/commands/output/unplug.c b/sway/commands/output/unplug.c new file mode 100644 index 00000000..dfef626f --- /dev/null +++ b/sway/commands/output/unplug.c | |||
@@ -0,0 +1,54 @@ | |||
1 | #include <strings.h> | ||
2 | #include <wlr/config.h> | ||
3 | #include <wlr/backend/headless.h> | ||
4 | #include <wlr/backend/wayland.h> | ||
5 | #if WLR_HAS_X11_BACKEND | ||
6 | #include <wlr/backend/x11.h> | ||
7 | #endif | ||
8 | #include "sway/commands.h" | ||
9 | #include "sway/config.h" | ||
10 | #include "sway/output.h" | ||
11 | |||
12 | static bool is_backend_allowed(struct wlr_backend *backend) { | ||
13 | if (wlr_backend_is_headless(backend)) { | ||
14 | return true; | ||
15 | } | ||
16 | if (wlr_backend_is_wl(backend)) { | ||
17 | return true; | ||
18 | } | ||
19 | #if WLR_HAS_X11_BACKEND | ||
20 | if (wlr_backend_is_x11(backend)) { | ||
21 | return true; | ||
22 | } | ||
23 | #endif | ||
24 | return false; | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * This command is intended for developer use only. | ||
29 | */ | ||
30 | struct cmd_results *output_cmd_unplug(int argc, char **argv) { | ||
31 | if (!config->handler_context.output_config) { | ||
32 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
33 | } | ||
34 | |||
35 | const char *oc_name = config->handler_context.output_config->name; | ||
36 | if (strcmp(oc_name, "*") == 0) { | ||
37 | return cmd_results_new(CMD_INVALID, "Won't unplug all outputs"); | ||
38 | } | ||
39 | |||
40 | struct sway_output *sway_output = all_output_by_name_or_id(oc_name); | ||
41 | if (!sway_output) { | ||
42 | return cmd_results_new(CMD_INVALID, | ||
43 | "Cannot unplug unknown output %s", oc_name); | ||
44 | } | ||
45 | |||
46 | if (!is_backend_allowed(sway_output->wlr_output->backend)) { | ||
47 | return cmd_results_new(CMD_INVALID, | ||
48 | "Can only unplug outputs with headless, wayland or x11 backend"); | ||
49 | } | ||
50 | |||
51 | wlr_output_destroy(sway_output->wlr_output); | ||
52 | |||
53 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
54 | } | ||
diff --git a/sway/commands/primary_selection.c b/sway/commands/primary_selection.c new file mode 100644 index 00000000..9e2689c2 --- /dev/null +++ b/sway/commands/primary_selection.c | |||
@@ -0,0 +1,25 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/commands.h" | ||
5 | #include "util.h" | ||
6 | |||
7 | struct cmd_results *cmd_primary_selection(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "primary_selection", EXPECTED_EQUAL_TO, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | |||
13 | bool primary_selection = parse_boolean(argv[0], true); | ||
14 | |||
15 | // config->primary_selection is reset to the previous value on reload in | ||
16 | // load_main_config() | ||
17 | if (config->reloading && config->primary_selection != primary_selection) { | ||
18 | return cmd_results_new(CMD_FAILURE, | ||
19 | "primary_selection can only be enabled/disabled at launch"); | ||
20 | } | ||
21 | |||
22 | config->primary_selection = primary_selection; | ||
23 | |||
24 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
25 | } | ||
diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 3c994d54..6c0aac26 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -9,9 +8,8 @@ | |||
9 | #include "list.h" | 8 | #include "list.h" |
10 | #include "log.h" | 9 | #include "log.h" |
11 | 10 | ||
12 | static void rebuild_textures_iterator(struct sway_container *con, void *data) { | 11 | static void title_bar_update_iterator(struct sway_container *con, void *data) { |
13 | container_update_marks_textures(con); | 12 | container_update_title_bar(con); |
14 | container_update_title_textures(con); | ||
15 | } | 13 | } |
16 | 14 | ||
17 | static void do_reload(void *data) { | 15 | static void do_reload(void *data) { |
@@ -48,8 +46,7 @@ static void do_reload(void *data) { | |||
48 | } | 46 | } |
49 | list_free_items_and_destroy(bar_ids); | 47 | list_free_items_and_destroy(bar_ids); |
50 | 48 | ||
51 | config_update_font_height(true); | 49 | root_for_each_container(title_bar_update_iterator, NULL); |
52 | root_for_each_container(rebuild_textures_iterator, NULL); | ||
53 | 50 | ||
54 | arrange_root(); | 51 | arrange_root(); |
55 | } | 52 | } |
diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 3b855fdf..0d36cc21 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "sway/config.h" | 7 | #include "sway/config.h" |
8 | #include "sway/ipc-server.h" | 8 | #include "sway/ipc-server.h" |
9 | #include "sway/output.h" | 9 | #include "sway/output.h" |
10 | #include "sway/desktop/launcher.h" | ||
10 | #include "sway/tree/container.h" | 11 | #include "sway/tree/container.h" |
11 | #include "sway/tree/workspace.h" | 12 | #include "sway/tree/workspace.h" |
12 | #include "sway/tree/root.h" | 13 | #include "sway/tree/root.h" |
@@ -25,7 +26,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { | |||
25 | "Can't run this command while there's no outputs connected."); | 26 | "Can't run this command while there's no outputs connected."); |
26 | } | 27 | } |
27 | if (strcasecmp(argv[0], "workspace") != 0) { | 28 | if (strcasecmp(argv[0], "workspace") != 0) { |
28 | return cmd_results_new(CMD_INVALID, expected_syntax); | 29 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
29 | } | 30 | } |
30 | 31 | ||
31 | int argn = 1; | 32 | int argn = 1; |
@@ -64,7 +65,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { | |||
64 | ++argn; // move past "to" | 65 | ++argn; // move past "to" |
65 | 66 | ||
66 | if (argn >= argc) { | 67 | if (argn >= argc) { |
67 | return cmd_results_new(CMD_INVALID, expected_syntax); | 68 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
68 | } | 69 | } |
69 | 70 | ||
70 | char *new_name = join_args(argv + argn, argc - argn); | 71 | char *new_name = join_args(argv + argn, argc - argn); |
@@ -91,8 +92,6 @@ struct cmd_results *cmd_rename(int argc, char **argv) { | |||
91 | 92 | ||
92 | sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); | 93 | sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); |
93 | 94 | ||
94 | root_rename_pid_workspaces(workspace->name, new_name); | ||
95 | |||
96 | free(workspace->name); | 95 | free(workspace->name); |
97 | workspace->name = new_name; | 96 | workspace->name = new_name; |
98 | 97 | ||
diff --git a/sway/commands/resize.c b/sway/commands/resize.c index ca36e858..32b746ea 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c | |||
@@ -57,7 +57,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con, | |||
57 | (allow_last || index < siblings->length - 1)) { | 57 | (allow_last || index < siblings->length - 1)) { |
58 | return con; | 58 | return con; |
59 | } | 59 | } |
60 | con = con->parent; | 60 | con = con->pending.parent; |
61 | } | 61 | } |
62 | 62 | ||
63 | return NULL; | 63 | return NULL; |
@@ -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 |
@@ -115,13 +119,13 @@ void container_resize_tiled(struct sway_container *con, | |||
115 | int sibling_amount = prev ? ceil((double)amount / 2.0) : amount; | 119 | int sibling_amount = prev ? ceil((double)amount / 2.0) : amount; |
116 | 120 | ||
117 | if (is_horizontal(axis)) { | 121 | if (is_horizontal(axis)) { |
118 | if (con->width + amount < MIN_SANE_W) { | 122 | if (con->pending.width + amount < MIN_SANE_W) { |
119 | return; | 123 | return; |
120 | } | 124 | } |
121 | if (next->width - sibling_amount < MIN_SANE_W) { | 125 | if (next->pending.width - sibling_amount < MIN_SANE_W) { |
122 | return; | 126 | return; |
123 | } | 127 | } |
124 | if (prev && prev->width - sibling_amount < MIN_SANE_W) { | 128 | if (prev && prev->pending.width - sibling_amount < MIN_SANE_W) { |
125 | return; | 129 | return; |
126 | } | 130 | } |
127 | if (con->child_total_width <= 0) { | 131 | if (con->child_total_width <= 0) { |
@@ -133,7 +137,7 @@ void container_resize_tiled(struct sway_container *con, | |||
133 | list_t *siblings = container_get_siblings(con); | 137 | list_t *siblings = container_get_siblings(con); |
134 | for (int i = 0; i < siblings->length; ++i) { | 138 | for (int i = 0; i < siblings->length; ++i) { |
135 | struct sway_container *con = siblings->items[i]; | 139 | struct sway_container *con = siblings->items[i]; |
136 | con->width_fraction = con->width / con->child_total_width; | 140 | con->width_fraction = con->pending.width / con->child_total_width; |
137 | } | 141 | } |
138 | 142 | ||
139 | double amount_fraction = (double)amount / con->child_total_width; | 143 | double amount_fraction = (double)amount / con->child_total_width; |
@@ -146,13 +150,13 @@ void container_resize_tiled(struct sway_container *con, | |||
146 | prev->width_fraction -= sibling_amount_fraction; | 150 | prev->width_fraction -= sibling_amount_fraction; |
147 | } | 151 | } |
148 | } else { | 152 | } else { |
149 | if (con->height + amount < MIN_SANE_H) { | 153 | if (con->pending.height + amount < MIN_SANE_H) { |
150 | return; | 154 | return; |
151 | } | 155 | } |
152 | if (next->height - sibling_amount < MIN_SANE_H) { | 156 | if (next->pending.height - sibling_amount < MIN_SANE_H) { |
153 | return; | 157 | return; |
154 | } | 158 | } |
155 | if (prev && prev->height - sibling_amount < MIN_SANE_H) { | 159 | if (prev && prev->pending.height - sibling_amount < MIN_SANE_H) { |
156 | return; | 160 | return; |
157 | } | 161 | } |
158 | if (con->child_total_height <= 0) { | 162 | if (con->child_total_height <= 0) { |
@@ -164,7 +168,7 @@ void container_resize_tiled(struct sway_container *con, | |||
164 | list_t *siblings = container_get_siblings(con); | 168 | list_t *siblings = container_get_siblings(con); |
165 | for (int i = 0; i < siblings->length; ++i) { | 169 | for (int i = 0; i < siblings->length; ++i) { |
166 | struct sway_container *con = siblings->items[i]; | 170 | struct sway_container *con = siblings->items[i]; |
167 | con->height_fraction = con->height / con->child_total_height; | 171 | con->height_fraction = con->pending.height / con->child_total_height; |
168 | } | 172 | } |
169 | 173 | ||
170 | double amount_fraction = (double)amount / con->child_total_height; | 174 | double amount_fraction = (double)amount / con->child_total_height; |
@@ -178,10 +182,10 @@ void container_resize_tiled(struct sway_container *con, | |||
178 | } | 182 | } |
179 | } | 183 | } |
180 | 184 | ||
181 | if (con->parent) { | 185 | if (con->pending.parent) { |
182 | arrange_container(con->parent); | 186 | arrange_container(con->pending.parent); |
183 | } else { | 187 | } else { |
184 | arrange_workspace(con->workspace); | 188 | arrange_workspace(con->pending.workspace); |
185 | } | 189 | } |
186 | } | 190 | } |
187 | 191 | ||
@@ -203,15 +207,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis, | |||
203 | int min_width, max_width, min_height, max_height; | 207 | int min_width, max_width, min_height, max_height; |
204 | floating_calculate_constraints(&min_width, &max_width, | 208 | floating_calculate_constraints(&min_width, &max_width, |
205 | &min_height, &max_height); | 209 | &min_height, &max_height); |
206 | if (con->width + grow_width < min_width) { | 210 | if (con->pending.width + grow_width < min_width) { |
207 | grow_width = min_width - con->width; | 211 | grow_width = min_width - con->pending.width; |
208 | } else if (con->width + grow_width > max_width) { | 212 | } else if (con->pending.width + grow_width > max_width) { |
209 | grow_width = max_width - con->width; | 213 | grow_width = max_width - con->pending.width; |
210 | } | 214 | } |
211 | if (con->height + grow_height < min_height) { | 215 | if (con->pending.height + grow_height < min_height) { |
212 | grow_height = min_height - con->height; | 216 | grow_height = min_height - con->pending.height; |
213 | } else if (con->height + grow_height > max_height) { | 217 | } else if (con->pending.height + grow_height > max_height) { |
214 | grow_height = max_height - con->height; | 218 | grow_height = max_height - con->pending.height; |
215 | } | 219 | } |
216 | int grow_x = 0, grow_y = 0; | 220 | int grow_x = 0, grow_y = 0; |
217 | 221 | ||
@@ -227,15 +231,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis, | |||
227 | if (grow_width == 0 && grow_height == 0) { | 231 | if (grow_width == 0 && grow_height == 0) { |
228 | return cmd_results_new(CMD_INVALID, "Cannot resize any further"); | 232 | return cmd_results_new(CMD_INVALID, "Cannot resize any further"); |
229 | } | 233 | } |
230 | con->x += grow_x; | 234 | con->pending.x += grow_x; |
231 | con->y += grow_y; | 235 | con->pending.y += grow_y; |
232 | con->width += grow_width; | 236 | con->pending.width += grow_width; |
233 | con->height += grow_height; | 237 | con->pending.height += grow_height; |
234 | 238 | ||
235 | con->content_x += grow_x; | 239 | con->pending.content_x += grow_x; |
236 | con->content_y += grow_y; | 240 | con->pending.content_y += grow_y; |
237 | con->content_width += grow_width; | 241 | con->pending.content_width += grow_width; |
238 | con->content_height += grow_height; | 242 | con->pending.content_height += grow_height; |
239 | 243 | ||
240 | arrange_container(con); | 244 | arrange_container(con); |
241 | 245 | ||
@@ -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->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->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,24 +300,29 @@ 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) { |
283 | // Convert to px | 311 | // Convert to px |
284 | struct sway_container *parent = con->parent; | 312 | struct sway_container *parent = con->pending.parent; |
285 | while (parent && parent->layout != L_HORIZ) { | 313 | while (parent && parent->pending.layout != L_HORIZ) { |
286 | parent = parent->parent; | 314 | parent = parent->pending.parent; |
287 | } | 315 | } |
288 | if (parent) { | 316 | if (parent) { |
289 | width->amount = parent->width * width->amount / 100; | 317 | width->amount = parent->pending.width * width->amount / 100; |
290 | } else { | 318 | } else { |
291 | width->amount = con->workspace->width * width->amount / 100; | 319 | width->amount = con->pending.workspace->width * width->amount / 100; |
292 | } | 320 | } |
293 | width->unit = MOVEMENT_UNIT_PX; | 321 | width->unit = MOVEMENT_UNIT_PX; |
294 | } | 322 | } |
295 | if (width->unit == MOVEMENT_UNIT_PX) { | 323 | if (width->unit == MOVEMENT_UNIT_PX) { |
296 | container_resize_tiled(con, AXIS_HORIZONTAL, | 324 | container_resize_tiled(con, AXIS_HORIZONTAL, |
297 | width->amount - con->width); | 325 | width->amount - con->pending.width); |
298 | } | 326 | } |
299 | } | 327 | } |
300 | 328 | ||
@@ -302,20 +330,20 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, | |||
302 | if (height->unit == MOVEMENT_UNIT_PPT || | 330 | if (height->unit == MOVEMENT_UNIT_PPT || |
303 | height->unit == MOVEMENT_UNIT_DEFAULT) { | 331 | height->unit == MOVEMENT_UNIT_DEFAULT) { |
304 | // Convert to px | 332 | // Convert to px |
305 | struct sway_container *parent = con->parent; | 333 | struct sway_container *parent = con->pending.parent; |
306 | while (parent && parent->layout != L_VERT) { | 334 | while (parent && parent->pending.layout != L_VERT) { |
307 | parent = parent->parent; | 335 | parent = parent->pending.parent; |
308 | } | 336 | } |
309 | if (parent) { | 337 | if (parent) { |
310 | height->amount = parent->height * height->amount / 100; | 338 | height->amount = parent->pending.height * height->amount / 100; |
311 | } else { | 339 | } else { |
312 | height->amount = con->workspace->height * height->amount / 100; | 340 | height->amount = con->pending.workspace->height * height->amount / 100; |
313 | } | 341 | } |
314 | height->unit = MOVEMENT_UNIT_PX; | 342 | height->unit = MOVEMENT_UNIT_PX; |
315 | } | 343 | } |
316 | if (height->unit == MOVEMENT_UNIT_PX) { | 344 | if (height->unit == MOVEMENT_UNIT_PX) { |
317 | container_resize_tiled(con, AXIS_VERTICAL, | 345 | container_resize_tiled(con, AXIS_VERTICAL, |
318 | height->amount - con->height); | 346 | height->amount - con->pending.height); |
319 | } | 347 | } |
320 | } | 348 | } |
321 | 349 | ||
@@ -339,15 +367,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, | |||
339 | "Cannot resize a hidden scratchpad container by ppt"); | 367 | "Cannot resize a hidden scratchpad container by ppt"); |
340 | } | 368 | } |
341 | // Convert to px | 369 | // Convert to px |
342 | width->amount = con->workspace->width * width->amount / 100; | 370 | width->amount = con->pending.workspace->width * width->amount / 100; |
343 | width->unit = MOVEMENT_UNIT_PX; | 371 | width->unit = MOVEMENT_UNIT_PX; |
344 | // Falls through | 372 | // Falls through |
345 | case MOVEMENT_UNIT_PX: | 373 | case MOVEMENT_UNIT_PX: |
346 | case MOVEMENT_UNIT_DEFAULT: | 374 | case MOVEMENT_UNIT_DEFAULT: |
347 | width->amount = fmax(min_width, fmin(width->amount, max_width)); | 375 | width->amount = fmax(min_width, fmin(width->amount, max_width)); |
348 | grow_width = width->amount - con->width; | 376 | grow_width = width->amount - con->pending.width; |
349 | con->x -= grow_width / 2; | 377 | con->pending.x -= grow_width / 2; |
350 | con->width = width->amount; | 378 | con->pending.width = width->amount; |
351 | break; | 379 | break; |
352 | case MOVEMENT_UNIT_INVALID: | 380 | case MOVEMENT_UNIT_INVALID: |
353 | sway_assert(false, "invalid width unit"); | 381 | sway_assert(false, "invalid width unit"); |
@@ -363,15 +391,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, | |||
363 | "Cannot resize a hidden scratchpad container by ppt"); | 391 | "Cannot resize a hidden scratchpad container by ppt"); |
364 | } | 392 | } |
365 | // Convert to px | 393 | // Convert to px |
366 | height->amount = con->workspace->height * height->amount / 100; | 394 | height->amount = con->pending.workspace->height * height->amount / 100; |
367 | height->unit = MOVEMENT_UNIT_PX; | 395 | height->unit = MOVEMENT_UNIT_PX; |
368 | // Falls through | 396 | // Falls through |
369 | case MOVEMENT_UNIT_PX: | 397 | case MOVEMENT_UNIT_PX: |
370 | case MOVEMENT_UNIT_DEFAULT: | 398 | case MOVEMENT_UNIT_DEFAULT: |
371 | height->amount = fmax(min_height, fmin(height->amount, max_height)); | 399 | height->amount = fmax(min_height, fmin(height->amount, max_height)); |
372 | grow_height = height->amount - con->height; | 400 | grow_height = height->amount - con->pending.height; |
373 | con->y -= grow_height / 2; | 401 | con->pending.y -= grow_height / 2; |
374 | con->height = height->amount; | 402 | con->pending.height = height->amount; |
375 | break; | 403 | break; |
376 | case MOVEMENT_UNIT_INVALID: | 404 | case MOVEMENT_UNIT_INVALID: |
377 | sway_assert(false, "invalid height unit"); | 405 | sway_assert(false, "invalid height unit"); |
@@ -379,10 +407,10 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, | |||
379 | } | 407 | } |
380 | } | 408 | } |
381 | 409 | ||
382 | con->content_x -= grow_width / 2; | 410 | con->pending.content_x -= grow_width / 2; |
383 | con->content_y -= grow_height / 2; | 411 | con->pending.content_y -= grow_height / 2; |
384 | con->content_width += grow_width; | 412 | con->pending.content_width += grow_width; |
385 | con->content_height += grow_height; | 413 | con->pending.content_height += grow_height; |
386 | 414 | ||
387 | arrange_container(con); | 415 | arrange_container(con); |
388 | 416 | ||
@@ -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,20 +455,20 @@ 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 | ||
437 | // If 0, don't resize that dimension | 465 | // If 0, don't resize that dimension |
438 | struct sway_container *con = config->handler_context.container; | 466 | struct sway_container *con = config->handler_context.container; |
439 | if (width.amount <= 0) { | 467 | if (width.amount <= 0) { |
440 | width.amount = con->width; | 468 | width.amount = con->pending.width; |
441 | } | 469 | } |
442 | if (height.amount <= 0) { | 470 | if (height.amount <= 0) { |
443 | height.amount = con->height; | 471 | height.amount = con->pending.height; |
444 | } | 472 | } |
445 | 473 | ||
446 | if (container_is_floating(con)) { | 474 | if (container_is_floating(con)) { |
@@ -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/scratchpad.c b/sway/commands/scratchpad.c index 34871bc6..c995f2f0 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c | |||
@@ -21,8 +21,8 @@ static void scratchpad_toggle_auto(void) { | |||
21 | // If the focus is in a floating split container, | 21 | // If the focus is in a floating split container, |
22 | // operate on the split container instead of the child. | 22 | // operate on the split container instead of the child. |
23 | if (focus && container_is_floating_or_child(focus)) { | 23 | if (focus && container_is_floating_or_child(focus)) { |
24 | while (focus->parent) { | 24 | while (focus->pending.parent) { |
25 | focus = focus->parent; | 25 | focus = focus->pending.parent; |
26 | } | 26 | } |
27 | } | 27 | } |
28 | 28 | ||
@@ -52,7 +52,7 @@ static void scratchpad_toggle_auto(void) { | |||
52 | // In this case we move it to the current workspace. | 52 | // In this case we move it to the current workspace. |
53 | for (int i = 0; i < root->scratchpad->length; ++i) { | 53 | for (int i = 0; i < root->scratchpad->length; ++i) { |
54 | struct sway_container *con = root->scratchpad->items[i]; | 54 | struct sway_container *con = root->scratchpad->items[i]; |
55 | if (con->parent) { | 55 | if (con->pending.parent) { |
56 | sway_log(SWAY_DEBUG, | 56 | sway_log(SWAY_DEBUG, |
57 | "Moving a visible scratchpad window (%s) to this workspace", | 57 | "Moving a visible scratchpad window (%s) to this workspace", |
58 | con->title); | 58 | con->title); |
@@ -80,7 +80,7 @@ static void scratchpad_toggle_container(struct sway_container *con) { | |||
80 | struct sway_seat *seat = input_manager_current_seat(); | 80 | struct sway_seat *seat = input_manager_current_seat(); |
81 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | 81 | struct sway_workspace *ws = seat_get_focused_workspace(seat); |
82 | // Check if it matches a currently visible scratchpad window and hide it. | 82 | // Check if it matches a currently visible scratchpad window and hide it. |
83 | if (con->workspace && ws == con->workspace) { | 83 | if (con->pending.workspace && ws == con->pending.workspace) { |
84 | root_scratchpad_hide(con); | 84 | root_scratchpad_hide(con); |
85 | return; | 85 | return; |
86 | } | 86 | } |
@@ -105,21 +105,22 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) { | |||
105 | return cmd_results_new(CMD_INVALID, "Scratchpad is empty"); | 105 | return cmd_results_new(CMD_INVALID, "Scratchpad is empty"); |
106 | } | 106 | } |
107 | 107 | ||
108 | if (config->handler_context.using_criteria) { | 108 | if (config->handler_context.node_overridden) { |
109 | struct sway_container *con = config->handler_context.container; | 109 | struct sway_container *con = config->handler_context.container; |
110 | 110 | ||
111 | // If the container is in a floating split container, | 111 | // If the container is in a floating split container, |
112 | // operate on the split container instead of the child. | 112 | // operate on the split container instead of the child. |
113 | if (container_is_floating_or_child(con)) { | 113 | if (con && container_is_floating_or_child(con)) { |
114 | while (con->parent) { | 114 | while (con->pending.parent) { |
115 | con = con->parent; | 115 | con = con->pending.parent; |
116 | } | 116 | } |
117 | } | 117 | } |
118 | 118 | ||
119 | // If using criteria, this command is executed for every container which | 119 | // If using criteria, this command is executed for every container which |
120 | // matches the criteria. If this container isn't in the scratchpad, | 120 | // matches the criteria. If this container isn't in the scratchpad, |
121 | // we'll just silently return a success. | 121 | // we'll just silently return a success. The same is true if the |
122 | if (!con->scratchpad) { | 122 | // overridden node is not a container. |
123 | if (!con || !con->scratchpad) { | ||
123 | return cmd_results_new(CMD_SUCCESS, NULL); | 124 | return cmd_results_new(CMD_SUCCESS, NULL); |
124 | } | 125 | } |
125 | scratchpad_toggle_container(con); | 126 | scratchpad_toggle_container(con); |
diff --git a/sway/commands/seat.c b/sway/commands/seat.c index 84c6ba53..2d197b69 100644 --- a/sway/commands/seat.c +++ b/sway/commands/seat.c | |||
@@ -8,13 +8,13 @@ | |||
8 | 8 | ||
9 | // must be in order for the bsearch | 9 | // must be in order for the bsearch |
10 | // these handlers perform actions on the seat | 10 | // these handlers perform actions on the seat |
11 | static struct cmd_handler seat_action_handlers[] = { | 11 | static const struct cmd_handler seat_action_handlers[] = { |
12 | { "cursor", seat_cmd_cursor }, | 12 | { "cursor", seat_cmd_cursor }, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | // must be in order for the bsearch | 15 | // must be in order for the bsearch |
16 | // these handlers alter the seat config | 16 | // these handlers alter the seat config |
17 | static struct cmd_handler seat_handlers[] = { | 17 | static const struct cmd_handler seat_handlers[] = { |
18 | { "attach", seat_cmd_attach }, | 18 | { "attach", seat_cmd_attach }, |
19 | { "fallback", seat_cmd_fallback }, | 19 | { "fallback", seat_cmd_fallback }, |
20 | { "hide_cursor", seat_cmd_hide_cursor }, | 20 | { "hide_cursor", seat_cmd_hide_cursor }, |
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c index 7615eef9..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" |
@@ -12,7 +11,7 @@ struct cmd_results *seat_cmd_attach(int argc, char **argv) { | |||
12 | if (!config->handler_context.seat_config) { | 11 | if (!config->handler_context.seat_config) { |
13 | return cmd_results_new(CMD_FAILURE, "No seat defined"); | 12 | return cmd_results_new(CMD_FAILURE, "No seat defined"); |
14 | } | 13 | } |
15 | if (config->reading) { | 14 | if (!config->active) { |
16 | return cmd_results_new(CMD_DEFER, NULL); | 15 | return cmd_results_new(CMD_DEFER, NULL); |
17 | } | 16 | } |
18 | 17 | ||
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 749235eb..df7c379d 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <linux/input-event-codes.h> | 1 | #include <linux/input-event-codes.h> |
3 | 2 | ||
4 | #include <strings.h> | 3 | #include <strings.h> |
@@ -18,7 +17,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, | |||
18 | int argc, char **argv) { | 17 | int argc, char **argv) { |
19 | if (strcasecmp(argv[0], "move") == 0) { | 18 | if (strcasecmp(argv[0], "move") == 0) { |
20 | if (argc < 3) { | 19 | if (argc < 3) { |
21 | return cmd_results_new(CMD_INVALID, expected_syntax); | 20 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
22 | } | 21 | } |
23 | int delta_x = strtol(argv[1], NULL, 10); | 22 | int delta_x = strtol(argv[1], NULL, 10); |
24 | int delta_y = strtol(argv[2], NULL, 10); | 23 | int delta_y = strtol(argv[2], NULL, 10); |
@@ -27,7 +26,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, | |||
27 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 26 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
28 | } else if (strcasecmp(argv[0], "set") == 0) { | 27 | } else if (strcasecmp(argv[0], "set") == 0) { |
29 | if (argc < 3) { | 28 | if (argc < 3) { |
30 | return cmd_results_new(CMD_INVALID, expected_syntax); | 29 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
31 | } | 30 | } |
32 | // map absolute coords (0..1,0..1) to root container coords | 31 | // map absolute coords (0..1,0..1) to root container coords |
33 | float x = strtof(argv[1], NULL) / root->width; | 32 | float x = strtof(argv[1], NULL) / root->width; |
@@ -37,7 +36,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, | |||
37 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 36 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
38 | } else { | 37 | } else { |
39 | if (argc < 2) { | 38 | if (argc < 2) { |
40 | return cmd_results_new(CMD_INVALID, expected_syntax); | 39 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
41 | } | 40 | } |
42 | struct cmd_results *error = NULL; | 41 | struct cmd_results *error = NULL; |
43 | if ((error = press_or_release(cursor, argv[0], argv[1]))) { | 42 | if ((error = press_or_release(cursor, argv[0], argv[1]))) { |
@@ -85,36 +84,36 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) { | |||
85 | 84 | ||
86 | static struct cmd_results *press_or_release(struct sway_cursor *cursor, | 85 | static struct cmd_results *press_or_release(struct sway_cursor *cursor, |
87 | char *action, char *button_str) { | 86 | char *action, char *button_str) { |
88 | enum wlr_button_state state; | 87 | enum wl_pointer_button_state state; |
89 | uint32_t button; | 88 | uint32_t button; |
90 | if (strcasecmp(action, "press") == 0) { | 89 | if (strcasecmp(action, "press") == 0) { |
91 | state = WLR_BUTTON_PRESSED; | 90 | state = WL_POINTER_BUTTON_STATE_PRESSED; |
92 | } else if (strcasecmp(action, "release") == 0) { | 91 | } else if (strcasecmp(action, "release") == 0) { |
93 | state = WLR_BUTTON_RELEASED; | 92 | state = WL_POINTER_BUTTON_STATE_RELEASED; |
94 | } else { | 93 | } else { |
95 | return cmd_results_new(CMD_INVALID, expected_syntax); | 94 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
96 | } | 95 | } |
97 | 96 | ||
98 | char *message = NULL; | 97 | char *message = NULL; |
99 | button = get_mouse_button(button_str, &message); | 98 | button = get_mouse_button(button_str, &message); |
100 | if (message) { | 99 | if (message) { |
101 | struct cmd_results *error = | 100 | struct cmd_results *error = |
102 | cmd_results_new(CMD_INVALID, message); | 101 | cmd_results_new(CMD_INVALID, "%s", message); |
103 | free(message); | 102 | free(message); |
104 | return error; | 103 | return error; |
105 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN | 104 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN |
106 | || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { | 105 | || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { |
107 | // Dispatch axis event | 106 | // Dispatch axis event |
108 | enum wlr_axis_orientation orientation = | 107 | enum wl_pointer_axis orientation = |
109 | (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) | 108 | (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) |
110 | ? WLR_AXIS_ORIENTATION_VERTICAL | 109 | ? WL_POINTER_AXIS_VERTICAL_SCROLL |
111 | : WLR_AXIS_ORIENTATION_HORIZONTAL; | 110 | : WL_POINTER_AXIS_HORIZONTAL_SCROLL; |
112 | double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) | 111 | double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) |
113 | ? -1 : 1; | 112 | ? -1 : 1; |
114 | struct wlr_event_pointer_axis event = { | 113 | struct wlr_pointer_axis_event event = { |
115 | .device = NULL, | 114 | .pointer = NULL, |
116 | .time_msec = 0, | 115 | .time_msec = 0, |
117 | .source = WLR_AXIS_SOURCE_WHEEL, | 116 | .source = WL_POINTER_AXIS_SOURCE_WHEEL, |
118 | .orientation = orientation, | 117 | .orientation = orientation, |
119 | .delta = delta * 15, | 118 | .delta = delta * 15, |
120 | .delta_discrete = delta | 119 | .delta_discrete = delta |
diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c index e09b82d9..f5177a47 100644 --- a/sway/commands/seat/hide_cursor.c +++ b/sway/commands/seat/hide_cursor.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c index 82428f2c..2974453e 100644 --- a/sway/commands/seat/idle.c +++ b/sway/commands/seat/idle.c | |||
@@ -1,8 +1,8 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
5 | #include <stdint.h> | 4 | #include <stdint.h> |
5 | #include "log.h" | ||
6 | #include "sway/commands.h" | 6 | #include "sway/commands.h" |
7 | #include "sway/config.h" | 7 | #include "sway/config.h" |
8 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
@@ -69,5 +69,10 @@ struct cmd_results *seat_cmd_idle_wake(int argc, char **argv) { | |||
69 | return cmd_results_new(CMD_FAILURE, "Invalid idle source"); | 69 | return cmd_results_new(CMD_FAILURE, "Invalid idle source"); |
70 | } | 70 | } |
71 | config->handler_context.seat_config->idle_wake_sources = sources; | 71 | config->handler_context.seat_config->idle_wake_sources = sources; |
72 | sway_log(SWAY_INFO, "Warning: seat idle_wake is deprecated"); | ||
73 | if (config->reading) { | ||
74 | config_add_swaynag_warning("seat idle_wake is deprecated. " | ||
75 | "Only seat idle_inhibit is supported."); | ||
76 | } | ||
72 | return cmd_results_new(CMD_SUCCESS, NULL); | 77 | return cmd_results_new(CMD_SUCCESS, NULL); |
73 | } | 78 | } |
diff --git a/sway/commands/seat/xcursor_theme.c b/sway/commands/seat/xcursor_theme.c index 202f35b9..61322a57 100644 --- a/sway/commands/seat/xcursor_theme.c +++ b/sway/commands/seat/xcursor_theme.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/set.c b/sway/commands/set.c index c539e9fc..ba384c7c 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index 0d373b80..60cef9fa 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -10,8 +9,8 @@ | |||
10 | #include "stringop.h" | 9 | #include "stringop.h" |
11 | #include "util.h" | 10 | #include "util.h" |
12 | 11 | ||
13 | static void rebuild_marks_iterator(struct sway_container *con, void *data) { | 12 | static void title_bar_update_iterator(struct sway_container *con, void *data) { |
14 | container_update_marks_textures(con); | 13 | container_update_marks(con); |
15 | } | 14 | } |
16 | 15 | ||
17 | struct cmd_results *cmd_show_marks(int argc, char **argv) { | 16 | struct cmd_results *cmd_show_marks(int argc, char **argv) { |
@@ -23,12 +22,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { | |||
23 | config->show_marks = parse_boolean(argv[0], config->show_marks); | 22 | config->show_marks = parse_boolean(argv[0], config->show_marks); |
24 | 23 | ||
25 | if (config->show_marks) { | 24 | if (config->show_marks) { |
26 | root_for_each_container(rebuild_marks_iterator, NULL); | 25 | root_for_each_container(title_bar_update_iterator, NULL); |
27 | } | ||
28 | |||
29 | for (int i = 0; i < root->outputs->length; ++i) { | ||
30 | struct sway_output *output = root->outputs->items[i]; | ||
31 | output_damage_whole(output); | ||
32 | } | 26 | } |
33 | 27 | ||
34 | return cmd_results_new(CMD_SUCCESS, NULL); | 28 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c index b27f9ccd..a6d165dc 100644 --- a/sway/commands/smart_gaps.c +++ b/sway/commands/smart_gaps.c | |||
@@ -15,7 +15,12 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) { | |||
15 | return error; | 15 | return error; |
16 | } | 16 | } |
17 | 17 | ||
18 | config->smart_gaps = parse_boolean(argv[0], config->smart_gaps); | 18 | if (strcmp(argv[0], "inverse_outer") == 0) { |
19 | config->smart_gaps = SMART_GAPS_INVERSE_OUTER; | ||
20 | } else { | ||
21 | config->smart_gaps = parse_boolean(argv[0], config->smart_gaps) | ||
22 | ? SMART_GAPS_ON : SMART_GAPS_OFF; | ||
23 | } | ||
19 | 24 | ||
20 | arrange_root(); | 25 | arrange_root(); |
21 | 26 | ||
diff --git a/sway/commands/split.c b/sway/commands/split.c index 782bab02..500a497d 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c | |||
@@ -14,7 +14,7 @@ static struct cmd_results *do_split(int layout) { | |||
14 | struct sway_workspace *ws = config->handler_context.workspace; | 14 | struct sway_workspace *ws = config->handler_context.workspace; |
15 | if (con) { | 15 | if (con) { |
16 | if (container_is_scratchpad_hidden_or_child(con) && | 16 | if (container_is_scratchpad_hidden_or_child(con) && |
17 | con->fullscreen_mode != FULLSCREEN_GLOBAL) { | 17 | con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { |
18 | return cmd_results_new(CMD_FAILURE, | 18 | return cmd_results_new(CMD_FAILURE, |
19 | "Cannot split a hidden scratchpad container"); | 19 | "Cannot split a hidden scratchpad container"); |
20 | } | 20 | } |
@@ -32,6 +32,24 @@ static struct cmd_results *do_split(int layout) { | |||
32 | return cmd_results_new(CMD_SUCCESS, NULL); | 32 | return cmd_results_new(CMD_SUCCESS, NULL); |
33 | } | 33 | } |
34 | 34 | ||
35 | static struct cmd_results *do_unsplit(void) { | ||
36 | struct sway_container *con = config->handler_context.container; | ||
37 | struct sway_workspace *ws = config->handler_context.workspace; | ||
38 | |||
39 | if (con && con->pending.parent && con->pending.parent->pending.children->length == 1) { | ||
40 | container_flatten(con->pending.parent); | ||
41 | } else { | ||
42 | return cmd_results_new(CMD_FAILURE, "Can only flatten a child container with no siblings"); | ||
43 | } | ||
44 | |||
45 | if (root->fullscreen_global) { | ||
46 | arrange_root(); | ||
47 | } else { | ||
48 | arrange_workspace(ws); | ||
49 | } | ||
50 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
51 | } | ||
52 | |||
35 | struct cmd_results *cmd_split(int argc, char **argv) { | 53 | struct cmd_results *cmd_split(int argc, char **argv) { |
36 | struct cmd_results *error = NULL; | 54 | struct cmd_results *error = NULL; |
37 | if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { | 55 | if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { |
@@ -55,6 +73,9 @@ struct cmd_results *cmd_split(int argc, char **argv) { | |||
55 | } else { | 73 | } else { |
56 | return do_split(L_VERT); | 74 | return do_split(L_VERT); |
57 | } | 75 | } |
76 | } else if (strcasecmp(argv[0], "n") == 0 || | ||
77 | strcasecmp(argv[0], "none") == 0) { | ||
78 | return do_unsplit(); | ||
58 | } else { | 79 | } else { |
59 | return cmd_results_new(CMD_FAILURE, | 80 | return cmd_results_new(CMD_FAILURE, |
60 | "Invalid split command (expected either horizontal or vertical)."); | 81 | "Invalid split command (expected either horizontal or vertical)."); |
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c index 3c93a276..9b09a0f9 100644 --- a/sway/commands/sticky.c +++ b/sway/commands/sticky.c | |||
@@ -29,14 +29,14 @@ struct cmd_results *cmd_sticky(int argc, char **argv) { | |||
29 | !container_is_scratchpad_hidden(container)) { | 29 | !container_is_scratchpad_hidden(container)) { |
30 | // move container to active workspace | 30 | // move container to active workspace |
31 | struct sway_workspace *active_workspace = | 31 | struct sway_workspace *active_workspace = |
32 | output_get_active_workspace(container->workspace->output); | 32 | output_get_active_workspace(container->pending.workspace->output); |
33 | if (!sway_assert(active_workspace, | 33 | if (!sway_assert(active_workspace, |
34 | "Expected output to have a workspace")) { | 34 | "Expected output to have a workspace")) { |
35 | return cmd_results_new(CMD_FAILURE, | 35 | return cmd_results_new(CMD_FAILURE, |
36 | "Expected output to have a workspace"); | 36 | "Expected output to have a workspace"); |
37 | } | 37 | } |
38 | if (container->workspace != active_workspace) { | 38 | if (container->pending.workspace != active_workspace) { |
39 | struct sway_workspace *old_workspace = container->workspace; | 39 | struct sway_workspace *old_workspace = container->pending.workspace; |
40 | container_detach(container); | 40 | container_detach(container); |
41 | workspace_add_floating(active_workspace, container); | 41 | workspace_add_floating(active_workspace, container); |
42 | container_handle_fullscreen_reparent(container); | 42 | container_handle_fullscreen_reparent(container); |
diff --git a/sway/commands/swap.c b/sway/commands/swap.c index a7f9691b..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->x = con1->x; | ||
20 | temp->y = con1->y; | ||
21 | temp->width = con1->width; | ||
22 | temp->height = con1->height; | ||
23 | temp->width_fraction = con1->width_fraction; | ||
24 | temp->height_fraction = con1->height_fraction; | ||
25 | temp->parent = con1->parent; | ||
26 | temp->workspace = con1->workspace; | ||
27 | bool temp_floating = container_is_floating(con1); | ||
28 | |||
29 | con1->x = con2->x; | ||
30 | con1->y = con2->y; | ||
31 | con1->width = con2->width; | ||
32 | con1->height = con2->height; | ||
33 | con1->width_fraction = con2->width_fraction; | ||
34 | con1->height_fraction = con2->height_fraction; | ||
35 | |||
36 | con2->x = temp->x; | ||
37 | con2->y = temp->y; | ||
38 | con2->width = temp->width; | ||
39 | con2->height = temp->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->parent) { | ||
45 | container_insert_child(con2->parent, con1, | ||
46 | container_sibling_index(con2)); | ||
47 | } else if (container_is_floating(con2)) { | ||
48 | workspace_add_floating(con2->workspace, con1); | ||
49 | } else { | ||
50 | workspace_insert_tiling(con2->workspace, con1, | ||
51 | container_sibling_index(con2)); | ||
52 | } | ||
53 | if (temp->parent) { | ||
54 | container_insert_child(temp->parent, con2, temp_index); | ||
55 | } else if (temp_floating) { | ||
56 | workspace_add_floating(temp->workspace, con2); | ||
57 | } else { | ||
58 | workspace_insert_tiling(temp->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->workspace; | ||
69 | struct sway_workspace *ws2 = con2->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->fullscreen_mode; | ||
129 | enum sway_fullscreen_mode fs2 = con2->fullscreen_mode; | ||
130 | if (fs1) { | ||
131 | container_fullscreen_disable(con1); | ||
132 | } | ||
133 | if (fs2) { | ||
134 | container_fullscreen_disable(con2); | ||
135 | } | ||
136 | |||
137 | struct sway_seat *seat = config->handler_context.seat; | ||
138 | struct sway_container *focus = seat_get_focused_container(seat); | ||
139 | struct sway_workspace *vis1 = | ||
140 | output_get_active_workspace(con1->workspace->output); | ||
141 | struct sway_workspace *vis2 = | ||
142 | output_get_active_workspace(con2->workspace->output); | ||
143 | if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" | ||
144 | "workspace. This should not happen")) { | ||
145 | return; | ||
146 | } | ||
147 | |||
148 | char *stored_prev_name = NULL; | ||
149 | if (seat->prev_workspace_name) { | ||
150 | stored_prev_name = strdup(seat->prev_workspace_name); | ||
151 | } | ||
152 | |||
153 | swap_places(con1, con2); | ||
154 | |||
155 | if (!workspace_is_visible(vis1)) { | ||
156 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); | ||
157 | } | ||
158 | if (!workspace_is_visible(vis2)) { | ||
159 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); | ||
160 | } | ||
161 | |||
162 | swap_focus(con1, con2, seat, focus); | ||
163 | |||
164 | if (stored_prev_name) { | ||
165 | free(seat->prev_workspace_name); | ||
166 | seat->prev_workspace_name = stored_prev_name; | ||
167 | } | ||
168 | |||
169 | if (scratch1) { | ||
170 | root_scratchpad_add_container(con2, NULL); | ||
171 | if (!hidden1) { | ||
172 | root_scratchpad_show(con2); | ||
173 | } | ||
174 | } | ||
175 | if (scratch2) { | ||
176 | root_scratchpad_add_container(con1, NULL); | ||
177 | if (!hidden2) { | ||
178 | root_scratchpad_show(con1); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | if (fs1) { | ||
183 | container_set_fullscreen(con2, fs1); | ||
184 | } | ||
185 | if (fs2) { | ||
186 | container_set_fullscreen(con1, fs2); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | static bool test_con_id(struct sway_container *container, void *data) { | 16 | static bool test_con_id(struct sway_container *container, void *data) { |
191 | size_t *con_id = data; | 17 | size_t *con_id = data; |
192 | return container->node.id == *con_id; | 18 | return container->node.id == *con_id; |
@@ -219,7 +45,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
219 | } | 45 | } |
220 | 46 | ||
221 | if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { | 47 | if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { |
222 | return cmd_results_new(CMD_INVALID, expected_syntax); | 48 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
223 | } | 49 | } |
224 | 50 | ||
225 | struct sway_container *current = config->handler_context.container; | 51 | struct sway_container *current = config->handler_context.container; |
@@ -238,7 +64,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
238 | other = root_find_container(test_mark, value); | 64 | other = root_find_container(test_mark, value); |
239 | } else { | 65 | } else { |
240 | free(value); | 66 | free(value); |
241 | return cmd_results_new(CMD_INVALID, expected_syntax); | 67 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
242 | } | 68 | } |
243 | 69 | ||
244 | if (!other) { | 70 | if (!other) { |
@@ -247,6 +73,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
247 | } else if (!current) { | 73 | } else if (!current) { |
248 | error = cmd_results_new(CMD_FAILURE, | 74 | error = cmd_results_new(CMD_FAILURE, |
249 | "Can only swap with containers and views"); | 75 | "Can only swap with containers and views"); |
76 | } else if (current == other) { | ||
77 | error = cmd_results_new(CMD_FAILURE, | ||
78 | "Cannot swap a container with itself"); | ||
250 | } else if (container_has_ancestor(current, other) | 79 | } else if (container_has_ancestor(current, other) |
251 | || container_has_ancestor(other, current)) { | 80 | || container_has_ancestor(other, current)) { |
252 | error = cmd_results_new(CMD_FAILURE, | 81 | error = cmd_results_new(CMD_FAILURE, |
diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c index c30355de..be298a29 100644 --- a/sway/commands/title_align.c +++ b/sway/commands/title_align.c | |||
@@ -4,6 +4,10 @@ | |||
4 | #include "sway/tree/container.h" | 4 | #include "sway/tree/container.h" |
5 | #include "sway/tree/root.h" | 5 | #include "sway/tree/root.h" |
6 | 6 | ||
7 | static void arrange_title_bar_iterator(struct sway_container *con, void *data) { | ||
8 | container_arrange_title_bar(con); | ||
9 | } | ||
10 | |||
7 | struct cmd_results *cmd_title_align(int argc, char **argv) { | 11 | struct cmd_results *cmd_title_align(int argc, char **argv) { |
8 | struct cmd_results *error = NULL; | 12 | struct cmd_results *error = NULL; |
9 | if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { | 13 | if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { |
@@ -21,10 +25,7 @@ struct cmd_results *cmd_title_align(int argc, char **argv) { | |||
21 | "Expected 'title_align left|center|right'"); | 25 | "Expected 'title_align left|center|right'"); |
22 | } | 26 | } |
23 | 27 | ||
24 | for (int i = 0; i < root->outputs->length; ++i) { | 28 | root_for_each_container(arrange_title_bar_iterator, NULL); |
25 | struct sway_output *output = root->outputs->items[i]; | ||
26 | output_damage_whole(output); | ||
27 | } | ||
28 | 29 | ||
29 | return cmd_results_new(CMD_SUCCESS, NULL); | 30 | return cmd_results_new(CMD_SUCCESS, NULL); |
30 | } | 31 | } |
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c index 9d312470..0b2ea265 100644 --- a/sway/commands/title_format.c +++ b/sway/commands/title_format.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -23,6 +22,5 @@ struct cmd_results *cmd_title_format(int argc, char **argv) { | |||
23 | } | 22 | } |
24 | view->title_format = format; | 23 | view->title_format = format; |
25 | view_update_title(view, true); | 24 | view_update_title(view, true); |
26 | config_update_font_height(true); | ||
27 | return cmd_results_new(CMD_SUCCESS, NULL); | 25 | return cmd_results_new(CMD_SUCCESS, NULL); |
28 | } | 26 | } |
diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c index 7c27c163..fa3db3c5 100644 --- a/sway/commands/titlebar_border_thickness.c +++ b/sway/commands/titlebar_border_thickness.c | |||
@@ -27,7 +27,6 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) { | |||
27 | "Expected output to have a workspace"); | 27 | "Expected output to have a workspace"); |
28 | } | 28 | } |
29 | arrange_workspace(ws); | 29 | arrange_workspace(ws); |
30 | output_damage_whole(output); | ||
31 | } | 30 | } |
32 | 31 | ||
33 | return cmd_results_new(CMD_SUCCESS, NULL); | 32 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c index 29ce59ff..6999f7a2 100644 --- a/sway/commands/titlebar_padding.c +++ b/sway/commands/titlebar_padding.c | |||
@@ -33,7 +33,6 @@ struct cmd_results *cmd_titlebar_padding(int argc, char **argv) { | |||
33 | for (int i = 0; i < root->outputs->length; ++i) { | 33 | for (int i = 0; i < root->outputs->length; ++i) { |
34 | struct sway_output *output = root->outputs->items[i]; | 34 | struct sway_output *output = root->outputs->items[i]; |
35 | arrange_workspace(output_get_active_workspace(output)); | 35 | arrange_workspace(output_get_active_workspace(output)); |
36 | output_damage_whole(output); | ||
37 | } | 36 | } |
38 | 37 | ||
39 | return cmd_results_new(CMD_SUCCESS, NULL); | 38 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c index cedfcfb2..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 |
@@ -21,7 +24,7 @@ static void remove_all_marks_iterator(struct sway_container *con, void *data) { | |||
21 | struct cmd_results *cmd_unmark(int argc, char **argv) { | 24 | struct cmd_results *cmd_unmark(int argc, char **argv) { |
22 | // Determine the container | 25 | // Determine the container |
23 | struct sway_container *con = NULL; | 26 | struct sway_container *con = NULL; |
24 | if (config->handler_context.using_criteria) { | 27 | if (config->handler_context.node_overridden) { |
25 | con = config->handler_context.container; | 28 | con = config->handler_context.container; |
26 | } | 29 | } |
27 | 30 | ||
@@ -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 9ff1c97d..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. |
@@ -178,25 +177,20 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
178 | } | 177 | } |
179 | 178 | ||
180 | if (root->fullscreen_global) { | 179 | if (root->fullscreen_global) { |
181 | return cmd_results_new(CMD_FAILURE, "workspace", | 180 | return cmd_results_new(CMD_FAILURE, |
182 | "Can't switch workspaces while fullscreen global"); | 181 | "Can't switch workspaces while fullscreen global"); |
183 | } | 182 | } |
184 | 183 | ||
185 | bool no_auto_back_and_forth = false; | 184 | bool auto_back_and_forth = true; |
186 | while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { | 185 | while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { |
187 | no_auto_back_and_forth = true; | 186 | auto_back_and_forth = false; |
188 | if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { | 187 | if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { |
189 | return error; | 188 | return error; |
190 | } | 189 | } |
191 | ++argv; | 190 | ++argv; |
192 | } | 191 | } |
193 | 192 | ||
194 | bool create = argc > 1 && strcasecmp(argv[1], "--create") == 0; | ||
195 | struct sway_seat *seat = config->handler_context.seat; | 193 | struct sway_seat *seat = config->handler_context.seat; |
196 | struct sway_workspace *current = seat_get_focused_workspace(seat); | ||
197 | if (!current) { | ||
198 | return cmd_results_new(CMD_FAILURE, "No workspace to switch from"); | ||
199 | } | ||
200 | 194 | ||
201 | struct sway_workspace *ws = NULL; | 195 | struct sway_workspace *ws = NULL; |
202 | if (strcasecmp(argv[0], "number") == 0) { | 196 | if (strcasecmp(argv[0], "number") == 0) { |
@@ -213,14 +207,15 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
213 | ws = workspace_create(NULL, name); | 207 | ws = workspace_create(NULL, name); |
214 | free(name); | 208 | free(name); |
215 | } | 209 | } |
210 | if (ws && auto_back_and_forth) { | ||
211 | ws = workspace_auto_back_and_forth(ws); | ||
212 | } | ||
216 | } else if (strcasecmp(argv[0], "next") == 0 || | 213 | } else if (strcasecmp(argv[0], "next") == 0 || |
217 | strcasecmp(argv[0], "prev") == 0 || | 214 | strcasecmp(argv[0], "prev") == 0 || |
215 | strcasecmp(argv[0], "next_on_output") == 0 || | ||
216 | strcasecmp(argv[0], "prev_on_output") == 0 || | ||
218 | strcasecmp(argv[0], "current") == 0) { | 217 | strcasecmp(argv[0], "current") == 0) { |
219 | ws = workspace_by_name(argv[0]); | 218 | ws = workspace_by_name(argv[0]); |
220 | } else if (strcasecmp(argv[0], "next_on_output") == 0) { | ||
221 | ws = workspace_output_next(current, create); | ||
222 | } else if (strcasecmp(argv[0], "prev_on_output") == 0) { | ||
223 | ws = workspace_output_prev(current, create); | ||
224 | } else if (strcasecmp(argv[0], "back_and_forth") == 0) { | 219 | } else if (strcasecmp(argv[0], "back_and_forth") == 0) { |
225 | if (!seat->prev_workspace_name) { | 220 | if (!seat->prev_workspace_name) { |
226 | return cmd_results_new(CMD_INVALID, | 221 | return cmd_results_new(CMD_INVALID, |
@@ -235,11 +230,14 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
235 | ws = workspace_create(NULL, name); | 230 | ws = workspace_create(NULL, name); |
236 | } | 231 | } |
237 | free(name); | 232 | free(name); |
233 | if (ws && auto_back_and_forth) { | ||
234 | ws = workspace_auto_back_and_forth(ws); | ||
235 | } | ||
238 | } | 236 | } |
239 | if (!ws) { | 237 | if (!ws) { |
240 | return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); | 238 | return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); |
241 | } | 239 | } |
242 | workspace_switch(ws, no_auto_back_and_forth); | 240 | workspace_switch(ws); |
243 | seat_consider_warp_to_focus(seat); | 241 | seat_consider_warp_to_focus(seat); |
244 | } | 242 | } |
245 | return cmd_results_new(CMD_SUCCESS, NULL); | 243 | return cmd_results_new(CMD_SUCCESS, NULL); |
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 6e665434..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> |
@@ -26,7 +27,7 @@ | |||
26 | #include "sway/tree/arrange.h" | 27 | #include "sway/tree/arrange.h" |
27 | #include "sway/tree/root.h" | 28 | #include "sway/tree/root.h" |
28 | #include "sway/tree/workspace.h" | 29 | #include "sway/tree/workspace.h" |
29 | #include "cairo.h" | 30 | #include "cairo_util.h" |
30 | #include "pango.h" | 31 | #include "pango.h" |
31 | #include "stringop.h" | 32 | #include "stringop.h" |
32 | #include "list.h" | 33 | #include "list.h" |
@@ -36,19 +37,26 @@ | |||
36 | struct sway_config *config = NULL; | 37 | struct sway_config *config = NULL; |
37 | 38 | ||
38 | static struct xkb_state *keysym_translation_state_create( | 39 | static struct xkb_state *keysym_translation_state_create( |
39 | struct xkb_rule_names rules) { | 40 | struct xkb_rule_names rules, uint32_t context_flags) { |
40 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | 41 | struct xkb_context *context = xkb_context_new(context_flags | XKB_CONTEXT_NO_SECURE_GETENV); |
41 | struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( | 42 | struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( |
42 | context, | 43 | context, |
43 | &rules, | 44 | &rules, |
44 | XKB_KEYMAP_COMPILE_NO_FLAGS); | 45 | XKB_KEYMAP_COMPILE_NO_FLAGS); |
45 | |||
46 | xkb_context_unref(context); | 46 | xkb_context_unref(context); |
47 | if (xkb_keymap == NULL) { | ||
48 | sway_log(SWAY_ERROR, "Failed to compile keysym translation XKB keymap"); | ||
49 | return NULL; | ||
50 | } | ||
51 | |||
47 | return xkb_state_new(xkb_keymap); | 52 | return xkb_state_new(xkb_keymap); |
48 | } | 53 | } |
49 | 54 | ||
50 | static void keysym_translation_state_destroy( | 55 | static void keysym_translation_state_destroy( |
51 | struct xkb_state *state) { | 56 | struct xkb_state *state) { |
57 | if (state == NULL) { | ||
58 | return; | ||
59 | } | ||
52 | xkb_keymap_unref(xkb_state_get_keymap(state)); | 60 | xkb_keymap_unref(xkb_state_get_keymap(state)); |
53 | xkb_state_unref(state); | 61 | xkb_state_unref(state); |
54 | } | 62 | } |
@@ -82,6 +90,12 @@ static void free_mode(struct sway_mode *mode) { | |||
82 | } | 90 | } |
83 | list_free(mode->switch_bindings); | 91 | list_free(mode->switch_bindings); |
84 | } | 92 | } |
93 | if (mode->gesture_bindings) { | ||
94 | for (int i = 0; i < mode->gesture_bindings->length; i++) { | ||
95 | free_gesture_binding(mode->gesture_bindings->items[i]); | ||
96 | } | ||
97 | list_free(mode->gesture_bindings); | ||
98 | } | ||
85 | free(mode); | 99 | free(mode); |
86 | } | 100 | } |
87 | 101 | ||
@@ -222,6 +236,7 @@ static void config_defaults(struct sway_config *config) { | |||
222 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; | 236 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; |
223 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; | 237 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; |
224 | if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; | 238 | if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; |
239 | if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup; | ||
225 | list_add(config->modes, config->current_mode); | 240 | list_add(config->modes, config->current_mode); |
226 | 241 | ||
227 | config->floating_mod = 0; | 242 | config->floating_mod = 0; |
@@ -236,7 +251,7 @@ static void config_defaults(struct sway_config *config) { | |||
236 | config->default_layout = L_NONE; | 251 | config->default_layout = L_NONE; |
237 | config->default_orientation = L_NONE; | 252 | config->default_orientation = L_NONE; |
238 | if (!(config->font = strdup("monospace 10"))) goto cleanup; | 253 | if (!(config->font = strdup("monospace 10"))) goto cleanup; |
239 | config->font_height = 17; // height of monospace 10 | 254 | config->font_description = pango_font_description_from_string(config->font); |
240 | config->urgent_timeout = 500; | 255 | config->urgent_timeout = 500; |
241 | config->focus_on_window_activation = FOWA_URGENT; | 256 | config->focus_on_window_activation = FOWA_URGENT; |
242 | config->popup_during_fullscreen = POPUP_SMART; | 257 | config->popup_during_fullscreen = POPUP_SMART; |
@@ -266,8 +281,9 @@ static void config_defaults(struct sway_config *config) { | |||
266 | config->title_align = ALIGN_LEFT; | 281 | config->title_align = ALIGN_LEFT; |
267 | config->tiling_drag = true; | 282 | config->tiling_drag = true; |
268 | config->tiling_drag_threshold = 9; | 283 | config->tiling_drag_threshold = 9; |
284 | config->primary_selection = true; | ||
269 | 285 | ||
270 | config->smart_gaps = false; | 286 | config->smart_gaps = SMART_GAPS_OFF; |
271 | config->gaps_inner = 0; | 287 | config->gaps_inner = 0; |
272 | config->gaps_outer.top = 0; | 288 | config->gaps_outer.top = 0; |
273 | config->gaps_outer.right = 0; | 289 | config->gaps_outer.right = 0; |
@@ -291,6 +307,8 @@ static void config_defaults(struct sway_config *config) { | |||
291 | config->hide_edge_borders_smart = ESMART_OFF; | 307 | config->hide_edge_borders_smart = ESMART_OFF; |
292 | config->hide_lone_tab = false; | 308 | config->hide_lone_tab = false; |
293 | 309 | ||
310 | config->has_focused_tab_title = false; | ||
311 | |||
294 | // border colors | 312 | // border colors |
295 | color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); | 313 | color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); |
296 | color_to_rgba(config->border_colors.focused.background, 0x285577FF); | 314 | color_to_rgba(config->border_colors.focused.background, 0x285577FF); |
@@ -326,8 +344,14 @@ static void config_defaults(struct sway_config *config) { | |||
326 | 344 | ||
327 | // The keysym to keycode translation | 345 | // The keysym to keycode translation |
328 | struct xkb_rule_names rules = {0}; | 346 | struct xkb_rule_names rules = {0}; |
329 | config->keysym_translation_state = | 347 | config->keysym_translation_state = keysym_translation_state_create(rules, 0); |
330 | keysym_translation_state_create(rules); | 348 | if (config->keysym_translation_state == NULL) { |
349 | config->keysym_translation_state = keysym_translation_state_create(rules, | ||
350 | XKB_CONTEXT_NO_ENVIRONMENT_NAMES); | ||
351 | } | ||
352 | if (config->keysym_translation_state == NULL) { | ||
353 | goto cleanup; | ||
354 | } | ||
331 | 355 | ||
332 | return; | 356 | return; |
333 | cleanup: | 357 | cleanup: |
@@ -338,35 +362,53 @@ static bool file_exists(const char *path) { | |||
338 | return path && access(path, R_OK) != -1; | 362 | return path && access(path, R_OK) != -1; |
339 | } | 363 | } |
340 | 364 | ||
365 | static char *config_path(const char *prefix, const char *config_folder) { | ||
366 | if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { | ||
367 | return NULL; | ||
368 | } | ||
369 | return format_str("%s/%s/config", prefix, config_folder); | ||
370 | } | ||
371 | |||
341 | static char *get_config_path(void) { | 372 | static char *get_config_path(void) { |
342 | static const char *config_paths[] = { | 373 | char *path = NULL; |
343 | "$HOME/.sway/config", | 374 | const char *home = getenv("HOME"); |
344 | "$XDG_CONFIG_HOME/sway/config", | 375 | char *config_home_fallback = NULL; |
345 | "$HOME/.i3/config", | 376 | |
346 | "$XDG_CONFIG_HOME/i3/config", | 377 | const char *config_home = getenv("XDG_CONFIG_HOME"); |
347 | SYSCONFDIR "/sway/config", | 378 | if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { |
348 | SYSCONFDIR "/i3/config", | 379 | config_home_fallback = format_str("%s/.config", home); |
380 | config_home = config_home_fallback; | ||
381 | } | ||
382 | |||
383 | struct config_path { | ||
384 | const char *prefix; | ||
385 | const char *config_folder; | ||
349 | }; | 386 | }; |
350 | 387 | ||
351 | char *config_home = getenv("XDG_CONFIG_HOME"); | 388 | struct config_path config_paths[] = { |
352 | if (!config_home || !*config_home) { | 389 | { .prefix = home, .config_folder = ".sway"}, |
353 | config_paths[1] = "$HOME/.config/sway/config"; | 390 | { .prefix = config_home, .config_folder = "sway"}, |
354 | config_paths[3] = "$HOME/.config/i3/config"; | 391 | { .prefix = home, .config_folder = ".i3"}, |
355 | } | 392 | { .prefix = config_home, .config_folder = "i3"}, |
393 | { .prefix = SYSCONFDIR, .config_folder = "sway"}, | ||
394 | { .prefix = SYSCONFDIR, .config_folder = "i3"} | ||
395 | }; | ||
356 | 396 | ||
357 | for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { | 397 | size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]); |
358 | wordexp_t p; | 398 | for (size_t i = 0; i < num_config_paths; i++) { |
359 | if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { | 399 | path = config_path(config_paths[i].prefix, config_paths[i].config_folder); |
360 | char *path = strdup(p.we_wordv[0]); | 400 | if (!path) { |
361 | wordfree(&p); | 401 | continue; |
362 | if (file_exists(path)) { | 402 | } |
363 | return path; | 403 | if (file_exists(path)) { |
364 | } | 404 | break; |
365 | free(path); | ||
366 | } | 405 | } |
406 | free(path); | ||
407 | path = NULL; | ||
367 | } | 408 | } |
368 | 409 | ||
369 | return NULL; | 410 | free(config_home_fallback); |
411 | return path; | ||
370 | } | 412 | } |
371 | 413 | ||
372 | static bool load_config(const char *path, struct sway_config *config, | 414 | static bool load_config(const char *path, struct sway_config *config, |
@@ -438,6 +480,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
438 | old_config->xwayland ? "enabled" : "disabled"); | 480 | old_config->xwayland ? "enabled" : "disabled"); |
439 | config->xwayland = old_config->xwayland; | 481 | config->xwayland = old_config->xwayland; |
440 | 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 | |||
441 | if (!config->validating) { | 488 | if (!config->validating) { |
442 | if (old_config->swaybg_client != NULL) { | 489 | if (old_config->swaybg_client != NULL) { |
443 | wl_client_destroy(old_config->swaybg_client); | 490 | wl_client_destroy(old_config->swaybg_client); |
@@ -457,56 +504,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
457 | 504 | ||
458 | config->reading = true; | 505 | config->reading = true; |
459 | 506 | ||
460 | // Read security configs | 507 | bool success = load_config(path, config, &config->swaynag_config_errors); |
461 | // TODO: Security | ||
462 | bool success = true; | ||
463 | /* | ||
464 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); | ||
465 | if (!dir) { | ||
466 | sway_log(SWAY_ERROR, | ||
467 | "%s does not exist, sway will have no security configuration" | ||
468 | " and will probably be broken", SYSCONFDIR "/sway/security.d"); | ||
469 | } else { | ||
470 | list_t *secconfigs = create_list(); | ||
471 | char *base = SYSCONFDIR "/sway/security.d/"; | ||
472 | struct dirent *ent = readdir(dir); | ||
473 | struct stat s; | ||
474 | while (ent != NULL) { | ||
475 | char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1); | ||
476 | strcpy(_path, base); | ||
477 | strcat(_path, ent->d_name); | ||
478 | lstat(_path, &s); | ||
479 | if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') { | ||
480 | list_add(secconfigs, _path); | ||
481 | } | ||
482 | else { | ||
483 | free(_path); | ||
484 | } | ||
485 | ent = readdir(dir); | ||
486 | } | ||
487 | closedir(dir); | ||
488 | |||
489 | list_qsort(secconfigs, qstrcmp); | ||
490 | for (int i = 0; i < secconfigs->length; ++i) { | ||
491 | char *_path = secconfigs->items[i]; | ||
492 | if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || | ||
493 | (((s.st_mode & 0777) != 0644) && | ||
494 | (s.st_mode & 0777) != 0444)) { | ||
495 | sway_log(SWAY_ERROR, | ||
496 | "Refusing to load %s - it must be owned by root " | ||
497 | "and mode 644 or 444", _path); | ||
498 | success = false; | ||
499 | } else { | ||
500 | success = success && load_config(_path, config); | ||
501 | } | ||
502 | } | ||
503 | |||
504 | list_free_items_and_destroy(secconfigs); | ||
505 | } | ||
506 | */ | ||
507 | |||
508 | success = success && load_config(path, config, | ||
509 | &config->swaynag_config_errors); | ||
510 | 508 | ||
511 | if (validating) { | 509 | if (validating) { |
512 | free_config(config); | 510 | free_config(config); |
@@ -514,6 +512,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
514 | return success; | 512 | return success; |
515 | } | 513 | } |
516 | 514 | ||
515 | // Only really necessary if not explicitly `font` is set in the config. | ||
516 | config_update_font_height(); | ||
517 | |||
517 | if (is_active && !validating) { | 518 | if (is_active && !validating) { |
518 | input_manager_verify_fallback_seat(); | 519 | input_manager_verify_fallback_seat(); |
519 | 520 | ||
@@ -531,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
531 | } | 532 | } |
532 | sway_switch_retrigger_bindings_for_all(); | 533 | sway_switch_retrigger_bindings_for_all(); |
533 | 534 | ||
534 | reset_outputs(); | 535 | apply_all_output_configs(); |
535 | spawn_swaybg(); | 536 | spawn_swaybg(); |
536 | 537 | ||
537 | config->reloading = false; | 538 | config->reloading = false; |
@@ -884,23 +885,18 @@ void config_add_swaynag_warning(char *fmt, ...) { | |||
884 | if (config->reading && !config->validating) { | 885 | if (config->reading && !config->validating) { |
885 | va_list args; | 886 | va_list args; |
886 | va_start(args, fmt); | 887 | va_start(args, fmt); |
887 | size_t length = vsnprintf(NULL, 0, fmt, args) + 1; | 888 | char *str = vformat_str(fmt, args); |
888 | va_end(args); | 889 | va_end(args); |
889 | 890 | if (str == NULL) { | |
890 | char *temp = malloc(length + 1); | ||
891 | if (!temp) { | ||
892 | sway_log(SWAY_ERROR, "Failed to allocate buffer for warning."); | ||
893 | return; | 891 | return; |
894 | } | 892 | } |
895 | 893 | ||
896 | va_start(args, fmt); | ||
897 | vsnprintf(temp, length, fmt, args); | ||
898 | va_end(args); | ||
899 | |||
900 | swaynag_log(config->swaynag_command, &config->swaynag_config_errors, | 894 | swaynag_log(config->swaynag_command, &config->swaynag_config_errors, |
901 | "Warning on line %i (%s) '%s': %s", | 895 | "Warning on line %i (%s) '%s': %s", |
902 | config->current_config_line_number, config->current_config_path, | 896 | config->current_config_line_number, config->current_config_path, |
903 | config->current_config_line, temp); | 897 | config->current_config_line, str); |
898 | |||
899 | free(str); | ||
904 | } | 900 | } |
905 | } | 901 | } |
906 | 902 | ||
@@ -940,7 +936,7 @@ char *do_var_replacement(char *str) { | |||
940 | int offset = find - str; | 936 | int offset = find - str; |
941 | strncpy(newptr, str, offset); | 937 | strncpy(newptr, str, offset); |
942 | newptr += offset; | 938 | newptr += offset; |
943 | strncpy(newptr, var->value, vvlen); | 939 | memcpy(newptr, var->value, vvlen); |
944 | newptr += vvlen; | 940 | newptr += vvlen; |
945 | strcpy(newptr, find + vnlen); | 941 | strcpy(newptr, find + vnlen); |
946 | free(str); | 942 | free(str); |
@@ -964,31 +960,11 @@ int workspace_output_cmp_workspace(const void *a, const void *b) { | |||
964 | return lenient_strcmp(wsa->workspace, wsb->workspace); | 960 | return lenient_strcmp(wsa->workspace, wsb->workspace); |
965 | } | 961 | } |
966 | 962 | ||
967 | static void find_font_height_iterator(struct sway_container *con, void *data) { | ||
968 | size_t amount_below_baseline = con->title_height - con->title_baseline; | ||
969 | size_t extended_height = config->font_baseline + amount_below_baseline; | ||
970 | if (extended_height > config->font_height) { | ||
971 | config->font_height = extended_height; | ||
972 | } | ||
973 | } | ||
974 | |||
975 | static void find_baseline_iterator(struct sway_container *con, void *data) { | ||
976 | bool *recalculate = data; | ||
977 | if (*recalculate) { | ||
978 | container_calculate_title_height(con); | ||
979 | } | ||
980 | if (con->title_baseline > config->font_baseline) { | ||
981 | config->font_baseline = con->title_baseline; | ||
982 | } | ||
983 | } | ||
984 | 963 | ||
985 | void config_update_font_height(bool recalculate) { | 964 | void config_update_font_height(void) { |
986 | size_t prev_max_height = config->font_height; | 965 | int prev_max_height = config->font_height; |
987 | config->font_height = 0; | ||
988 | config->font_baseline = 0; | ||
989 | 966 | ||
990 | root_for_each_container(find_baseline_iterator, &recalculate); | 967 | get_text_metrics(config->font_description, &config->font_height, &config->font_baseline); |
991 | root_for_each_container(find_font_height_iterator, NULL); | ||
992 | 968 | ||
993 | if (config->font_height != prev_max_height) { | 969 | if (config->font_height != prev_max_height) { |
994 | arrange_root(); | 970 | arrange_root(); |
@@ -1022,8 +998,12 @@ void translate_keysyms(struct input_config *input_config) { | |||
1022 | 998 | ||
1023 | struct xkb_rule_names rules = {0}; | 999 | struct xkb_rule_names rules = {0}; |
1024 | input_config_fill_rule_names(input_config, &rules); | 1000 | input_config_fill_rule_names(input_config, &rules); |
1025 | config->keysym_translation_state = | 1001 | config->keysym_translation_state = keysym_translation_state_create(rules, 0); |
1026 | 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 | } | ||
1027 | 1007 | ||
1028 | for (int i = 0; i < config->modes->length; ++i) { | 1008 | for (int i = 0; i < config->modes->length; ++i) { |
1029 | 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 767534a6..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> |
@@ -91,7 +90,7 @@ struct bar_config *default_bar_config(void) { | |||
91 | } | 90 | } |
92 | bar->outputs = NULL; | 91 | bar->outputs = NULL; |
93 | bar->position = strdup("bottom"); | 92 | bar->position = strdup("bottom"); |
94 | bar->pango_markup = false; | 93 | bar->pango_markup = PANGO_MARKUP_DEFAULT; |
95 | bar->swaybar_command = NULL; | 94 | bar->swaybar_command = NULL; |
96 | bar->font = NULL; | 95 | bar->font = NULL; |
97 | bar->height = 0; | 96 | bar->height = 0; |
@@ -217,6 +216,9 @@ static void invoke_swaybar(struct bar_config *bar) { | |||
217 | sigset_t set; | 216 | sigset_t set; |
218 | sigemptyset(&set); | 217 | sigemptyset(&set); |
219 | sigprocmask(SIG_SETMASK, &set, NULL); | 218 | sigprocmask(SIG_SETMASK, &set, NULL); |
219 | signal(SIGPIPE, SIG_DFL); | ||
220 | |||
221 | restore_nofile_limit(); | ||
220 | 222 | ||
221 | pid = fork(); | 223 | pid = fork(); |
222 | if (pid < 0) { | 224 | if (pid < 0) { |
@@ -253,7 +255,6 @@ static void invoke_swaybar(struct bar_config *bar) { | |||
253 | } | 255 | } |
254 | 256 | ||
255 | sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); | 257 | sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); |
256 | return; | ||
257 | } | 258 | } |
258 | 259 | ||
259 | 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 c9ec6745..54af5d8e 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -1,13 +1,15 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include <drm_fourcc.h> | ||
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <sys/socket.h> | 5 | #include <sys/socket.h> |
6 | #include <sys/wait.h> | 6 | #include <sys/wait.h> |
7 | #include <unistd.h> | 7 | #include <unistd.h> |
8 | #include <wlr/config.h> | ||
8 | #include <wlr/types/wlr_cursor.h> | 9 | #include <wlr/types/wlr_cursor.h> |
9 | #include <wlr/types/wlr_output_layout.h> | 10 | #include <wlr/types/wlr_output_layout.h> |
10 | #include <wlr/types/wlr_output.h> | 11 | #include <wlr/types/wlr_output.h> |
12 | #include <wlr/types/wlr_output_swapchain_manager.h> | ||
11 | #include "sway/config.h" | 13 | #include "sway/config.h" |
12 | #include "sway/input/cursor.h" | 14 | #include "sway/input/cursor.h" |
13 | #include "sway/output.h" | 15 | #include "sway/output.h" |
@@ -15,6 +17,10 @@ | |||
15 | #include "log.h" | 17 | #include "log.h" |
16 | #include "util.h" | 18 | #include "util.h" |
17 | 19 | ||
20 | #if WLR_HAS_DRM_BACKEND | ||
21 | #include <wlr/backend/drm.h> | ||
22 | #endif | ||
23 | |||
18 | int output_name_cmp(const void *item, const void *data) { | 24 | int output_name_cmp(const void *item, const void *data) { |
19 | const struct output_config *output = item; | 25 | const struct output_config *output = item; |
20 | const char *name = data; | 26 | const char *name = data; |
@@ -25,8 +31,10 @@ int output_name_cmp(const void *item, const void *data) { | |||
25 | void output_get_identifier(char *identifier, size_t len, | 31 | void output_get_identifier(char *identifier, size_t len, |
26 | struct sway_output *output) { | 32 | struct sway_output *output) { |
27 | struct wlr_output *wlr_output = output->wlr_output; | 33 | struct wlr_output *wlr_output = output->wlr_output; |
28 | snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, | 34 | snprintf(identifier, len, "%s %s %s", |
29 | wlr_output->serial); | 35 | wlr_output->make ? wlr_output->make : "Unknown", |
36 | wlr_output->model ? wlr_output->model : "Unknown", | ||
37 | wlr_output->serial ? wlr_output->serial : "Unknown"); | ||
30 | } | 38 | } |
31 | 39 | ||
32 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { | 40 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { |
@@ -58,6 +66,7 @@ struct output_config *new_output_config(const char *name) { | |||
58 | oc->width = oc->height = -1; | 66 | oc->width = oc->height = -1; |
59 | oc->refresh_rate = -1; | 67 | oc->refresh_rate = -1; |
60 | oc->custom_mode = -1; | 68 | oc->custom_mode = -1; |
69 | oc->drm_mode.type = -1; | ||
61 | oc->x = oc->y = -1; | 70 | oc->x = oc->y = -1; |
62 | oc->scale = -1; | 71 | oc->scale = -1; |
63 | oc->scale_filter = SCALE_FILTER_DEFAULT; | 72 | oc->scale_filter = SCALE_FILTER_DEFAULT; |
@@ -65,10 +74,77 @@ struct output_config *new_output_config(const char *name) { | |||
65 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | 74 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; |
66 | oc->max_render_time = -1; | 75 | oc->max_render_time = -1; |
67 | oc->adaptive_sync = -1; | 76 | oc->adaptive_sync = -1; |
77 | oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; | ||
78 | oc->power = -1; | ||
68 | return oc; | 79 | return oc; |
69 | } | 80 | } |
70 | 81 | ||
71 | void merge_output_config(struct output_config *dst, struct output_config *src) { | 82 | // supersede_output_config clears all fields in dst that were set in src |
83 | static void supersede_output_config(struct output_config *dst, struct output_config *src) { | ||
84 | if (src->enabled != -1) { | ||
85 | dst->enabled = -1; | ||
86 | } | ||
87 | if (src->width != -1) { | ||
88 | dst->width = -1; | ||
89 | } | ||
90 | if (src->height != -1) { | ||
91 | dst->height = -1; | ||
92 | } | ||
93 | if (src->x != -1) { | ||
94 | dst->x = -1; | ||
95 | } | ||
96 | if (src->y != -1) { | ||
97 | dst->y = -1; | ||
98 | } | ||
99 | if (src->scale != -1) { | ||
100 | dst->scale = -1; | ||
101 | } | ||
102 | if (src->scale_filter != SCALE_FILTER_DEFAULT) { | ||
103 | dst->scale_filter = SCALE_FILTER_DEFAULT; | ||
104 | } | ||
105 | if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { | ||
106 | dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | ||
107 | } | ||
108 | if (src->refresh_rate != -1) { | ||
109 | dst->refresh_rate = -1; | ||
110 | } | ||
111 | if (src->custom_mode != -1) { | ||
112 | dst->custom_mode = -1; | ||
113 | } | ||
114 | if (src->drm_mode.type != (uint32_t) -1) { | ||
115 | dst->drm_mode.type = -1; | ||
116 | } | ||
117 | if (src->transform != -1) { | ||
118 | dst->transform = -1; | ||
119 | } | ||
120 | if (src->max_render_time != -1) { | ||
121 | dst->max_render_time = -1; | ||
122 | } | ||
123 | if (src->adaptive_sync != -1) { | ||
124 | dst->adaptive_sync = -1; | ||
125 | } | ||
126 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
127 | dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; | ||
128 | } | ||
129 | if (src->background) { | ||
130 | free(dst->background); | ||
131 | dst->background = NULL; | ||
132 | } | ||
133 | if (src->background_option) { | ||
134 | free(dst->background_option); | ||
135 | dst->background_option = NULL; | ||
136 | } | ||
137 | if (src->background_fallback) { | ||
138 | free(dst->background_fallback); | ||
139 | dst->background_fallback = NULL; | ||
140 | } | ||
141 | if (src->power != -1) { | ||
142 | dst->power = -1; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | // merge_output_config sets all fields in dst that were set in src | ||
147 | static void merge_output_config(struct output_config *dst, struct output_config *src) { | ||
72 | if (src->enabled != -1) { | 148 | if (src->enabled != -1) { |
73 | dst->enabled = src->enabled; | 149 | dst->enabled = src->enabled; |
74 | } | 150 | } |
@@ -99,6 +175,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
99 | if (src->custom_mode != -1) { | 175 | if (src->custom_mode != -1) { |
100 | dst->custom_mode = src->custom_mode; | 176 | dst->custom_mode = src->custom_mode; |
101 | } | 177 | } |
178 | if (src->drm_mode.type != (uint32_t) -1) { | ||
179 | memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode)); | ||
180 | } | ||
102 | if (src->transform != -1) { | 181 | if (src->transform != -1) { |
103 | dst->transform = src->transform; | 182 | dst->transform = src->transform; |
104 | } | 183 | } |
@@ -108,6 +187,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
108 | if (src->adaptive_sync != -1) { | 187 | if (src->adaptive_sync != -1) { |
109 | dst->adaptive_sync = src->adaptive_sync; | 188 | dst->adaptive_sync = src->adaptive_sync; |
110 | } | 189 | } |
190 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
191 | dst->render_bit_depth = src->render_bit_depth; | ||
192 | } | ||
111 | if (src->background) { | 193 | if (src->background) { |
112 | free(dst->background); | 194 | free(dst->background); |
113 | dst->background = strdup(src->background); | 195 | dst->background = strdup(src->background); |
@@ -120,155 +202,124 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
120 | free(dst->background_fallback); | 202 | free(dst->background_fallback); |
121 | dst->background_fallback = strdup(src->background_fallback); | 203 | dst->background_fallback = strdup(src->background_fallback); |
122 | } | 204 | } |
123 | if (src->dpms_state != 0) { | 205 | if (src->power != -1) { |
124 | dst->dpms_state = src->dpms_state; | 206 | dst->power = src->power; |
125 | } | 207 | } |
126 | } | 208 | } |
127 | 209 | ||
128 | static void merge_wildcard_on_all(struct output_config *wildcard) { | 210 | void store_output_config(struct output_config *oc) { |
129 | for (int i = 0; i < config->output_configs->length; i++) { | 211 | bool merged = false; |
130 | struct output_config *oc = config->output_configs->items[i]; | 212 | bool wildcard = strcmp(oc->name, "*") == 0; |
131 | if (strcmp(wildcard->name, oc->name) != 0) { | 213 | struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name); |
132 | sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name); | ||
133 | merge_output_config(oc, wildcard); | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | 214 | ||
138 | static void merge_id_on_name(struct output_config *oc) { | ||
139 | char *id_on_name = NULL; | ||
140 | char id[128]; | 215 | char id[128]; |
141 | char *name = NULL; | 216 | if (output) { |
142 | struct sway_output *output; | ||
143 | wl_list_for_each(output, &root->all_outputs, link) { | ||
144 | name = output->wlr_output->name; | ||
145 | output_get_identifier(id, sizeof(id), output); | 217 | output_get_identifier(id, sizeof(id), output); |
146 | if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) { | ||
147 | size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1; | ||
148 | id_on_name = malloc(length); | ||
149 | if (!id_on_name) { | ||
150 | sway_log(SWAY_ERROR, "Failed to allocate id on name string"); | ||
151 | return; | ||
152 | } | ||
153 | snprintf(id_on_name, length, "%s on %s", id, name); | ||
154 | break; | ||
155 | } | ||
156 | } | 218 | } |
157 | 219 | ||
158 | if (!id_on_name) { | 220 | for (int i = 0; i < config->output_configs->length; i++) { |
159 | return; | 221 | struct output_config *old = config->output_configs->items[i]; |
160 | } | 222 | |
161 | 223 | // If the old config matches the new config's name, regardless of | |
162 | 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 |
163 | if (i >= 0) { | 225 | // config. If the new config is a wildcard, this also merges on top of |
164 | sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); | 226 | // old wildcard configs. |
165 | merge_output_config(config->output_configs->items[i], oc); | 227 | if (strcmp(old->name, oc->name) == 0) { |
166 | } else { | 228 | merge_output_config(old, oc); |
167 | // If both a name and identifier config, exist generate an id on name | 229 | merged = true; |
168 | int ni = list_seq_find(config->output_configs, output_name_cmp, name); | 230 | continue; |
169 | int ii = list_seq_find(config->output_configs, output_name_cmp, id); | ||
170 | if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) | ||
171 | || (ii >= 0 && strcmp(oc->name, name) == 0)) { | ||
172 | struct output_config *ion_oc = new_output_config(id_on_name); | ||
173 | if (ni >= 0) { | ||
174 | merge_output_config(ion_oc, config->output_configs->items[ni]); | ||
175 | } | ||
176 | if (ii >= 0) { | ||
177 | merge_output_config(ion_oc, config->output_configs->items[ii]); | ||
178 | } | ||
179 | merge_output_config(ion_oc, oc); | ||
180 | list_add(config->output_configs, ion_oc); | ||
181 | sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" | ||
182 | " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " | ||
183 | "transform %d) (bg %s %s) (dpms %d) (max render time: %d)", | ||
184 | ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, | ||
185 | ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, | ||
186 | ion_oc->transform, ion_oc->background, | ||
187 | ion_oc->background_option, ion_oc->dpms_state, | ||
188 | ion_oc->max_render_time); | ||
189 | } | 231 | } |
190 | } | ||
191 | free(id_on_name); | ||
192 | } | ||
193 | 232 | ||
194 | struct output_config *store_output_config(struct output_config *oc) { | 233 | // If the new config is a wildcard config we supersede all non-wildcard |
195 | bool wildcard = strcmp(oc->name, "*") == 0; | 234 | // configs. Old wildcard configs have already been handled above. |
196 | if (wildcard) { | 235 | if (wildcard) { |
197 | merge_wildcard_on_all(oc); | 236 | supersede_output_config(old, oc); |
198 | } else { | 237 | continue; |
199 | merge_id_on_name(oc); | 238 | } |
200 | } | ||
201 | 239 | ||
202 | int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); | 240 | // If the new config matches an output's name, and the old config |
203 | if (i >= 0) { | 241 | // matches on that output's identifier, supersede it. |
204 | sway_log(SWAY_DEBUG, "Merging on top of existing output config"); | 242 | if (output && strcmp(old->name, id) == 0 && |
205 | struct output_config *current = config->output_configs->items[i]; | 243 | strcmp(oc->name, output->wlr_output->name) == 0) { |
206 | merge_output_config(current, oc); | 244 | supersede_output_config(old, oc); |
207 | free_output_config(oc); | ||
208 | oc = current; | ||
209 | } else if (!wildcard) { | ||
210 | sway_log(SWAY_DEBUG, "Adding non-wildcard output config"); | ||
211 | i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
212 | if (i >= 0) { | ||
213 | sway_log(SWAY_DEBUG, "Merging on top of output * config"); | ||
214 | struct output_config *current = new_output_config(oc->name); | ||
215 | merge_output_config(current, config->output_configs->items[i]); | ||
216 | merge_output_config(current, oc); | ||
217 | free_output_config(oc); | ||
218 | oc = current; | ||
219 | } | 245 | } |
220 | list_add(config->output_configs, oc); | ||
221 | } else { | ||
222 | // New wildcard config. Just add it | ||
223 | sway_log(SWAY_DEBUG, "Adding output * config"); | ||
224 | list_add(config->output_configs, oc); | ||
225 | } | 246 | } |
226 | 247 | ||
227 | sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " | 248 | sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " |
228 | "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) " | 249 | "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " |
229 | "(max render time: %d)", | 250 | "(max render time: %d)", |
230 | oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, | 251 | oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, |
231 | oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), | 252 | oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), |
232 | oc->transform, oc->background, oc->background_option, oc->dpms_state, | 253 | oc->transform, oc->background, oc->background_option, oc->power, |
233 | oc->max_render_time); | 254 | oc->max_render_time); |
234 | 255 | ||
235 | return oc; | 256 | // If the configuration was not merged into an existing configuration, add |
257 | // it to the list. Otherwise we're done with it and can free it. | ||
258 | if (!merged) { | ||
259 | list_add(config->output_configs, oc); | ||
260 | } else { | ||
261 | free_output_config(oc); | ||
262 | } | ||
236 | } | 263 | } |
237 | 264 | ||
238 | static void set_mode(struct wlr_output *output, int width, int height, | 265 | static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, |
239 | float refresh_rate, bool custom) { | 266 | int width, int height, float refresh_rate, bool custom) { |
240 | // Not all floating point integers can be represented exactly | 267 | // Not all floating point integers can be represented exactly |
241 | // as (int)(1000 * mHz / 1000.f) | 268 | // as (int)(1000 * mHz / 1000.f) |
242 | // round() the result to avoid any error | 269 | // round() the result to avoid any error |
243 | int mhz = (int)round(refresh_rate * 1000); | 270 | int mhz = (int)roundf(refresh_rate * 1000); |
271 | // If no target refresh rate is given, match highest available | ||
272 | mhz = mhz <= 0 ? INT_MAX : mhz; | ||
244 | 273 | ||
245 | if (wl_list_empty(&output->modes) || custom) { | 274 | if (wl_list_empty(&output->modes) || custom) { |
246 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); | 275 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); |
247 | wlr_output_set_custom_mode(output, width, height, | 276 | wlr_output_state_set_custom_mode(pending, width, height, |
248 | refresh_rate > 0 ? mhz : 0); | 277 | refresh_rate > 0 ? mhz : 0); |
249 | return; | 278 | return; |
250 | } | 279 | } |
251 | 280 | ||
252 | struct wlr_output_mode *mode, *best = NULL; | 281 | struct wlr_output_mode *mode, *best = NULL; |
282 | int best_diff_mhz = INT_MAX; | ||
253 | wl_list_for_each(mode, &output->modes, link) { | 283 | wl_list_for_each(mode, &output->modes, link) { |
254 | if (mode->width == width && mode->height == height) { | 284 | if (mode->width == width && mode->height == height) { |
255 | if (mode->refresh == mhz) { | 285 | int diff_mhz = abs(mode->refresh - mhz); |
256 | best = mode; | 286 | if (diff_mhz < best_diff_mhz) { |
257 | break; | 287 | best_diff_mhz = diff_mhz; |
258 | } | ||
259 | if (best == NULL || mode->refresh > best->refresh) { | ||
260 | best = mode; | 288 | best = mode; |
289 | if (best_diff_mhz == 0) { | ||
290 | break; | ||
291 | } | ||
261 | } | 292 | } |
262 | } | 293 | } |
263 | } | 294 | } |
264 | if (!best) { | 295 | if (best) { |
265 | sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); | 296 | sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s", |
266 | sway_log(SWAY_INFO, "Picking preferred mode instead"); | 297 | best->width, best->height, best->refresh / 1000.f, output->name); |
267 | best = wlr_output_preferred_mode(output); | ||
268 | } else { | 298 | } else { |
269 | sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); | 299 | best = wlr_output_preferred_mode(output); |
300 | sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, " | ||
301 | "applying preferred mode (%dx%d@%.3fHz)", | ||
302 | width, height, refresh_rate, | ||
303 | best->width, best->height, best->refresh / 1000.f); | ||
304 | } | ||
305 | wlr_output_state_set_mode(pending, best); | ||
306 | } | ||
307 | |||
308 | static void set_modeline(struct wlr_output *output, | ||
309 | struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { | ||
310 | #if WLR_HAS_DRM_BACKEND | ||
311 | if (!wlr_output_is_drm(output)) { | ||
312 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | ||
313 | return; | ||
314 | } | ||
315 | sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); | ||
316 | struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); | ||
317 | if (mode) { | ||
318 | wlr_output_state_set_mode(pending, mode); | ||
270 | } | 319 | } |
271 | wlr_output_set_mode(output, best); | 320 | #else |
321 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | ||
322 | #endif | ||
272 | } | 323 | } |
273 | 324 | ||
274 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical | 325 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical |
@@ -289,23 +340,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) { | |||
289 | // 1 inch = 25.4 mm | 340 | // 1 inch = 25.4 mm |
290 | #define MM_PER_INCH 25.4 | 341 | #define MM_PER_INCH 25.4 |
291 | 342 | ||
292 | static int compute_default_scale(struct wlr_output *output) { | 343 | static int compute_default_scale(struct wlr_output *output, |
344 | struct wlr_output_state *pending) { | ||
293 | struct wlr_box box = { .width = output->width, .height = output->height }; | 345 | struct wlr_box box = { .width = output->width, .height = output->height }; |
294 | if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { | 346 | if (pending->committed & WLR_OUTPUT_STATE_MODE) { |
295 | switch (output->pending.mode_type) { | 347 | switch (pending->mode_type) { |
296 | case WLR_OUTPUT_STATE_MODE_FIXED: | 348 | case WLR_OUTPUT_STATE_MODE_FIXED: |
297 | box.width = output->pending.mode->width; | 349 | box.width = pending->mode->width; |
298 | box.height = output->pending.mode->height; | 350 | box.height = pending->mode->height; |
299 | break; | 351 | break; |
300 | case WLR_OUTPUT_STATE_MODE_CUSTOM: | 352 | case WLR_OUTPUT_STATE_MODE_CUSTOM: |
301 | box.width = output->pending.custom_mode.width; | 353 | box.width = pending->custom_mode.width; |
302 | box.height = output->pending.custom_mode.height; | 354 | box.height = pending->custom_mode.height; |
303 | break; | 355 | break; |
304 | } | 356 | } |
305 | } | 357 | } |
306 | enum wl_output_transform transform = output->transform; | 358 | enum wl_output_transform transform = output->transform; |
307 | if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { | 359 | if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) { |
308 | transform = output->pending.transform; | 360 | transform = pending->transform; |
309 | } | 361 | } |
310 | wlr_box_transform(&box, &box, transform, box.width, box.height); | 362 | wlr_box_transform(&box, &box, transform, box.width, box.height); |
311 | 363 | ||
@@ -334,42 +386,90 @@ static int compute_default_scale(struct wlr_output *output) { | |||
334 | return 2; | 386 | return 2; |
335 | } | 387 | } |
336 | 388 | ||
389 | /* Lists of formats to try, in order, when a specific render bit depth has | ||
390 | * been asked for. The second to last format in each list should always | ||
391 | * be XRGB8888, as a reliable backup in case the others are not available; | ||
392 | * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ | ||
393 | static const uint32_t *bit_depth_preferences[] = { | ||
394 | [RENDER_BIT_DEPTH_8] = (const uint32_t []){ | ||
395 | DRM_FORMAT_XRGB8888, | ||
396 | DRM_FORMAT_INVALID, | ||
397 | }, | ||
398 | [RENDER_BIT_DEPTH_10] = (const uint32_t []){ | ||
399 | DRM_FORMAT_XRGB2101010, | ||
400 | DRM_FORMAT_XBGR2101010, | ||
401 | DRM_FORMAT_XRGB8888, | ||
402 | DRM_FORMAT_INVALID, | ||
403 | }, | ||
404 | }; | ||
405 | |||
337 | static void queue_output_config(struct output_config *oc, | 406 | static void queue_output_config(struct output_config *oc, |
338 | struct sway_output *output) { | 407 | struct sway_output *output, struct wlr_output_state *pending) { |
339 | if (output == root->noop_output) { | 408 | if (output == root->fallback_output) { |
340 | return; | 409 | return; |
341 | } | 410 | } |
342 | 411 | ||
343 | struct wlr_output *wlr_output = output->wlr_output; | 412 | struct wlr_output *wlr_output = output->wlr_output; |
344 | 413 | ||
345 | if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { | 414 | if (oc && (!oc->enabled || oc->power == 0)) { |
346 | sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); | 415 | sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); |
347 | wlr_output_enable(wlr_output, false); | 416 | wlr_output_state_set_enabled(pending, false); |
348 | return; | 417 | return; |
349 | } | 418 | } |
350 | 419 | ||
351 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); | 420 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); |
352 | wlr_output_enable(wlr_output, true); | 421 | wlr_output_state_set_enabled(pending, true); |
353 | 422 | ||
354 | if (oc && oc->width > 0 && oc->height > 0) { | 423 | if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { |
424 | sway_log(SWAY_DEBUG, "Set %s modeline", | ||
425 | wlr_output->name); | ||
426 | set_modeline(wlr_output, pending, &oc->drm_mode); | ||
427 | } else if (oc && oc->width > 0 && oc->height > 0) { | ||
355 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", | 428 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", |
356 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); | 429 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); |
357 | set_mode(wlr_output, oc->width, oc->height, | 430 | set_mode(wlr_output, pending, oc->width, oc->height, |
358 | oc->refresh_rate, oc->custom_mode == 1); | 431 | oc->refresh_rate, oc->custom_mode == 1); |
359 | } else if (!wl_list_empty(&wlr_output->modes)) { | 432 | } else if (!wl_list_empty(&wlr_output->modes)) { |
360 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); | 433 | sway_log(SWAY_DEBUG, "Set preferred mode"); |
361 | wlr_output_set_mode(wlr_output, mode); | 434 | struct wlr_output_mode *preferred_mode = |
435 | wlr_output_preferred_mode(wlr_output); | ||
436 | wlr_output_state_set_mode(pending, preferred_mode); | ||
437 | |||
438 | if (!wlr_output_test_state(wlr_output, pending)) { | ||
439 | sway_log(SWAY_DEBUG, "Preferred mode rejected, " | ||
440 | "falling back to another mode"); | ||
441 | struct wlr_output_mode *mode; | ||
442 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
443 | if (mode == preferred_mode) { | ||
444 | continue; | ||
445 | } | ||
446 | |||
447 | wlr_output_state_set_mode(pending, mode); | ||
448 | if (wlr_output_test_state(wlr_output, pending)) { | ||
449 | break; | ||
450 | } | ||
451 | } | ||
452 | } | ||
362 | } | 453 | } |
363 | 454 | ||
364 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { | 455 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { |
365 | sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, | 456 | sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, |
366 | sway_wl_output_subpixel_to_string(oc->subpixel)); | 457 | sway_wl_output_subpixel_to_string(oc->subpixel)); |
367 | wlr_output_set_subpixel(wlr_output, oc->subpixel); | 458 | wlr_output_state_set_subpixel(pending, oc->subpixel); |
368 | } | 459 | } |
369 | 460 | ||
461 | enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; | ||
370 | if (oc && oc->transform >= 0) { | 462 | if (oc && oc->transform >= 0) { |
371 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); | 463 | tr = oc->transform; |
372 | wlr_output_set_transform(wlr_output, oc->transform); | 464 | #if WLR_HAS_DRM_BACKEND |
465 | } else if (wlr_output_is_drm(wlr_output)) { | ||
466 | tr = wlr_drm_connector_get_panel_orientation(wlr_output); | ||
467 | sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); | ||
468 | #endif | ||
469 | } | ||
470 | if (wlr_output->transform != tr) { | ||
471 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); | ||
472 | wlr_output_state_set_transform(pending, tr); | ||
373 | } | 473 | } |
374 | 474 | ||
375 | // Apply the scale last before the commit, because the scale auto-detection | 475 | // Apply the scale last before the commit, because the scale auto-detection |
@@ -377,50 +477,58 @@ static void queue_output_config(struct output_config *oc, | |||
377 | float scale; | 477 | float scale; |
378 | if (oc && oc->scale > 0) { | 478 | if (oc && oc->scale > 0) { |
379 | scale = oc->scale; | 479 | scale = oc->scale; |
480 | |||
481 | // The factional-scale-v1 protocol uses increments of 120ths to send | ||
482 | // the scale factor to the client. Adjust the scale so that we use the | ||
483 | // same value as the clients'. | ||
484 | float adjusted_scale = round(scale * 120) / 120; | ||
485 | if (scale != adjusted_scale) { | ||
486 | sway_log(SWAY_INFO, "Adjusting output scale from %f to %f", | ||
487 | scale, adjusted_scale); | ||
488 | scale = adjusted_scale; | ||
489 | } | ||
380 | } else { | 490 | } else { |
381 | scale = compute_default_scale(wlr_output); | 491 | scale = compute_default_scale(wlr_output, pending); |
382 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); | 492 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); |
383 | } | 493 | } |
384 | if (scale != wlr_output->scale) { | 494 | if (scale != wlr_output->scale) { |
385 | sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); | 495 | sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); |
386 | wlr_output_set_scale(wlr_output, scale); | 496 | wlr_output_state_set_scale(pending, scale); |
387 | } | 497 | } |
388 | 498 | ||
389 | if (oc && oc->adaptive_sync != -1) { | 499 | if (oc && oc->adaptive_sync != -1) { |
390 | sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, | 500 | sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, |
391 | oc->adaptive_sync); | 501 | oc->adaptive_sync); |
392 | wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); | 502 | wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); |
393 | } | 503 | if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) { |
394 | } | 504 | sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring"); |
395 | 505 | wlr_output_state_set_adaptive_sync_enabled(pending, false); | |
396 | bool apply_output_config(struct output_config *oc, struct sway_output *output) { | 506 | } |
397 | if (output == root->noop_output) { | ||
398 | return false; | ||
399 | } | 507 | } |
400 | 508 | ||
401 | struct wlr_output *wlr_output = output->wlr_output; | 509 | if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { |
402 | 510 | const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; | |
403 | // Flag to prevent the output mode event handler from calling us | 511 | assert(fmts); |
404 | output->enabling = (!oc || oc->enabled); | ||
405 | 512 | ||
406 | queue_output_config(oc, output); | 513 | for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { |
514 | wlr_output_state_set_render_format(pending, fmts[i]); | ||
515 | if (wlr_output_test_state(wlr_output, pending)) { | ||
516 | break; | ||
517 | } | ||
407 | 518 | ||
408 | if (!oc || oc->dpms_state != DPMS_OFF) { | 519 | sway_log(SWAY_DEBUG, "Preferred output format 0x%08x " |
409 | output->current_mode = wlr_output->pending.mode; | 520 | "failed to work, falling back to next in " |
521 | "list, 0x%08x", fmts[i], fmts[i + 1]); | ||
522 | } | ||
410 | } | 523 | } |
524 | } | ||
411 | 525 | ||
412 | sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); | 526 | static bool finalize_output_config(struct output_config *oc, struct sway_output *output) { |
413 | if (!wlr_output_commit(wlr_output)) { | 527 | if (output == root->fallback_output) { |
414 | // Failed to commit output changes, maybe the output is missing a CRTC. | ||
415 | // Leave the output disabled for now and try again when the output gets | ||
416 | // the mode we asked for. | ||
417 | sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name); | ||
418 | output->enabling = false; | ||
419 | return false; | 528 | return false; |
420 | } | 529 | } |
421 | 530 | ||
422 | output->enabling = false; | 531 | struct wlr_output *wlr_output = output->wlr_output; |
423 | |||
424 | if (oc && !oc->enabled) { | 532 | if (oc && !oc->enabled) { |
425 | sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); | 533 | sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); |
426 | if (output->enabled) { | 534 | if (output->enabled) { |
@@ -430,10 +538,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
430 | return true; | 538 | return true; |
431 | } | 539 | } |
432 | 540 | ||
433 | if (config->reloading) { | ||
434 | output_damage_whole(output); | ||
435 | } | ||
436 | |||
437 | if (oc) { | 541 | if (oc) { |
438 | enum scale_filter_mode scale_filter_old = output->scale_filter; | 542 | enum scale_filter_mode scale_filter_old = output->scale_filter; |
439 | switch (oc->scale_filter) { | 543 | switch (oc->scale_filter) { |
@@ -450,6 +554,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
450 | if (scale_filter_old != output->scale_filter) { | 554 | if (scale_filter_old != output->scale_filter) { |
451 | sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, | 555 | sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, |
452 | sway_output_scale_filter_to_string(output->scale_filter)); | 556 | sway_output_scale_filter_to_string(output->scale_filter)); |
557 | wlr_damage_ring_add_whole(&output->scene_output->damage_ring); | ||
453 | } | 558 | } |
454 | } | 559 | } |
455 | 560 | ||
@@ -462,12 +567,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
462 | } | 567 | } |
463 | 568 | ||
464 | // Update output->{lx, ly, width, height} | 569 | // Update output->{lx, ly, width, height} |
465 | struct wlr_box *output_box = | 570 | struct wlr_box output_box; |
466 | wlr_output_layout_get_box(root->output_layout, wlr_output); | 571 | wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box); |
467 | output->lx = output_box->x; | 572 | output->lx = output_box.x; |
468 | output->ly = output_box->y; | 573 | output->ly = output_box.y; |
469 | output->width = output_box->width; | 574 | output->width = output_box.width; |
470 | output->height = output_box->height; | 575 | output->height = output_box.height; |
471 | 576 | ||
472 | if (!output->enabled) { | 577 | if (!output->enabled) { |
473 | output_enable(output); | 578 | output_enable(output); |
@@ -479,27 +584,13 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
479 | output->max_render_time = oc->max_render_time; | 584 | output->max_render_time = oc->max_render_time; |
480 | } | 585 | } |
481 | 586 | ||
482 | // Reconfigure all devices, since input config may have been applied before | ||
483 | // this output came online, and some config items (like map_to_output) are | ||
484 | // dependent on an output being present. | ||
485 | input_manager_configure_all_inputs(); | ||
486 | return true; | 587 | return true; |
487 | } | 588 | } |
488 | 589 | ||
489 | bool test_output_config(struct output_config *oc, struct sway_output *output) { | ||
490 | if (output == root->noop_output) { | ||
491 | return false; | ||
492 | } | ||
493 | |||
494 | queue_output_config(oc, output); | ||
495 | bool ok = wlr_output_test(output->wlr_output); | ||
496 | wlr_output_rollback(output->wlr_output); | ||
497 | return ok; | ||
498 | } | ||
499 | |||
500 | static void default_output_config(struct output_config *oc, | 590 | static void default_output_config(struct output_config *oc, |
501 | struct wlr_output *wlr_output) { | 591 | struct wlr_output *wlr_output) { |
502 | oc->enabled = 1; | 592 | oc->enabled = 1; |
593 | oc->power = 1; | ||
503 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); | 594 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); |
504 | if (mode != NULL) { | 595 | if (mode != NULL) { |
505 | oc->width = mode->width; | 596 | oc->width = mode->width; |
@@ -512,147 +603,170 @@ static void default_output_config(struct output_config *oc, | |||
512 | struct sway_output *output = wlr_output->data; | 603 | struct sway_output *output = wlr_output->data; |
513 | oc->subpixel = output->detected_subpixel; | 604 | oc->subpixel = output->detected_subpixel; |
514 | oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; | 605 | oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; |
515 | oc->dpms_state = DPMS_ON; | ||
516 | oc->max_render_time = 0; | 606 | oc->max_render_time = 0; |
517 | } | 607 | } |
518 | 608 | ||
519 | static struct output_config *get_output_config(char *identifier, | 609 | // find_output_config returns a merged output_config containing all stored |
520 | 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) { | ||
521 | const char *name = sway_output->wlr_output->name; | 612 | const char *name = sway_output->wlr_output->name; |
613 | struct output_config *oc = NULL; | ||
522 | 614 | ||
523 | struct output_config *oc_id_on_name = NULL; | 615 | struct output_config *result = new_output_config(name); |
524 | struct output_config *oc_name = NULL; | 616 | if (config->reloading) { |
525 | struct output_config *oc_id = NULL; | 617 | default_output_config(result, sway_output->wlr_output); |
618 | } | ||
526 | 619 | ||
527 | size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; | 620 | char id[128]; |
528 | char *id_on_name = malloc(length); | 621 | output_get_identifier(id, sizeof(id), sway_output); |
529 | snprintf(id_on_name, length, "%s on %s", identifier, name); | ||
530 | int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); | ||
531 | if (i >= 0) { | ||
532 | oc_id_on_name = config->output_configs->items[i]; | ||
533 | } else { | ||
534 | i = list_seq_find(config->output_configs, output_name_cmp, name); | ||
535 | if (i >= 0) { | ||
536 | oc_name = config->output_configs->items[i]; | ||
537 | } | ||
538 | 622 | ||
539 | i = list_seq_find(config->output_configs, output_name_cmp, identifier); | 623 | int i; |
540 | if (i >= 0) { | 624 | bool match = false; |
541 | oc_id = config->output_configs->items[i]; | 625 | if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) { |
542 | } | 626 | match = true; |
627 | oc = config->output_configs->items[i]; | ||
628 | merge_output_config(result, oc); | ||
543 | } | 629 | } |
544 | 630 | if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) { | |
545 | struct output_config *result = new_output_config("temp"); | 631 | match = true; |
546 | if (config->reloading) { | 632 | oc = config->output_configs->items[i]; |
547 | default_output_config(result, sway_output->wlr_output); | 633 | merge_output_config(result, oc); |
548 | } | 634 | } |
549 | if (oc_id_on_name) { | 635 | if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) { |
550 | // Already have an identifier on name config, use that | 636 | match = true; |
551 | free(result->name); | 637 | oc = config->output_configs->items[i]; |
552 | result->name = strdup(id_on_name); | 638 | merge_output_config(result, oc); |
553 | merge_output_config(result, oc_id_on_name); | 639 | } |
554 | } else if (oc_name && oc_id) { | 640 | |
555 | // Generate a config named `<identifier> on <name>` which contains a | 641 | if (!match && !config->reloading) { |
556 | // merged copy of the identifier on name. This will make sure that both | 642 | // No name, identifier, or wildcard config. Since we are not |
557 | // identifier and name configs are respected, with identifier getting | 643 | // reloading with defaults, the output config will be empty, so |
558 | // priority | 644 | // just return NULL |
559 | struct output_config *temp = new_output_config(id_on_name); | 645 | free_output_config(result); |
560 | merge_output_config(temp, oc_name); | 646 | return NULL; |
561 | merge_output_config(temp, oc_id); | ||
562 | list_add(config->output_configs, temp); | ||
563 | |||
564 | free(result->name); | ||
565 | result->name = strdup(id_on_name); | ||
566 | merge_output_config(result, temp); | ||
567 | |||
568 | sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" | ||
569 | " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" | ||
570 | " (dpms %d) (max render time: %d)", result->name, result->enabled, | ||
571 | result->width, result->height, result->refresh_rate, | ||
572 | result->x, result->y, result->scale, result->transform, | ||
573 | result->background, result->background_option, result->dpms_state, | ||
574 | result->max_render_time); | ||
575 | } else if (oc_name) { | ||
576 | // No identifier config, just return a copy of the name config | ||
577 | free(result->name); | ||
578 | result->name = strdup(name); | ||
579 | merge_output_config(result, oc_name); | ||
580 | } else if (oc_id) { | ||
581 | // No name config, just return a copy of the identifier config | ||
582 | free(result->name); | ||
583 | result->name = strdup(identifier); | ||
584 | merge_output_config(result, oc_id); | ||
585 | } else { | ||
586 | i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
587 | if (i >= 0) { | ||
588 | // No name or identifier config, but there is a wildcard config | ||
589 | free(result->name); | ||
590 | result->name = strdup("*"); | ||
591 | merge_output_config(result, config->output_configs->items[i]); | ||
592 | } else if (!config->reloading) { | ||
593 | // No name, identifier, or wildcard config. Since we are not | ||
594 | // reloading with defaults, the output config will be empty, so | ||
595 | // just return NULL | ||
596 | free_output_config(result); | ||
597 | result = NULL; | ||
598 | } | ||
599 | } | 647 | } |
600 | 648 | ||
601 | free(id_on_name); | ||
602 | return result; | 649 | return result; |
603 | } | 650 | } |
604 | 651 | ||
605 | struct output_config *find_output_config(struct sway_output *output) { | 652 | bool apply_output_configs(struct matched_output_config *configs, |
606 | char id[128]; | 653 | size_t configs_len, bool test_only) { |
607 | output_get_identifier(id, sizeof(id), output); | 654 | struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); |
608 | return get_output_config(id, output); | 655 | if (!states) { |
609 | } | 656 | return false; |
657 | } | ||
610 | 658 | ||
611 | void apply_output_config_to_outputs(struct output_config *oc) { | 659 | sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len); |
612 | // Try to find the output container and apply configuration now. If | 660 | for (size_t idx = 0; idx < configs_len; idx++) { |
613 | // this is during startup then there will be no container and config | 661 | struct matched_output_config *cfg = &configs[idx]; |
614 | // will be applied during normal "new output" event from wlroots. | 662 | struct wlr_backend_output_state *backend_state = &states[idx]; |
615 | bool wildcard = strcmp(oc->name, "*") == 0; | ||
616 | char id[128]; | ||
617 | struct sway_output *sway_output, *tmp; | ||
618 | wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { | ||
619 | char *name = sway_output->wlr_output->name; | ||
620 | output_get_identifier(id, sizeof(id), sway_output); | ||
621 | if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { | ||
622 | struct output_config *current = get_output_config(id, sway_output); | ||
623 | if (!current) { | ||
624 | // No stored output config matched, apply oc directly | ||
625 | sway_log(SWAY_DEBUG, "Applying oc directly"); | ||
626 | current = new_output_config(oc->name); | ||
627 | merge_output_config(current, oc); | ||
628 | } | ||
629 | apply_output_config(current, sway_output); | ||
630 | free_output_config(current); | ||
631 | 663 | ||
632 | if (!wildcard) { | 664 | backend_state->output = cfg->output->wlr_output; |
633 | // Stop looking if the output config isn't applicable to all | 665 | wlr_output_state_init(&backend_state->base); |
634 | // outputs | 666 | |
635 | break; | 667 | sway_log(SWAY_DEBUG, "Preparing config for %s", |
636 | } | 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; | ||
637 | } | 700 | } |
638 | } | 701 | } |
639 | 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 | |||
640 | struct sway_seat *seat; | 735 | struct sway_seat *seat; |
641 | wl_list_for_each(seat, &server.input->seats, link) { | 736 | wl_list_for_each(seat, &server.input->seats, link) { |
642 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 737 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
643 | cursor_rebase(seat->cursor); | 738 | cursor_rebase(seat->cursor); |
644 | } | 739 | } |
740 | |||
741 | return ok; | ||
645 | } | 742 | } |
646 | 743 | ||
647 | void reset_outputs(void) { | 744 | void apply_all_output_configs(void) { |
648 | struct output_config *oc = NULL; | 745 | size_t configs_len = wl_list_length(&root->all_outputs); |
649 | int i = list_seq_find(config->output_configs, output_name_cmp, "*"); | 746 | struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); |
650 | if (i >= 0) { | 747 | if (!configs) { |
651 | oc = config->output_configs->items[i]; | 748 | return; |
652 | } else { | ||
653 | oc = store_output_config(new_output_config("*")); | ||
654 | } | 749 | } |
655 | 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); | ||
656 | } | 770 | } |
657 | 771 | ||
658 | void free_output_config(struct output_config *oc) { | 772 | void free_output_config(struct output_config *oc) { |
@@ -702,6 +816,8 @@ static bool _spawn_swaybg(char **command) { | |||
702 | sway_log_errno(SWAY_ERROR, "fork failed"); | 816 | sway_log_errno(SWAY_ERROR, "fork failed"); |
703 | return false; | 817 | return false; |
704 | } else if (pid == 0) { | 818 | } else if (pid == 0) { |
819 | restore_nofile_limit(); | ||
820 | |||
705 | pid = fork(); | 821 | pid = fork(); |
706 | if (pid < 0) { | 822 | if (pid < 0) { |
707 | sway_log_errno(SWAY_ERROR, "fork failed"); | 823 | sway_log_errno(SWAY_ERROR, "fork failed"); |
@@ -717,7 +833,9 @@ static bool _spawn_swaybg(char **command) { | |||
717 | setenv("WAYLAND_SOCKET", wayland_socket_str, true); | 833 | setenv("WAYLAND_SOCKET", wayland_socket_str, true); |
718 | 834 | ||
719 | execvp(command[0], command); | 835 | execvp(command[0], command); |
720 | 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]); | ||
721 | _exit(EXIT_FAILURE); | 839 | _exit(EXIT_FAILURE); |
722 | } | 840 | } |
723 | _exit(EXIT_SUCCESS); | 841 | _exit(EXIT_SUCCESS); |
@@ -727,12 +845,13 @@ static bool _spawn_swaybg(char **command) { | |||
727 | sway_log_errno(SWAY_ERROR, "close failed"); | 845 | sway_log_errno(SWAY_ERROR, "close failed"); |
728 | return false; | 846 | return false; |
729 | } | 847 | } |
730 | if (waitpid(pid, NULL, 0) < 0) { | 848 | int fork_status = 0; |
849 | if (waitpid(pid, &fork_status, 0) < 0) { | ||
731 | sway_log_errno(SWAY_ERROR, "waitpid failed"); | 850 | sway_log_errno(SWAY_ERROR, "waitpid failed"); |
732 | return false; | 851 | return false; |
733 | } | 852 | } |
734 | 853 | ||
735 | return true; | 854 | return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS; |
736 | } | 855 | } |
737 | 856 | ||
738 | 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 409160c5..e16b4fa8 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -1,9 +1,9 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
5 | #include <strings.h> | 4 | #include <strings.h> |
6 | #include <pcre.h> | 5 | #define PCRE2_CODE_UNIT_WIDTH 8 |
6 | #include <pcre2.h> | ||
7 | #include "sway/criteria.h" | 7 | #include "sway/criteria.h" |
8 | #include "sway/tree/container.h" | 8 | #include "sway/tree/container.h" |
9 | #include "sway/config.h" | 9 | #include "sway/config.h" |
@@ -18,6 +18,7 @@ | |||
18 | bool criteria_is_empty(struct criteria *criteria) { | 18 | bool criteria_is_empty(struct criteria *criteria) { |
19 | return !criteria->title | 19 | return !criteria->title |
20 | && !criteria->shell | 20 | && !criteria->shell |
21 | && !criteria->all | ||
21 | && !criteria->app_id | 22 | && !criteria->app_id |
22 | && !criteria->con_mark | 23 | && !criteria->con_mark |
23 | && !criteria->con_id | 24 | && !criteria->con_id |
@@ -40,17 +41,19 @@ bool criteria_is_empty(struct criteria *criteria) { | |||
40 | char *error = NULL; | 41 | char *error = NULL; |
41 | 42 | ||
42 | // Returns error string on failure or NULL otherwise. | 43 | // Returns error string on failure or NULL otherwise. |
43 | static bool generate_regex(pcre **regex, char *value) { | 44 | static bool generate_regex(pcre2_code **regex, char *value) { |
44 | const char *reg_err; | 45 | int errorcode; |
45 | int offset; | 46 | PCRE2_SIZE offset; |
46 | |||
47 | *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, ®_err, &offset, NULL); | ||
48 | 47 | ||
48 | *regex = pcre2_compile((PCRE2_SPTR)value, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_UCP, &errorcode, &offset, NULL); | ||
49 | if (!*regex) { | 49 | if (!*regex) { |
50 | PCRE2_UCHAR buffer[256]; | ||
51 | pcre2_get_error_message(errorcode, buffer, sizeof(buffer)); | ||
52 | |||
50 | const char *fmt = "Regex compilation for '%s' failed: %s"; | 53 | const char *fmt = "Regex compilation for '%s' failed: %s"; |
51 | int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; | 54 | int len = strlen(fmt) + strlen(value) + strlen((char*) buffer) - 3; |
52 | error = malloc(len); | 55 | error = malloc(len); |
53 | snprintf(error, len, fmt, value, reg_err); | 56 | snprintf(error, len, fmt, value, buffer); |
54 | return false; | 57 | return false; |
55 | } | 58 | } |
56 | 59 | ||
@@ -66,7 +69,7 @@ static bool pattern_create(struct pattern **pattern, char *value) { | |||
66 | if (strcmp(value, "__focused__") == 0) { | 69 | if (strcmp(value, "__focused__") == 0) { |
67 | (*pattern)->match_type = PATTERN_FOCUSED; | 70 | (*pattern)->match_type = PATTERN_FOCUSED; |
68 | } else { | 71 | } else { |
69 | (*pattern)->match_type = PATTERN_PCRE; | 72 | (*pattern)->match_type = PATTERN_PCRE2; |
70 | if (!generate_regex(&(*pattern)->regex, value)) { | 73 | if (!generate_regex(&(*pattern)->regex, value)) { |
71 | return false; | 74 | return false; |
72 | }; | 75 | }; |
@@ -77,7 +80,7 @@ static bool pattern_create(struct pattern **pattern, char *value) { | |||
77 | static void pattern_destroy(struct pattern *pattern) { | 80 | static void pattern_destroy(struct pattern *pattern) { |
78 | if (pattern) { | 81 | if (pattern) { |
79 | if (pattern->regex) { | 82 | if (pattern->regex) { |
80 | pcre_free(pattern->regex); | 83 | pcre2_code_free(pattern->regex); |
81 | } | 84 | } |
82 | free(pattern); | 85 | free(pattern); |
83 | } | 86 | } |
@@ -93,14 +96,18 @@ void criteria_destroy(struct criteria *criteria) { | |||
93 | pattern_destroy(criteria->window_role); | 96 | pattern_destroy(criteria->window_role); |
94 | #endif | 97 | #endif |
95 | pattern_destroy(criteria->con_mark); | 98 | pattern_destroy(criteria->con_mark); |
96 | free(criteria->workspace); | 99 | pattern_destroy(criteria->workspace); |
100 | free(criteria->target); | ||
97 | free(criteria->cmdlist); | 101 | free(criteria->cmdlist); |
98 | free(criteria->raw); | 102 | free(criteria->raw); |
99 | free(criteria); | 103 | free(criteria); |
100 | } | 104 | } |
101 | 105 | ||
102 | static int regex_cmp(const char *item, const pcre *regex) { | 106 | static int regex_cmp(const char *item, const pcre2_code *regex) { |
103 | return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); | 107 | pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL); |
108 | int result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL); | ||
109 | pcre2_match_data_free(match_data); | ||
110 | return result; | ||
104 | } | 111 | } |
105 | 112 | ||
106 | #if HAVE_XWAYLAND | 113 | #if HAVE_XWAYLAND |
@@ -155,7 +162,7 @@ static bool criteria_matches_container(struct criteria *criteria, | |||
155 | bool exists = false; | 162 | bool exists = false; |
156 | struct sway_container *con = container; | 163 | struct sway_container *con = container; |
157 | for (int i = 0; i < con->marks->length; ++i) { | 164 | for (int i = 0; i < con->marks->length; ++i) { |
158 | if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) == 0) { | 165 | if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) { |
159 | exists = true; | 166 | exists = true; |
160 | break; | 167 | break; |
161 | } | 168 | } |
@@ -183,7 +190,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
183 | if (criteria->title) { | 190 | if (criteria->title) { |
184 | const char *title = view_get_title(view); | 191 | const char *title = view_get_title(view); |
185 | if (!title) { | 192 | if (!title) { |
186 | return false; | 193 | title = ""; |
187 | } | 194 | } |
188 | 195 | ||
189 | switch (criteria->title->match_type) { | 196 | switch (criteria->title->match_type) { |
@@ -192,8 +199,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
192 | return false; | 199 | return false; |
193 | } | 200 | } |
194 | break; | 201 | break; |
195 | case PATTERN_PCRE: | 202 | case PATTERN_PCRE2: |
196 | if (regex_cmp(title, criteria->title->regex) != 0) { | 203 | if (regex_cmp(title, criteria->title->regex) < 0) { |
197 | return false; | 204 | return false; |
198 | } | 205 | } |
199 | break; | 206 | break; |
@@ -203,7 +210,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
203 | if (criteria->shell) { | 210 | if (criteria->shell) { |
204 | const char *shell = view_get_shell(view); | 211 | const char *shell = view_get_shell(view); |
205 | if (!shell) { | 212 | if (!shell) { |
206 | return false; | 213 | shell = ""; |
207 | } | 214 | } |
208 | 215 | ||
209 | switch (criteria->shell->match_type) { | 216 | switch (criteria->shell->match_type) { |
@@ -212,8 +219,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
212 | return false; | 219 | return false; |
213 | } | 220 | } |
214 | break; | 221 | break; |
215 | case PATTERN_PCRE: | 222 | case PATTERN_PCRE2: |
216 | if (regex_cmp(shell, criteria->shell->regex) != 0) { | 223 | if (regex_cmp(shell, criteria->shell->regex) < 0) { |
217 | return false; | 224 | return false; |
218 | } | 225 | } |
219 | break; | 226 | break; |
@@ -223,7 +230,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
223 | if (criteria->app_id) { | 230 | if (criteria->app_id) { |
224 | const char *app_id = view_get_app_id(view); | 231 | const char *app_id = view_get_app_id(view); |
225 | if (!app_id) { | 232 | if (!app_id) { |
226 | return false; | 233 | app_id = ""; |
227 | } | 234 | } |
228 | 235 | ||
229 | switch (criteria->app_id->match_type) { | 236 | switch (criteria->app_id->match_type) { |
@@ -232,8 +239,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
232 | return false; | 239 | return false; |
233 | } | 240 | } |
234 | break; | 241 | break; |
235 | case PATTERN_PCRE: | 242 | case PATTERN_PCRE2: |
236 | if (regex_cmp(app_id, criteria->app_id->regex) != 0) { | 243 | if (regex_cmp(app_id, criteria->app_id->regex) < 0) { |
237 | return false; | 244 | return false; |
238 | } | 245 | } |
239 | break; | 246 | break; |
@@ -255,7 +262,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
255 | if (criteria->class) { | 262 | if (criteria->class) { |
256 | const char *class = view_get_class(view); | 263 | const char *class = view_get_class(view); |
257 | if (!class) { | 264 | if (!class) { |
258 | return false; | 265 | class = ""; |
259 | } | 266 | } |
260 | 267 | ||
261 | switch (criteria->class->match_type) { | 268 | switch (criteria->class->match_type) { |
@@ -264,8 +271,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
264 | return false; | 271 | return false; |
265 | } | 272 | } |
266 | break; | 273 | break; |
267 | case PATTERN_PCRE: | 274 | case PATTERN_PCRE2: |
268 | if (regex_cmp(class, criteria->class->regex) != 0) { | 275 | if (regex_cmp(class, criteria->class->regex) < 0) { |
269 | return false; | 276 | return false; |
270 | } | 277 | } |
271 | break; | 278 | break; |
@@ -275,17 +282,17 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
275 | if (criteria->instance) { | 282 | if (criteria->instance) { |
276 | const char *instance = view_get_instance(view); | 283 | const char *instance = view_get_instance(view); |
277 | if (!instance) { | 284 | if (!instance) { |
278 | return false; | 285 | instance = ""; |
279 | } | 286 | } |
280 | 287 | ||
281 | switch (criteria->instance->match_type) { | 288 | switch (criteria->instance->match_type) { |
282 | case PATTERN_FOCUSED: | 289 | case PATTERN_FOCUSED: |
283 | if (focused && strcmp(instance, view_get_instance(focused))) { | 290 | if (focused && lenient_strcmp(instance, view_get_instance(focused))) { |
284 | return false; | 291 | return false; |
285 | } | 292 | } |
286 | break; | 293 | break; |
287 | case PATTERN_PCRE: | 294 | case PATTERN_PCRE2: |
288 | if (regex_cmp(instance, criteria->instance->regex) != 0) { | 295 | if (regex_cmp(instance, criteria->instance->regex) < 0) { |
289 | return false; | 296 | return false; |
290 | } | 297 | } |
291 | break; | 298 | break; |
@@ -295,17 +302,17 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
295 | if (criteria->window_role) { | 302 | if (criteria->window_role) { |
296 | const char *window_role = view_get_window_role(view); | 303 | const char *window_role = view_get_window_role(view); |
297 | if (!window_role) { | 304 | if (!window_role) { |
298 | return false; | 305 | window_role = ""; |
299 | } | 306 | } |
300 | 307 | ||
301 | switch (criteria->window_role->match_type) { | 308 | switch (criteria->window_role->match_type) { |
302 | case PATTERN_FOCUSED: | 309 | case PATTERN_FOCUSED: |
303 | if (focused && strcmp(window_role, view_get_window_role(focused))) { | 310 | if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) { |
304 | return false; | 311 | return false; |
305 | } | 312 | } |
306 | break; | 313 | break; |
307 | case PATTERN_PCRE: | 314 | case PATTERN_PCRE2: |
308 | if (regex_cmp(window_role, criteria->window_role->regex) != 0) { | 315 | if (regex_cmp(window_role, criteria->window_role->regex) < 0) { |
309 | return false; | 316 | return false; |
310 | } | 317 | } |
311 | break; | 318 | break; |
@@ -351,7 +358,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
351 | } | 358 | } |
352 | 359 | ||
353 | if (criteria->workspace) { | 360 | if (criteria->workspace) { |
354 | struct sway_workspace *ws = view->container->workspace; | 361 | struct sway_workspace *ws = view->container->pending.workspace; |
355 | if (!ws) { | 362 | if (!ws) { |
356 | return false; | 363 | return false; |
357 | } | 364 | } |
@@ -359,12 +366,12 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
359 | switch (criteria->workspace->match_type) { | 366 | switch (criteria->workspace->match_type) { |
360 | case PATTERN_FOCUSED: | 367 | case PATTERN_FOCUSED: |
361 | if (focused && | 368 | if (focused && |
362 | strcmp(ws->name, focused->container->workspace->name)) { | 369 | strcmp(ws->name, focused->container->pending.workspace->name)) { |
363 | return false; | 370 | return false; |
364 | } | 371 | } |
365 | break; | 372 | break; |
366 | case PATTERN_PCRE: | 373 | case PATTERN_PCRE2: |
367 | if (regex_cmp(ws->name, criteria->workspace->regex) != 0) { | 374 | if (regex_cmp(ws->name, criteria->workspace->regex) < 0) { |
368 | return false; | 375 | return false; |
369 | } | 376 | } |
370 | break; | 377 | break; |
@@ -449,6 +456,7 @@ static enum atom_name parse_window_type(const char *type) { | |||
449 | #endif | 456 | #endif |
450 | 457 | ||
451 | enum criteria_token { | 458 | enum criteria_token { |
459 | T_ALL, | ||
452 | T_APP_ID, | 460 | T_APP_ID, |
453 | T_CON_ID, | 461 | T_CON_ID, |
454 | T_CON_MARK, | 462 | T_CON_MARK, |
@@ -471,7 +479,9 @@ enum criteria_token { | |||
471 | }; | 479 | }; |
472 | 480 | ||
473 | static enum criteria_token token_from_name(char *name) { | 481 | static enum criteria_token token_from_name(char *name) { |
474 | if (strcmp(name, "app_id") == 0) { | 482 | if (strcmp(name, "all") == 0) { |
483 | return T_ALL; | ||
484 | } else if (strcmp(name, "app_id") == 0) { | ||
475 | return T_APP_ID; | 485 | return T_APP_ID; |
476 | } else if (strcmp(name, "con_id") == 0) { | 486 | } else if (strcmp(name, "con_id") == 0) { |
477 | return T_CON_ID; | 487 | return T_CON_ID; |
@@ -517,8 +527,8 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
517 | return false; | 527 | return false; |
518 | } | 528 | } |
519 | 529 | ||
520 | // Require value, unless token is floating or tiled | 530 | // Require value, unless token is all, floating or tiled |
521 | if (!value && token != T_FLOATING && token != T_TILING) { | 531 | if (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) { |
522 | const char *fmt = "Token '%s' requires a value"; | 532 | const char *fmt = "Token '%s' requires a value"; |
523 | int len = strlen(fmt) + strlen(name) - 1; | 533 | int len = strlen(fmt) + strlen(name) - 1; |
524 | error = malloc(len); | 534 | error = malloc(len); |
@@ -528,6 +538,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
528 | 538 | ||
529 | char *endptr = NULL; | 539 | char *endptr = NULL; |
530 | switch (token) { | 540 | switch (token) { |
541 | case T_ALL: | ||
542 | criteria->all = true; | ||
543 | break; | ||
531 | case T_TITLE: | 544 | case T_TITLE: |
532 | pattern_create(&criteria->title, value); | 545 | pattern_create(&criteria->title, value); |
533 | break; | 546 | break; |
@@ -676,7 +689,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
676 | } | 689 | } |
677 | name = calloc(head - namestart + 1, 1); | 690 | name = calloc(head - namestart + 1, 1); |
678 | if (head != namestart) { | 691 | if (head != namestart) { |
679 | strncpy(name, namestart, head - namestart); | 692 | memcpy(name, namestart, head - namestart); |
680 | } | 693 | } |
681 | // Parse token value | 694 | // Parse token value |
682 | skip_spaces(&head); | 695 | skip_spaces(&head); |
@@ -703,7 +716,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
703 | } | 716 | } |
704 | } | 717 | } |
705 | value = calloc(head - valuestart + 1, 1); | 718 | value = calloc(head - valuestart + 1, 1); |
706 | strncpy(value, valuestart, head - valuestart); | 719 | memcpy(value, valuestart, head - valuestart); |
707 | if (in_quotes) { | 720 | if (in_quotes) { |
708 | ++head; | 721 | ++head; |
709 | in_quotes = false; | 722 | in_quotes = false; |
@@ -734,7 +747,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
734 | ++head; | 747 | ++head; |
735 | int len = head - raw; | 748 | int len = head - raw; |
736 | criteria->raw = calloc(len + 1, 1); | 749 | criteria->raw = calloc(len + 1, 1); |
737 | strncpy(criteria->raw, raw, len); | 750 | memcpy(criteria->raw, raw, len); |
738 | return criteria; | 751 | return criteria; |
739 | 752 | ||
740 | cleanup: | 753 | cleanup: |
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c deleted file mode 100644 index ec45d80a..00000000 --- a/sway/desktop/desktop.c +++ /dev/null | |||
@@ -1,39 +0,0 @@ | |||
1 | #include "sway/tree/container.h" | ||
2 | #include "sway/desktop.h" | ||
3 | #include "sway/output.h" | ||
4 | |||
5 | void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, | ||
6 | bool whole) { | ||
7 | for (int i = 0; i < root->outputs->length; ++i) { | ||
8 | struct sway_output *output = root->outputs->items[i]; | ||
9 | struct wlr_box *output_box = wlr_output_layout_get_box( | ||
10 | root->output_layout, output->wlr_output); | ||
11 | output_damage_surface(output, lx - output_box->x, | ||
12 | ly - output_box->y, surface, whole); | ||
13 | } | ||
14 | } | ||
15 | |||
16 | void desktop_damage_whole_container(struct sway_container *con) { | ||
17 | for (int i = 0; i < root->outputs->length; ++i) { | ||
18 | struct sway_output *output = root->outputs->items[i]; | ||
19 | output_damage_whole_container(output, con); | ||
20 | } | ||
21 | } | ||
22 | |||
23 | void desktop_damage_box(struct wlr_box *box) { | ||
24 | for (int i = 0; i < root->outputs->length; ++i) { | ||
25 | struct sway_output *output = root->outputs->items[i]; | ||
26 | output_damage_box(output, box); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | void desktop_damage_view(struct sway_view *view) { | ||
31 | desktop_damage_whole_container(view->container); | ||
32 | struct wlr_box box = { | ||
33 | .x = view->container->current.content_x - view->geometry.x, | ||
34 | .y = view->container->current.content_y - view->geometry.y, | ||
35 | .width = view->surface->current.width, | ||
36 | .height = view->surface->current.height, | ||
37 | }; | ||
38 | desktop_damage_box(&box); | ||
39 | } | ||
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index a5cfd5b2..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,43 +34,43 @@ 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->view = view_from_wlr_surface(wlr_inhibitor->surface); | 38 | inhibitor->wlr_inhibitor = wlr_inhibitor; |
40 | wl_list_insert(&manager->inhibitors, &inhibitor->link); | 39 | wl_list_insert(&manager->inhibitors, &inhibitor->link); |
41 | 40 | ||
42 | inhibitor->destroy.notify = handle_destroy; | 41 | inhibitor->destroy.notify = handle_destroy; |
43 | wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); | 42 | wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); |
44 | 43 | ||
45 | sway_idle_inhibit_v1_check_active(manager); | 44 | sway_idle_inhibit_v1_check_active(); |
46 | } | 45 | } |
47 | 46 | ||
48 | void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, | 47 | void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, |
49 | enum sway_idle_inhibit_mode mode) { | 48 | enum sway_idle_inhibit_mode mode) { |
49 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
50 | |||
50 | struct sway_idle_inhibitor_v1 *inhibitor = | 51 | struct sway_idle_inhibitor_v1 *inhibitor = |
51 | calloc(1, sizeof(struct sway_idle_inhibitor_v1)); | 52 | calloc(1, sizeof(struct sway_idle_inhibitor_v1)); |
52 | if (!inhibitor) { | 53 | if (!inhibitor) { |
53 | return; | 54 | return; |
54 | } | 55 | } |
55 | 56 | ||
56 | inhibitor->manager = server.idle_inhibit_manager_v1; | ||
57 | inhibitor->mode = mode; | 57 | inhibitor->mode = mode; |
58 | inhibitor->view = view; | 58 | inhibitor->view = view; |
59 | wl_list_insert(&inhibitor->manager->inhibitors, &inhibitor->link); | 59 | wl_list_insert(&manager->inhibitors, &inhibitor->link); |
60 | 60 | ||
61 | inhibitor->destroy.notify = handle_destroy; | 61 | inhibitor->destroy.notify = handle_destroy; |
62 | wl_signal_add(&view->events.unmap, &inhibitor->destroy); | 62 | wl_signal_add(&view->events.unmap, &inhibitor->destroy); |
63 | 63 | ||
64 | sway_idle_inhibit_v1_check_active(inhibitor->manager); | 64 | sway_idle_inhibit_v1_check_active(); |
65 | } | 65 | } |
66 | 66 | ||
67 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( | 67 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( |
68 | struct sway_view *view) { | 68 | struct sway_view *view) { |
69 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
69 | struct sway_idle_inhibitor_v1 *inhibitor; | 70 | struct sway_idle_inhibitor_v1 *inhibitor; |
70 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, | 71 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { |
71 | link) { | 72 | if (inhibitor->mode != INHIBIT_IDLE_APPLICATION && |
72 | if (inhibitor->view == view && | 73 | inhibitor->view == view) { |
73 | inhibitor->mode != INHIBIT_IDLE_APPLICATION) { | ||
74 | return inhibitor; | 74 | return inhibitor; |
75 | } | 75 | } |
76 | } | 76 | } |
@@ -79,11 +79,11 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( | |||
79 | 79 | ||
80 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( | 80 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( |
81 | struct sway_view *view) { | 81 | struct sway_view *view) { |
82 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
82 | struct sway_idle_inhibitor_v1 *inhibitor; | 83 | struct sway_idle_inhibitor_v1 *inhibitor; |
83 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, | 84 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { |
84 | link) { | 85 | if (inhibitor->mode == INHIBIT_IDLE_APPLICATION && |
85 | if (inhibitor->view == view && | 86 | view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { |
86 | inhibitor->mode == INHIBIT_IDLE_APPLICATION) { | ||
87 | return inhibitor; | 87 | return inhibitor; |
88 | } | 88 | } |
89 | } | 89 | } |
@@ -104,10 +104,10 @@ void sway_idle_inhibit_v1_user_inhibitor_destroy( | |||
104 | 104 | ||
105 | bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { | 105 | bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { |
106 | switch (inhibitor->mode) { | 106 | switch (inhibitor->mode) { |
107 | case INHIBIT_IDLE_APPLICATION: | 107 | case INHIBIT_IDLE_APPLICATION:; |
108 | // If there is no view associated with the inhibitor, assume visible | 108 | // If there is no view associated with the inhibitor, assume visible |
109 | return !inhibitor->view || !inhibitor->view->container || | 109 | struct sway_view *view = view_from_wlr_surface(inhibitor->wlr_inhibitor->surface); |
110 | view_is_visible(inhibitor->view); | 110 | return !view || !view->container || view_is_visible(view); |
111 | case INHIBIT_IDLE_FOCUS:; | 111 | case INHIBIT_IDLE_FOCUS:; |
112 | struct sway_seat *seat = NULL; | 112 | struct sway_seat *seat = NULL; |
113 | wl_list_for_each(seat, &server.input->seats, link) { | 113 | wl_list_for_each(seat, &server.input->seats, link) { |
@@ -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 d4ca4fb4..4b2584b6 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c | |||
@@ -2,11 +2,13 @@ | |||
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
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_box.h> | ||
6 | #include <wlr/types/wlr_layer_shell_v1.h> | 5 | #include <wlr/types/wlr_layer_shell_v1.h> |
7 | #include <wlr/types/wlr_output_damage.h> | ||
8 | #include <wlr/types/wlr_output.h> | 6 | #include <wlr/types/wlr_output.h> |
7 | #include <wlr/types/wlr_scene.h> | ||
8 | #include <wlr/types/wlr_subcompositor.h> | ||
9 | #include <wlr/types/wlr_xdg_shell.h> | ||
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,155 +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 ((state->anchor & both_horiz) && box.width == 0) { | ||
119 | box.x = bounds.x; | ||
120 | box.width = bounds.width; | ||
121 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
122 | box.x = bounds.x; | ||
123 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
124 | box.x = bounds.x + (bounds.width - box.width); | ||
125 | } else { | ||
126 | box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); | ||
127 | } | ||
128 | // Vertical axis | ||
129 | const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ||
130 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; | ||
131 | if ((state->anchor & both_vert) && box.height == 0) { | ||
132 | box.y = bounds.y; | ||
133 | box.height = bounds.height; | ||
134 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
135 | box.y = bounds.y; | ||
136 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
137 | box.y = bounds.y + (bounds.height - box.height); | ||
138 | } else { | ||
139 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | ||
140 | } | ||
141 | // Margin | ||
142 | if ((state->anchor & both_horiz) == both_horiz) { | ||
143 | box.x += state->margin.left; | ||
144 | box.width -= state->margin.left + state->margin.right; | ||
145 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
146 | box.x += state->margin.left; | ||
147 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
148 | box.x -= state->margin.right; | ||
149 | } | ||
150 | if ((state->anchor & both_vert) == both_vert) { | ||
151 | box.y += state->margin.top; | ||
152 | box.height -= state->margin.top + state->margin.bottom; | ||
153 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
154 | box.y += state->margin.top; | ||
155 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
156 | box.y -= state->margin.bottom; | ||
157 | } | ||
158 | if (box.width < 0 || box.height < 0) { | ||
159 | // TODO: Bubble up a protocol error? | ||
160 | wlr_layer_surface_v1_close(layer); | ||
161 | continue; | 67 | continue; |
162 | } | 68 | } |
163 | // Apply | 69 | |
164 | sway_layer->geo = box; | 70 | wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); |
165 | apply_exclusive(usable_area, state->anchor, state->exclusive_zone, | ||
166 | state->margin.top, state->margin.right, | ||
167 | state->margin.bottom, state->margin.left); | ||
168 | wlr_layer_surface_v1_configure(layer, box.width, box.height); | ||
169 | } | 71 | } |
170 | } | 72 | } |
171 | 73 | ||
@@ -173,81 +75,94 @@ void arrange_layers(struct sway_output *output) { | |||
173 | struct wlr_box usable_area = { 0 }; | 75 | struct wlr_box usable_area = { 0 }; |
174 | wlr_output_effective_resolution(output->wlr_output, | 76 | wlr_output_effective_resolution(output->wlr_output, |
175 | &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); | ||
176 | 84 | ||
177 | // Arrange exclusive surfaces from top->bottom | 85 | if (!wlr_box_equal(&usable_area, &output->usable_area)) { |
178 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
179 | &usable_area, true); | ||
180 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
181 | &usable_area, true); | ||
182 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
183 | &usable_area, true); | ||
184 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
185 | &usable_area, true); | ||
186 | |||
187 | if (memcmp(&usable_area, &output->usable_area, | ||
188 | sizeof(struct wlr_box)) != 0) { | ||
189 | sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); | 86 | sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); |
190 | memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); | 87 | output->usable_area = usable_area; |
191 | arrange_output(output); | 88 | arrange_output(output); |
89 | } else { | ||
90 | arrange_popups(root->layers.popup); | ||
192 | } | 91 | } |
92 | } | ||
193 | 93 | ||
194 | // Arrange non-exlusive surfaces from top->bottom | 94 | static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, |
195 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 95 | enum zwlr_layer_shell_v1_layer type) { |
196 | &usable_area, false); | 96 | switch (type) { |
197 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | 97 | case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: |
198 | &usable_area, false); | 98 | return output->layers.shell_background; |
199 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | 99 | case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: |
200 | &usable_area, false); | 100 | return output->layers.shell_bottom; |
201 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | 101 | case ZWLR_LAYER_SHELL_V1_LAYER_TOP: |
202 | &usable_area, false); | 102 | return output->layers.shell_top; |
203 | 103 | case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: | |
204 | // Find topmost keyboard interactive layer, if such a layer exists | 104 | return output->layers.shell_overlay; |
205 | uint32_t layers_above_shell[] = { | ||
206 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, | ||
207 | ZWLR_LAYER_SHELL_V1_LAYER_TOP, | ||
208 | }; | ||
209 | size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); | ||
210 | struct sway_layer_surface *layer, *topmost = NULL; | ||
211 | for (size_t i = 0; i < nlayers; ++i) { | ||
212 | wl_list_for_each_reverse(layer, | ||
213 | &output->layers[layers_above_shell[i]], link) { | ||
214 | if (layer->layer_surface->current.keyboard_interactive && | ||
215 | layer->layer_surface->mapped) { | ||
216 | topmost = layer; | ||
217 | break; | ||
218 | } | ||
219 | } | ||
220 | if (topmost != NULL) { | ||
221 | break; | ||
222 | } | ||
223 | } | 105 | } |
224 | 106 | ||
225 | struct sway_seat *seat; | 107 | sway_assert(false, "unreachable"); |
226 | wl_list_for_each(seat, &server.input->seats, link) { | 108 | return NULL; |
227 | if (topmost != NULL) { | 109 | } |
228 | seat_set_focus_layer(seat, topmost->layer_surface); | 110 | |
229 | } else if (seat->focused_layer && | 111 | static struct sway_layer_surface *sway_layer_surface_create( |
230 | !seat->focused_layer->current.keyboard_interactive) { | 112 | struct wlr_scene_layer_surface_v1 *scene) { |
231 | seat_set_focus_layer(seat, NULL); | 113 | struct sway_layer_surface *surface = calloc(1, sizeof(*surface)); |
232 | } | 114 | if (!surface) { |
115 | sway_log(SWAY_ERROR, "Could not allocate a scene_layer surface"); | ||
116 | return NULL; | ||
233 | } | 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; | ||
234 | } | 143 | } |
235 | 144 | ||
236 | static struct sway_layer_surface *find_mapped_layer_by_client( | 145 | static struct sway_layer_surface *find_mapped_layer_by_client( |
237 | struct wl_client *client, struct wlr_output *ignore_output) { | 146 | struct wl_client *client, struct sway_output *ignore_output) { |
238 | for (int i = 0; i < root->outputs->length; ++i) { | 147 | for (int i = 0; i < root->outputs->length; ++i) { |
239 | struct sway_output *output = root->outputs->items[i]; | 148 | struct sway_output *output = root->outputs->items[i]; |
240 | if (output->wlr_output == ignore_output) { | 149 | if (output == ignore_output) { |
241 | continue; | 150 | continue; |
242 | } | 151 | } |
243 | // For now we'll only check the overlay layer | 152 | // For now we'll only check the overlay layer |
244 | struct sway_layer_surface *lsurface; | 153 | struct wlr_scene_node *node; |
245 | wl_list_for_each(lsurface, | 154 | wl_list_for_each (node, &output->layers.shell_overlay->children, link) { |
246 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | 155 | struct sway_layer_surface *surface = scene_descriptor_try_get(node, |
247 | 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; | ||
248 | if (wl_resource_get_client(resource) == client | 163 | if (wl_resource_get_client(resource) == client |
249 | && lsurface->layer_surface->mapped) { | 164 | && layer_surface->surface->mapped) { |
250 | return lsurface; | 165 | return surface; |
251 | } | 166 | } |
252 | } | 167 | } |
253 | } | 168 | } |
@@ -255,280 +170,142 @@ static struct sway_layer_surface *find_mapped_layer_by_client( | |||
255 | } | 170 | } |
256 | 171 | ||
257 | static void handle_output_destroy(struct wl_listener *listener, void *data) { | 172 | static void handle_output_destroy(struct wl_listener *listener, void *data) { |
258 | struct sway_layer_surface *sway_layer = | 173 | struct sway_layer_surface *layer = |
259 | wl_container_of(listener, sway_layer, output_destroy); | 174 | wl_container_of(listener, layer, output_destroy); |
260 | // Determine if this layer is being used by an exclusive client. If it is, | ||
261 | // try and find another layer owned by this client to pass focus to. | ||
262 | struct sway_seat *seat = input_manager_get_default_seat(); | ||
263 | struct wl_client *client = | ||
264 | wl_resource_get_client(sway_layer->layer_surface->resource); | ||
265 | bool set_focus = seat->exclusive_client == client; | ||
266 | |||
267 | wl_list_remove(&sway_layer->output_destroy.link); | ||
268 | wl_list_remove(&sway_layer->link); | ||
269 | wl_list_init(&sway_layer->link); | ||
270 | |||
271 | if (set_focus) { | ||
272 | struct sway_layer_surface *layer = | ||
273 | find_mapped_layer_by_client(client, sway_layer->layer_surface->output); | ||
274 | if (layer) { | ||
275 | seat_set_focus_layer(seat, layer->layer_surface); | ||
276 | } | ||
277 | } | ||
278 | 175 | ||
279 | sway_layer->layer_surface->output = NULL; | 176 | layer->output = NULL; |
280 | wlr_layer_surface_v1_close(sway_layer->layer_surface); | 177 | wlr_scene_node_destroy(&layer->scene->tree->node); |
281 | } | 178 | } |
282 | 179 | ||
283 | static void handle_surface_commit(struct wl_listener *listener, void *data) { | 180 | static void handle_node_destroy(struct wl_listener *listener, void *data) { |
284 | struct sway_layer_surface *layer = | 181 | struct sway_layer_surface *layer = |
285 | wl_container_of(listener, layer, surface_commit); | 182 | wl_container_of(listener, layer, node_destroy); |
286 | struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; | ||
287 | struct wlr_output *wlr_output = layer_surface->output; | ||
288 | if (wlr_output == NULL) { | ||
289 | return; | ||
290 | } | ||
291 | 183 | ||
292 | struct sway_output *output = wlr_output->data; | 184 | // destroy the scene descriptor straight away if it exists, otherwise |
293 | struct wlr_box old_geo = layer->geo; | 185 | // we will try to reflow still considering the destroyed node. |
294 | arrange_layers(output); | 186 | scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL); |
295 | |||
296 | bool geo_changed = | ||
297 | memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0; | ||
298 | bool 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 | if (geo_changed || layer_changed) { | ||
306 | output_damage_surface(output, old_geo.x, old_geo.y, | ||
307 | layer_surface->surface, true); | ||
308 | output_damage_surface(output, layer->geo.x, layer->geo.y, | ||
309 | layer_surface->surface, true); | ||
310 | } else { | ||
311 | output_damage_surface(output, layer->geo.x, layer->geo.y, | ||
312 | layer_surface->surface, false); | ||
313 | } | ||
314 | |||
315 | transaction_commit_dirty(); | ||
316 | } | ||
317 | 187 | ||
318 | static void unmap(struct sway_layer_surface *sway_layer) { | 188 | // Determine if this layer is being used by an exclusive client. If it is, |
319 | struct sway_seat *seat; | 189 | // try and find another layer owned by this client to pass focus to. |
320 | wl_list_for_each(seat, &server.input->seats, link) { | 190 | struct sway_seat *seat = input_manager_get_default_seat(); |
321 | if (seat->focused_layer == sway_layer->layer_surface) { | 191 | struct wl_client *client = |
322 | seat_set_focus_layer(seat, NULL); | 192 | wl_resource_get_client(layer->layer_surface->resource); |
193 | if (!server.session_lock.lock) { | ||
194 | struct sway_layer_surface *consider_layer = | ||
195 | find_mapped_layer_by_client(client, layer->output); | ||
196 | if (consider_layer) { | ||
197 | seat_set_focus_layer(seat, consider_layer->layer_surface); | ||
323 | } | 198 | } |
324 | } | 199 | } |
325 | 200 | ||
326 | cursor_rebase_all(); | 201 | if (layer->output) { |
327 | 202 | arrange_layers(layer->output); | |
328 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | 203 | transaction_commit_dirty(); |
329 | if (wlr_output == NULL) { | ||
330 | return; | ||
331 | } | ||
332 | struct sway_output *output = wlr_output->data; | ||
333 | if (output == NULL) { | ||
334 | return; | ||
335 | } | ||
336 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | ||
337 | sway_layer->layer_surface->surface, true); | ||
338 | } | ||
339 | |||
340 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
341 | struct sway_layer_surface *sway_layer = | ||
342 | wl_container_of(listener, sway_layer, destroy); | ||
343 | sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", | ||
344 | sway_layer->layer_surface->namespace); | ||
345 | if (sway_layer->layer_surface->mapped) { | ||
346 | unmap(sway_layer); | ||
347 | } | 204 | } |
348 | wl_list_remove(&sway_layer->link); | ||
349 | wl_list_remove(&sway_layer->destroy.link); | ||
350 | wl_list_remove(&sway_layer->map.link); | ||
351 | wl_list_remove(&sway_layer->unmap.link); | ||
352 | wl_list_remove(&sway_layer->surface_commit.link); | ||
353 | wl_list_remove(&sway_layer->new_popup.link); | ||
354 | wl_list_remove(&sway_layer->new_subsurface.link); | ||
355 | if (sway_layer->layer_surface->output != NULL) { | ||
356 | struct sway_output *output = sway_layer->layer_surface->output->data; | ||
357 | if (output != NULL) { | ||
358 | arrange_layers(output); | ||
359 | transaction_commit_dirty(); | ||
360 | } | ||
361 | wl_list_remove(&sway_layer->output_destroy.link); | ||
362 | sway_layer->layer_surface->output = NULL; | ||
363 | } | ||
364 | free(sway_layer); | ||
365 | } | ||
366 | 205 | ||
367 | static void handle_map(struct wl_listener *listener, void *data) { | 206 | wlr_scene_node_destroy(&layer->popups->node); |
368 | struct sway_layer_surface *sway_layer = wl_container_of(listener, | ||
369 | sway_layer, map); | ||
370 | struct sway_output *output = sway_layer->layer_surface->output->data; | ||
371 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | ||
372 | sway_layer->layer_surface->surface, true); | ||
373 | wlr_surface_send_enter(sway_layer->layer_surface->surface, | ||
374 | sway_layer->layer_surface->output); | ||
375 | cursor_rebase_all(); | ||
376 | } | ||
377 | 207 | ||
378 | static void handle_unmap(struct wl_listener *listener, void *data) { | 208 | wl_list_remove(&layer->map.link); |
379 | struct sway_layer_surface *sway_layer = wl_container_of( | 209 | wl_list_remove(&layer->unmap.link); |
380 | listener, sway_layer, unmap); | 210 | wl_list_remove(&layer->surface_commit.link); |
381 | unmap(sway_layer); | 211 | wl_list_remove(&layer->node_destroy.link); |
382 | } | 212 | wl_list_remove(&layer->output_destroy.link); |
383 | 213 | ||
384 | static void subsurface_damage(struct sway_layer_subsurface *subsurface, | 214 | layer->layer_surface->data = NULL; |
385 | bool whole) { | ||
386 | struct sway_layer_surface *layer = subsurface->layer_surface; | ||
387 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
388 | if (!wlr_output) { | ||
389 | return; | ||
390 | } | ||
391 | struct sway_output *output = wlr_output->data; | ||
392 | int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; | ||
393 | int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; | ||
394 | output_damage_surface( | ||
395 | output, ox, oy, subsurface->wlr_subsurface->surface, whole); | ||
396 | } | ||
397 | |||
398 | static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { | ||
399 | struct sway_layer_subsurface *subsurface = | ||
400 | wl_container_of(listener, subsurface, unmap); | ||
401 | subsurface_damage(subsurface, true); | ||
402 | } | ||
403 | 215 | ||
404 | static void subsurface_handle_map(struct wl_listener *listener, void *data) { | 216 | free(layer); |
405 | struct sway_layer_subsurface *subsurface = | ||
406 | wl_container_of(listener, subsurface, map); | ||
407 | subsurface_damage(subsurface, true); | ||
408 | } | 217 | } |
409 | 218 | ||
410 | static void subsurface_handle_commit(struct wl_listener *listener, void *data) { | 219 | static void handle_surface_commit(struct wl_listener *listener, void *data) { |
411 | struct sway_layer_subsurface *subsurface = | 220 | struct sway_layer_surface *surface = |
412 | wl_container_of(listener, subsurface, commit); | 221 | wl_container_of(listener, surface, surface_commit); |
413 | subsurface_damage(subsurface, false); | ||
414 | } | ||
415 | |||
416 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
417 | void *data) { | ||
418 | struct sway_layer_subsurface *subsurface = | ||
419 | wl_container_of(listener, subsurface, destroy); | ||
420 | |||
421 | wl_list_remove(&subsurface->map.link); | ||
422 | wl_list_remove(&subsurface->unmap.link); | ||
423 | wl_list_remove(&subsurface->destroy.link); | ||
424 | wl_list_remove(&subsurface->commit.link); | ||
425 | free(subsurface); | ||
426 | } | ||
427 | 222 | ||
428 | static struct sway_layer_subsurface *create_subsurface( | 223 | struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; |
429 | struct wlr_subsurface *wlr_subsurface, | 224 | if (!layer_surface->initialized) { |
430 | struct sway_layer_surface *layer_surface) { | 225 | return; |
431 | struct sway_layer_subsurface *subsurface = | ||
432 | calloc(1, sizeof(struct sway_layer_surface)); | ||
433 | if (subsurface == NULL) { | ||
434 | return NULL; | ||
435 | } | 226 | } |
436 | 227 | ||
437 | subsurface->wlr_subsurface = wlr_subsurface; | 228 | uint32_t committed = layer_surface->current.committed; |
438 | subsurface->layer_surface = layer_surface; | 229 | if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { |
439 | 230 | enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; | |
440 | subsurface->map.notify = subsurface_handle_map; | 231 | struct wlr_scene_tree *output_layer = sway_layer_get_scene( |
441 | wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); | 232 | surface->output, layer_type); |
442 | subsurface->unmap.notify = subsurface_handle_unmap; | 233 | wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); |
443 | wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); | 234 | } |
444 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
445 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
446 | subsurface->commit.notify = subsurface_handle_commit; | ||
447 | wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit); | ||
448 | |||
449 | return subsurface; | ||
450 | } | ||
451 | |||
452 | static void handle_new_subsurface(struct wl_listener *listener, void *data) { | ||
453 | struct sway_layer_surface *sway_layer_surface = | ||
454 | wl_container_of(listener, sway_layer_surface, new_subsurface); | ||
455 | struct wlr_subsurface *wlr_subsurface = data; | ||
456 | create_subsurface(wlr_subsurface, sway_layer_surface); | ||
457 | } | ||
458 | |||
459 | 235 | ||
460 | static struct sway_layer_surface *popup_get_layer( | 236 | if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) { |
461 | struct sway_layer_popup *popup) { | 237 | surface->mapped = layer_surface->surface->mapped; |
462 | while (popup->parent_type == LAYER_PARENT_POPUP) { | 238 | arrange_layers(surface->output); |
463 | popup = popup->parent_popup; | 239 | transaction_commit_dirty(); |
464 | } | 240 | } |
465 | return popup->parent_layer; | ||
466 | } | 241 | } |
467 | 242 | ||
468 | static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { | 243 | static void handle_map(struct wl_listener *listener, void *data) { |
469 | struct wlr_xdg_popup *popup = layer_popup->wlr_popup; | 244 | struct sway_layer_surface *surface = wl_container_of(listener, |
470 | struct wlr_surface *surface = popup->base->surface; | 245 | surface, map); |
471 | int popup_sx = popup->geometry.x - popup->base->geometry.x; | 246 | |
472 | int popup_sy = popup->geometry.y - popup->base->geometry.y; | 247 | struct wlr_layer_surface_v1 *layer_surface = |
473 | int ox = popup_sx, oy = popup_sy; | 248 | surface->scene->layer_surface; |
474 | struct sway_layer_surface *layer; | 249 | |
475 | while (true) { | 250 | // focus on new surface |
476 | if (layer_popup->parent_type == LAYER_PARENT_POPUP) { | 251 | if (layer_surface->current.keyboard_interactive && |
477 | layer_popup = layer_popup->parent_popup; | 252 | (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || |
478 | ox += layer_popup->wlr_popup->geometry.x; | 253 | layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { |
479 | oy += layer_popup->wlr_popup->geometry.y; | 254 | struct sway_seat *seat; |
480 | } else { | 255 | wl_list_for_each(seat, &server.input->seats, link) { |
481 | layer = layer_popup->parent_layer; | 256 | // but only if the currently focused layer has a lower precedence |
482 | ox += layer->geo.x; | 257 | if (!seat->focused_layer || |
483 | oy += layer->geo.y; | 258 | seat->focused_layer->current.layer >= layer_surface->current.layer) { |
484 | break; | 259 | seat_set_focus_layer(seat, layer_surface); |
260 | } | ||
485 | } | 261 | } |
262 | arrange_layers(surface->output); | ||
486 | } | 263 | } |
487 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
488 | struct sway_output *output = wlr_output->data; | ||
489 | output_damage_surface(output, ox, oy, surface, whole); | ||
490 | } | ||
491 | 264 | ||
492 | static void popup_handle_map(struct wl_listener *listener, void *data) { | 265 | cursor_rebase_all(); |
493 | struct sway_layer_popup *popup = wl_container_of(listener, popup, map); | ||
494 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
495 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
496 | wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); | ||
497 | popup_damage(popup, true); | ||
498 | } | 266 | } |
499 | 267 | ||
500 | static void popup_handle_unmap(struct wl_listener *listener, void *data) { | 268 | static void handle_unmap(struct wl_listener *listener, void *data) { |
501 | struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); | 269 | struct sway_layer_surface *surface = wl_container_of( |
502 | popup_damage(popup, true); | 270 | listener, surface, unmap); |
503 | } | 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 | } | ||
504 | 277 | ||
505 | static void popup_handle_commit(struct wl_listener *listener, void *data) { | 278 | cursor_rebase_all(); |
506 | struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); | ||
507 | popup_damage(popup, false); | ||
508 | } | 279 | } |
509 | 280 | ||
510 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 281 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
511 | struct sway_layer_popup *popup = | 282 | struct sway_layer_popup *popup = |
512 | wl_container_of(listener, popup, destroy); | 283 | wl_container_of(listener, popup, destroy); |
513 | 284 | ||
514 | wl_list_remove(&popup->map.link); | ||
515 | wl_list_remove(&popup->unmap.link); | ||
516 | wl_list_remove(&popup->destroy.link); | 285 | wl_list_remove(&popup->destroy.link); |
286 | wl_list_remove(&popup->new_popup.link); | ||
517 | wl_list_remove(&popup->commit.link); | 287 | wl_list_remove(&popup->commit.link); |
518 | free(popup); | 288 | free(popup); |
519 | } | 289 | } |
520 | 290 | ||
521 | static void popup_unconstrain(struct sway_layer_popup *popup) { | 291 | static void popup_unconstrain(struct sway_layer_popup *popup) { |
522 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
523 | 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; | ||
524 | 294 | ||
525 | struct sway_output *output = layer->layer_surface->output->data; | 295 | // if a client tries to create a popup while we are in the process of destroying |
296 | // its output, don't crash. | ||
297 | if (!output) { | ||
298 | return; | ||
299 | } | ||
300 | |||
301 | int lx, ly; | ||
302 | wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly); | ||
526 | 303 | ||
527 | // 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 |
528 | // of the popup | 305 | // of the popup |
529 | struct wlr_box output_toplevel_sx_box = { | 306 | struct wlr_box output_toplevel_sx_box = { |
530 | .x = -layer->geo.x, | 307 | .x = output->lx - lx, |
531 | .y = -layer->geo.y, | 308 | .y = output->ly - ly, |
532 | .width = output->width, | 309 | .width = output->width, |
533 | .height = output->height, | 310 | .height = output->height, |
534 | }; | 311 | }; |
@@ -536,32 +313,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { | |||
536 | 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); |
537 | } | 314 | } |
538 | 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 | |||
539 | 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); |
540 | 324 | ||
541 | 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, |
542 | enum layer_parent parent_type, void *parent) { | 326 | struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) { |
543 | struct sway_layer_popup *popup = | 327 | struct sway_layer_popup *popup = calloc(1, sizeof(*popup)); |
544 | calloc(1, sizeof(struct sway_layer_popup)); | ||
545 | if (popup == NULL) { | 328 | if (popup == NULL) { |
546 | return NULL; | 329 | return NULL; |
547 | } | 330 | } |
548 | 331 | ||
332 | popup->toplevel = toplevel; | ||
549 | popup->wlr_popup = wlr_popup; | 333 | popup->wlr_popup = wlr_popup; |
550 | popup->parent_type = parent_type; | 334 | popup->scene = wlr_scene_xdg_surface_create(parent, |
551 | popup->parent_layer = parent; | 335 | wlr_popup->base); |
336 | |||
337 | if (!popup->scene) { | ||
338 | free(popup); | ||
339 | return NULL; | ||
340 | } | ||
552 | 341 | ||
553 | popup->map.notify = popup_handle_map; | ||
554 | wl_signal_add(&wlr_popup->base->events.map, &popup->map); | ||
555 | popup->unmap.notify = popup_handle_unmap; | ||
556 | wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); | ||
557 | popup->destroy.notify = popup_handle_destroy; | 342 | popup->destroy.notify = popup_handle_destroy; |
558 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); | 343 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); |
559 | popup->commit.notify = popup_handle_commit; | ||
560 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); | ||
561 | popup->new_popup.notify = popup_handle_new_popup; | 344 | popup->new_popup.notify = popup_handle_new_popup; |
562 | 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); |
563 | 346 | popup->commit.notify = popup_handle_commit; | |
564 | popup_unconstrain(popup); | 347 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); |
565 | 348 | ||
566 | return popup; | 349 | return popup; |
567 | } | 350 | } |
@@ -570,19 +353,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | |||
570 | struct sway_layer_popup *sway_layer_popup = | 353 | struct sway_layer_popup *sway_layer_popup = |
571 | wl_container_of(listener, sway_layer_popup, new_popup); | 354 | wl_container_of(listener, sway_layer_popup, new_popup); |
572 | struct wlr_xdg_popup *wlr_popup = data; | 355 | struct wlr_xdg_popup *wlr_popup = data; |
573 | create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); | 356 | create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene); |
574 | } | 357 | } |
575 | 358 | ||
576 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 359 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
577 | struct sway_layer_surface *sway_layer_surface = | 360 | struct sway_layer_surface *sway_layer_surface = |
578 | wl_container_of(listener, sway_layer_surface, new_popup); | 361 | wl_container_of(listener, sway_layer_surface, new_popup); |
579 | struct wlr_xdg_popup *wlr_popup = data; | 362 | struct wlr_xdg_popup *wlr_popup = data; |
580 | create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); | 363 | create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups); |
581 | } | ||
582 | |||
583 | struct sway_layer_surface *layer_from_wlr_layer_surface_v1( | ||
584 | struct wlr_layer_surface_v1 *layer_surface) { | ||
585 | return layer_surface->data; | ||
586 | } | 364 | } |
587 | 365 | ||
588 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | 366 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { |
@@ -590,14 +368,14 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
590 | sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 | 368 | sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 |
591 | " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", | 369 | " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", |
592 | layer_surface->namespace, | 370 | layer_surface->namespace, |
593 | layer_surface->client_pending.layer, | 371 | layer_surface->pending.layer, |
594 | layer_surface->client_pending.anchor, | 372 | layer_surface->pending.anchor, |
595 | layer_surface->client_pending.desired_width, | 373 | layer_surface->pending.desired_width, |
596 | layer_surface->client_pending.desired_height, | 374 | layer_surface->pending.desired_height, |
597 | layer_surface->client_pending.margin.top, | 375 | layer_surface->pending.margin.top, |
598 | layer_surface->client_pending.margin.right, | 376 | layer_surface->pending.margin.right, |
599 | layer_surface->client_pending.margin.bottom, | 377 | layer_surface->pending.margin.bottom, |
600 | layer_surface->client_pending.margin.left); | 378 | layer_surface->pending.margin.left); |
601 | 379 | ||
602 | if (!layer_surface->output) { | 380 | if (!layer_surface->output) { |
603 | // Assign last active output | 381 | // Assign last active output |
@@ -609,12 +387,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
609 | output = ws->output; | 387 | output = ws->output; |
610 | } | 388 | } |
611 | } | 389 | } |
612 | if (!output || output == root->noop_output) { | 390 | if (!output || output == root->fallback_output) { |
613 | if (!root->outputs->length) { | 391 | if (!root->outputs->length) { |
614 | sway_log(SWAY_ERROR, | 392 | sway_log(SWAY_ERROR, |
615 | "no output to auto-assign layer surface '%s' to", | 393 | "no output to auto-assign layer surface '%s' to", |
616 | layer_surface->namespace); | 394 | layer_surface->namespace); |
617 | wlr_layer_surface_v1_close(layer_surface); | 395 | wlr_layer_surface_v1_destroy(layer_surface); |
618 | return; | 396 | return; |
619 | } | 397 | } |
620 | output = root->outputs->items[0]; | 398 | output = root->outputs->items[0]; |
@@ -622,42 +400,51 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
622 | layer_surface->output = output->wlr_output; | 400 | layer_surface->output = output->wlr_output; |
623 | } | 401 | } |
624 | 402 | ||
625 | struct sway_layer_surface *sway_layer = | 403 | struct sway_output *output = layer_surface->output->data; |
626 | calloc(1, sizeof(struct sway_layer_surface)); | 404 | |
627 | 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"); | ||
628 | return; | 412 | return; |
629 | } | 413 | } |
630 | 414 | ||
631 | sway_layer->surface_commit.notify = handle_surface_commit; | 415 | struct sway_layer_surface *surface = |
632 | wl_signal_add(&layer_surface->surface->events.commit, | 416 | sway_layer_surface_create(scene_surface); |
633 | &sway_layer->surface_commit); | 417 | if (!surface) { |
634 | 418 | wlr_layer_surface_v1_destroy(layer_surface); | |
635 | sway_layer->destroy.notify = handle_destroy; | ||
636 | wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); | ||
637 | sway_layer->map.notify = handle_map; | ||
638 | wl_signal_add(&layer_surface->events.map, &sway_layer->map); | ||
639 | sway_layer->unmap.notify = handle_unmap; | ||
640 | wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap); | ||
641 | sway_layer->new_popup.notify = handle_new_popup; | ||
642 | wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup); | ||
643 | sway_layer->new_subsurface.notify = handle_new_subsurface; | ||
644 | wl_signal_add(&layer_surface->surface->events.new_subsurface, | ||
645 | &sway_layer->new_subsurface); | ||
646 | |||
647 | sway_layer->layer_surface = layer_surface; | ||
648 | layer_surface->data = sway_layer; | ||
649 | 419 | ||
650 | struct sway_output *output = layer_surface->output->data; | 420 | sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface"); |
651 | sway_layer->output_destroy.notify = handle_output_destroy; | 421 | return; |
652 | wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); | 422 | } |
653 | 423 | ||
654 | wl_list_insert(&output->layers[layer_surface->client_pending.layer], | 424 | if (!scene_descriptor_assign(&scene_surface->tree->node, |
655 | &sway_layer->link); | 425 | SWAY_SCENE_DESC_LAYER_SHELL, surface)) { |
656 | 426 | sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor"); | |
657 | // Temporarily set the layer's current state to client_pending | 427 | // destroying the layer_surface will also destroy its corresponding |
658 | // So that we can easily arrange it | 428 | // scene node |
659 | struct wlr_layer_surface_v1_state old_state = layer_surface->current; | 429 | wlr_layer_surface_v1_destroy(layer_surface); |
660 | layer_surface->current = layer_surface->client_pending; | 430 | return; |
661 | arrange_layers(output); | 431 | } |
662 | layer_surface->current = old_state; | 432 | |
433 | surface->output = output; | ||
434 | |||
435 | surface->surface_commit.notify = handle_surface_commit; | ||
436 | wl_signal_add(&layer_surface->surface->events.commit, | ||
437 | &surface->surface_commit); | ||
438 | surface->map.notify = handle_map; | ||
439 | wl_signal_add(&layer_surface->surface->events.map, &surface->map); | ||
440 | surface->unmap.notify = handle_unmap; | ||
441 | wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap); | ||
442 | surface->new_popup.notify = handle_new_popup; | ||
443 | wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); | ||
444 | |||
445 | surface->output_destroy.notify = handle_output_destroy; | ||
446 | wl_signal_add(&output->events.disable, &surface->output_destroy); | ||
447 | |||
448 | surface->node_destroy.notify = handle_node_destroy; | ||
449 | wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy); | ||
663 | } | 450 | } |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 5edc8f96..70987feb 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -1,42 +1,61 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
5 | #include <time.h> | 4 | #include <time.h> |
6 | #include <wayland-server-core.h> | 5 | #include <wayland-server-core.h> |
6 | #include <wlr/config.h> | ||
7 | #include <wlr/backend/headless.h> | ||
8 | #include <wlr/render/swapchain.h> | ||
7 | #include <wlr/render/wlr_renderer.h> | 9 | #include <wlr/render/wlr_renderer.h> |
8 | #include <wlr/types/wlr_box.h> | ||
9 | #include <wlr/types/wlr_buffer.h> | 10 | #include <wlr/types/wlr_buffer.h> |
11 | #include <wlr/types/wlr_gamma_control_v1.h> | ||
10 | #include <wlr/types/wlr_matrix.h> | 12 | #include <wlr/types/wlr_matrix.h> |
11 | #include <wlr/types/wlr_output_damage.h> | ||
12 | #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> | ||
13 | #include <wlr/types/wlr_output.h> | 16 | #include <wlr/types/wlr_output.h> |
14 | #include <wlr/types/wlr_presentation_time.h> | 17 | #include <wlr/types/wlr_presentation_time.h> |
15 | #include <wlr/types/wlr_surface.h> | 18 | #include <wlr/types/wlr_compositor.h> |
16 | #include <wlr/util/region.h> | 19 | #include <wlr/util/region.h> |
20 | #include <wlr/util/transform.h> | ||
17 | #include "config.h" | 21 | #include "config.h" |
18 | #include "log.h" | 22 | #include "log.h" |
19 | #include "sway/config.h" | 23 | #include "sway/config.h" |
20 | #include "sway/desktop/transaction.h" | 24 | #include "sway/desktop/transaction.h" |
21 | #include "sway/input/input-manager.h" | 25 | #include "sway/input/input-manager.h" |
22 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/ipc-server.h" | ||
23 | #include "sway/layers.h" | 28 | #include "sway/layers.h" |
24 | #include "sway/output.h" | 29 | #include "sway/output.h" |
30 | #include "sway/scene_descriptor.h" | ||
25 | #include "sway/server.h" | 31 | #include "sway/server.h" |
26 | #include "sway/surface.h" | ||
27 | #include "sway/tree/arrange.h" | 32 | #include "sway/tree/arrange.h" |
28 | #include "sway/tree/container.h" | 33 | #include "sway/tree/container.h" |
29 | #include "sway/tree/root.h" | 34 | #include "sway/tree/root.h" |
30 | #include "sway/tree/view.h" | 35 | #include "sway/tree/view.h" |
31 | #include "sway/tree/workspace.h" | 36 | #include "sway/tree/workspace.h" |
32 | 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 | |||
33 | 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) { |
34 | for (int i = 0; i < root->outputs->length; ++i) { | 56 | for (int i = 0; i < root->outputs->length; ++i) { |
35 | struct sway_output *output = root->outputs->items[i]; | 57 | struct sway_output *output = root->outputs->items[i]; |
36 | char identifier[128]; | 58 | if (output_match_name_or_id(output, name_or_id)) { |
37 | output_get_identifier(identifier, sizeof(identifier), output); | ||
38 | if (strcasecmp(identifier, name_or_id) == 0 | ||
39 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
40 | return output; | 59 | return output; |
41 | } | 60 | } |
42 | } | 61 | } |
@@ -46,583 +65,217 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) { | |||
46 | 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) { |
47 | struct sway_output *output; | 66 | struct sway_output *output; |
48 | wl_list_for_each(output, &root->all_outputs, link) { | 67 | wl_list_for_each(output, &root->all_outputs, link) { |
49 | char identifier[128]; | 68 | if (output_match_name_or_id(output, name_or_id)) { |
50 | output_get_identifier(identifier, sizeof(identifier), output); | ||
51 | if (strcasecmp(identifier, name_or_id) == 0 | ||
52 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
53 | return output; | 69 | return output; |
54 | } | 70 | } |
55 | } | 71 | } |
56 | return NULL; | 72 | return NULL; |
57 | } | 73 | } |
58 | 74 | ||
59 | /** | ||
60 | * Rotate a child's position relative to a parent. The parent size is (pw, ph), | ||
61 | * the child position is (*sx, *sy) and its size is (sw, sh). | ||
62 | */ | ||
63 | static void rotate_child_position(double *sx, double *sy, double sw, double sh, | ||
64 | double pw, double ph, float rotation) { | ||
65 | if (rotation == 0.0f) { | ||
66 | return; | ||
67 | } | ||
68 | 75 | ||
69 | // Coordinates relative to the center of the subsurface | 76 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { |
70 | double ox = *sx - pw/2 + sw/2, | 77 | struct sway_seat *seat = input_manager_current_seat(); |
71 | oy = *sy - ph/2 + sh/2; | 78 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); |
72 | // Rotated coordinates | 79 | if (!focus) { |
73 | double rx = cos(-rotation)*ox - sin(-rotation)*oy, | 80 | if (!output->workspaces->length) { |
74 | ry = cos(-rotation)*oy + sin(-rotation)*ox; | 81 | return NULL; |
75 | *sx = rx + pw/2 - sw/2; | 82 | } |
76 | *sy = ry + ph/2 - sh/2; | 83 | return output->workspaces->items[0]; |
84 | } | ||
85 | return focus->sway_workspace; | ||
77 | } | 86 | } |
78 | 87 | ||
79 | struct surface_iterator_data { | 88 | struct send_frame_done_data { |
80 | sway_surface_iterator_func_t user_iterator; | 89 | struct timespec when; |
81 | void *user_data; | 90 | int msec_until_refresh; |
82 | |||
83 | struct sway_output *output; | 91 | struct sway_output *output; |
84 | struct sway_view *view; | ||
85 | double ox, oy; | ||
86 | int width, height; | ||
87 | float rotation; | ||
88 | }; | 92 | }; |
89 | 93 | ||
90 | static bool get_surface_box(struct surface_iterator_data *data, | 94 | struct buffer_timer { |
91 | struct wlr_surface *surface, int sx, int sy, | 95 | struct wl_listener destroy; |
92 | struct wlr_box *surface_box) { | 96 | struct wl_event_source *frame_done_timer; |
93 | struct sway_output *output = data->output; | 97 | }; |
94 | |||
95 | if (!wlr_surface_has_buffer(surface)) { | ||
96 | return false; | ||
97 | } | ||
98 | |||
99 | int sw = surface->current.width; | ||
100 | int sh = surface->current.height; | ||
101 | |||
102 | double _sx = sx + surface->sx; | ||
103 | double _sy = sy + surface->sy; | ||
104 | rotate_child_position(&_sx, &_sy, sw, sh, data->width, data->height, | ||
105 | data->rotation); | ||
106 | |||
107 | struct wlr_box box = { | ||
108 | .x = data->ox + _sx, | ||
109 | .y = data->oy + _sy, | ||
110 | .width = sw, | ||
111 | .height = sh, | ||
112 | }; | ||
113 | if (surface_box != NULL) { | ||
114 | memcpy(surface_box, &box, sizeof(struct wlr_box)); | ||
115 | } | ||
116 | |||
117 | struct wlr_box rotated_box; | ||
118 | wlr_box_rotated_bounds(&rotated_box, &box, data->rotation); | ||
119 | |||
120 | struct wlr_box output_box = { | ||
121 | .width = output->width, | ||
122 | .height = output->height, | ||
123 | }; | ||
124 | |||
125 | struct wlr_box intersection; | ||
126 | return wlr_box_intersection(&intersection, &output_box, &rotated_box); | ||
127 | } | ||
128 | |||
129 | static void output_for_each_surface_iterator(struct wlr_surface *surface, | ||
130 | int sx, int sy, void *_data) { | ||
131 | struct surface_iterator_data *data = _data; | ||
132 | |||
133 | struct wlr_box box; | ||
134 | bool intersects = get_surface_box(data, surface, sx, sy, &box); | ||
135 | if (!intersects) { | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | data->user_iterator(data->output, data->view, surface, &box, data->rotation, | ||
140 | data->user_data); | ||
141 | } | ||
142 | |||
143 | void output_surface_for_each_surface(struct sway_output *output, | ||
144 | struct wlr_surface *surface, double ox, double oy, | ||
145 | sway_surface_iterator_func_t iterator, void *user_data) { | ||
146 | struct surface_iterator_data data = { | ||
147 | .user_iterator = iterator, | ||
148 | .user_data = user_data, | ||
149 | .output = output, | ||
150 | .view = NULL, | ||
151 | .ox = ox, | ||
152 | .oy = oy, | ||
153 | .width = surface->current.width, | ||
154 | .height = surface->current.height, | ||
155 | .rotation = 0, | ||
156 | }; | ||
157 | |||
158 | wlr_surface_for_each_surface(surface, | ||
159 | output_for_each_surface_iterator, &data); | ||
160 | } | ||
161 | 98 | ||
162 | void output_view_for_each_surface(struct sway_output *output, | 99 | static int handle_buffer_timer(void *data) { |
163 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 100 | struct wlr_scene_buffer *buffer = data; |
164 | void *user_data) { | ||
165 | struct surface_iterator_data data = { | ||
166 | .user_iterator = iterator, | ||
167 | .user_data = user_data, | ||
168 | .output = output, | ||
169 | .view = view, | ||
170 | .ox = view->container->surface_x - output->lx | ||
171 | - view->geometry.x, | ||
172 | .oy = view->container->surface_y - output->ly | ||
173 | - view->geometry.y, | ||
174 | .width = view->container->current.content_width, | ||
175 | .height = view->container->current.content_height, | ||
176 | .rotation = 0, // TODO | ||
177 | }; | ||
178 | |||
179 | view_for_each_surface(view, output_for_each_surface_iterator, &data); | ||
180 | } | ||
181 | 101 | ||
182 | void output_view_for_each_popup_surface(struct sway_output *output, | 102 | struct timespec now; |
183 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 103 | clock_gettime(CLOCK_MONOTONIC, &now); |
184 | void *user_data) { | 104 | wlr_scene_buffer_send_frame_done(buffer, &now); |
185 | struct surface_iterator_data data = { | 105 | return 0; |
186 | .user_iterator = iterator, | ||
187 | .user_data = user_data, | ||
188 | .output = output, | ||
189 | .view = view, | ||
190 | .ox = view->container->surface_x - output->lx | ||
191 | - view->geometry.x, | ||
192 | .oy = view->container->surface_y - output->ly | ||
193 | - view->geometry.y, | ||
194 | .width = view->container->current.content_width, | ||
195 | .height = view->container->current.content_height, | ||
196 | .rotation = 0, // TODO | ||
197 | }; | ||
198 | |||
199 | view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); | ||
200 | } | 106 | } |
201 | 107 | ||
202 | void output_layer_for_each_surface(struct sway_output *output, | 108 | static void handle_buffer_timer_destroy(struct wl_listener *listener, |
203 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 109 | void *data) { |
204 | void *user_data) { | 110 | struct buffer_timer *timer = wl_container_of(listener, timer, destroy); |
205 | struct sway_layer_surface *layer_surface; | ||
206 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
207 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
208 | layer_surface->layer_surface; | ||
209 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | ||
210 | layer_surface->geo.x, layer_surface->geo.y, iterator, | ||
211 | user_data); | ||
212 | |||
213 | struct wlr_xdg_popup *state; | ||
214 | wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { | ||
215 | struct wlr_xdg_surface *popup = state->base; | ||
216 | if (!popup->configured) { | ||
217 | continue; | ||
218 | } | ||
219 | 111 | ||
220 | double popup_sx, popup_sy; | 112 | wl_list_remove(&timer->destroy.link); |
221 | popup_sx = layer_surface->geo.x + | 113 | wl_event_source_remove(timer->frame_done_timer); |
222 | popup->popup->geometry.x - popup->geometry.x; | 114 | free(timer); |
223 | popup_sy = layer_surface->geo.y + | ||
224 | popup->popup->geometry.y - popup->geometry.y; | ||
225 | |||
226 | struct wlr_surface *surface = popup->surface; | ||
227 | |||
228 | struct surface_iterator_data data = { | ||
229 | .user_iterator = iterator, | ||
230 | .user_data = user_data, | ||
231 | .output = output, | ||
232 | .view = NULL, | ||
233 | .ox = popup_sx, | ||
234 | .oy = popup_sy, | ||
235 | .width = surface->current.width, | ||
236 | .height = surface->current.height, | ||
237 | .rotation = 0, | ||
238 | }; | ||
239 | |||
240 | wlr_xdg_surface_for_each_surface( | ||
241 | popup, output_for_each_surface_iterator, &data); | ||
242 | } | ||
243 | } | ||
244 | } | 115 | } |
245 | 116 | ||
246 | 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) { |
247 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 118 | struct buffer_timer *timer = |
248 | void *user_data) { | 119 | scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); |
249 | struct sway_layer_surface *layer_surface; | 120 | if (timer) { |
250 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 121 | return timer; |
251 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
252 | layer_surface->layer_surface; | ||
253 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | ||
254 | layer_surface->geo.x, layer_surface->geo.y, iterator, | ||
255 | user_data); | ||
256 | } | 122 | } |
257 | } | ||
258 | |||
259 | 123 | ||
260 | void output_layer_for_each_popup_surface(struct sway_output *output, | 124 | timer = calloc(1, sizeof(struct buffer_timer)); |
261 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 125 | if (!timer) { |
262 | void *user_data) { | 126 | return NULL; |
263 | struct sway_layer_surface *layer_surface; | ||
264 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
265 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
266 | layer_surface->layer_surface; | ||
267 | |||
268 | struct wlr_xdg_popup *state; | ||
269 | wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { | ||
270 | struct wlr_xdg_surface *popup = state->base; | ||
271 | if (!popup->configured) { | ||
272 | continue; | ||
273 | } | ||
274 | |||
275 | double popup_sx, popup_sy; | ||
276 | popup_sx = layer_surface->geo.x + | ||
277 | popup->popup->geometry.x - popup->geometry.x; | ||
278 | popup_sy = layer_surface->geo.y + | ||
279 | popup->popup->geometry.y - popup->geometry.y; | ||
280 | |||
281 | struct wlr_surface *surface = popup->surface; | ||
282 | |||
283 | struct surface_iterator_data data = { | ||
284 | .user_iterator = iterator, | ||
285 | .user_data = user_data, | ||
286 | .output = output, | ||
287 | .view = NULL, | ||
288 | .ox = popup_sx, | ||
289 | .oy = popup_sy, | ||
290 | .width = surface->current.width, | ||
291 | .height = surface->current.height, | ||
292 | .rotation = 0, | ||
293 | }; | ||
294 | |||
295 | wlr_xdg_surface_for_each_surface( | ||
296 | popup, output_for_each_surface_iterator, &data); | ||
297 | } | ||
298 | } | 127 | } |
299 | } | ||
300 | 128 | ||
301 | #if HAVE_XWAYLAND | 129 | timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, |
302 | void output_unmanaged_for_each_surface(struct sway_output *output, | 130 | handle_buffer_timer, buffer); |
303 | struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, | 131 | if (!timer->frame_done_timer) { |
304 | void *user_data) { | 132 | free(timer); |
305 | struct sway_xwayland_unmanaged *unmanaged_surface; | 133 | return NULL; |
306 | wl_list_for_each(unmanaged_surface, unmanaged, link) { | ||
307 | struct wlr_xwayland_surface *xsurface = | ||
308 | unmanaged_surface->wlr_xwayland_surface; | ||
309 | double ox = unmanaged_surface->lx - output->lx; | ||
310 | double oy = unmanaged_surface->ly - output->ly; | ||
311 | |||
312 | output_surface_for_each_surface(output, xsurface->surface, ox, oy, | ||
313 | iterator, user_data); | ||
314 | } | 134 | } |
315 | } | ||
316 | #endif | ||
317 | 135 | ||
318 | void output_drag_icons_for_each_surface(struct sway_output *output, | 136 | scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer); |
319 | struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, | ||
320 | void *user_data) { | ||
321 | struct sway_drag_icon *drag_icon; | ||
322 | wl_list_for_each(drag_icon, drag_icons, link) { | ||
323 | double ox = drag_icon->x - output->lx; | ||
324 | double oy = drag_icon->y - output->ly; | ||
325 | |||
326 | if (drag_icon->wlr_drag_icon->mapped) { | ||
327 | output_surface_for_each_surface(output, | ||
328 | drag_icon->wlr_drag_icon->surface, ox, oy, | ||
329 | iterator, user_data); | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | 137 | ||
334 | static void for_each_surface_container_iterator(struct sway_container *con, | 138 | timer->destroy.notify = handle_buffer_timer_destroy; |
335 | void *_data) { | 139 | wl_signal_add(&buffer->node.events.destroy, &timer->destroy); |
336 | if (!con->view || !view_is_visible(con->view)) { | ||
337 | return; | ||
338 | } | ||
339 | 140 | ||
340 | struct surface_iterator_data *data = _data; | 141 | return timer; |
341 | output_view_for_each_surface(data->output, con->view, | ||
342 | data->user_iterator, data->user_data); | ||
343 | } | 142 | } |
344 | 143 | ||
345 | static void output_for_each_surface(struct sway_output *output, | 144 | static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, |
346 | sway_surface_iterator_func_t iterator, void *user_data) { | 145 | int x, int y, void *user_data) { |
347 | if (output_has_opaque_overlay_layer_surface(output)) { | 146 | struct send_frame_done_data *data = user_data; |
348 | goto overlay; | 147 | struct sway_output *output = data->output; |
349 | } | 148 | int view_max_render_time = 0; |
350 | 149 | ||
351 | struct surface_iterator_data data = { | 150 | if (buffer->primary_output != data->output->scene_output) { |
352 | .user_iterator = iterator, | 151 | return; |
353 | .user_data = user_data, | ||
354 | .output = output, | ||
355 | .view = NULL, | ||
356 | }; | ||
357 | |||
358 | struct sway_workspace *workspace = output_get_active_workspace(output); | ||
359 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
360 | if (!fullscreen_con) { | ||
361 | if (!workspace) { | ||
362 | return; | ||
363 | } | ||
364 | fullscreen_con = workspace->current.fullscreen; | ||
365 | } | ||
366 | if (fullscreen_con) { | ||
367 | for_each_surface_container_iterator(fullscreen_con, &data); | ||
368 | container_for_each_child(fullscreen_con, | ||
369 | for_each_surface_container_iterator, &data); | ||
370 | |||
371 | // TODO: Show transient containers for fullscreen global | ||
372 | if (fullscreen_con == workspace->current.fullscreen) { | ||
373 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
374 | struct sway_container *floater = | ||
375 | workspace->current.floating->items[i]; | ||
376 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
377 | for_each_surface_container_iterator(floater, &data); | ||
378 | } | ||
379 | } | ||
380 | } | ||
381 | #if HAVE_XWAYLAND | ||
382 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
383 | iterator, user_data); | ||
384 | #endif | ||
385 | } else { | ||
386 | output_layer_for_each_surface(output, | ||
387 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
388 | iterator, user_data); | ||
389 | output_layer_for_each_surface(output, | ||
390 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
391 | iterator, user_data); | ||
392 | |||
393 | workspace_for_each_container(workspace, | ||
394 | for_each_surface_container_iterator, &data); | ||
395 | |||
396 | #if HAVE_XWAYLAND | ||
397 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
398 | iterator, user_data); | ||
399 | #endif | ||
400 | output_layer_for_each_surface(output, | ||
401 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
402 | iterator, user_data); | ||
403 | } | 152 | } |
404 | 153 | ||
405 | overlay: | 154 | struct wlr_scene_node *current = &buffer->node; |
406 | output_layer_for_each_surface(output, | 155 | while (true) { |
407 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 156 | struct sway_view *view = scene_descriptor_try_get(current, |
408 | iterator, user_data); | 157 | SWAY_SCENE_DESC_VIEW); |
409 | output_drag_icons_for_each_surface(output, &root->drag_icons, | 158 | if (view) { |
410 | iterator, user_data); | 159 | view_max_render_time = view->max_render_time; |
411 | } | 160 | break; |
412 | |||
413 | static int scale_length(int length, int offset, float scale) { | ||
414 | return round((offset + length) * scale) - round(offset * scale); | ||
415 | } | ||
416 | |||
417 | void scale_box(struct wlr_box *box, float scale) { | ||
418 | box->width = scale_length(box->width, box->x, scale); | ||
419 | box->height = scale_length(box->height, box->y, scale); | ||
420 | box->x = round(box->x * scale); | ||
421 | box->y = round(box->y * scale); | ||
422 | } | ||
423 | |||
424 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { | ||
425 | struct sway_seat *seat = input_manager_current_seat(); | ||
426 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); | ||
427 | if (!focus) { | ||
428 | if (!output->workspaces->length) { | ||
429 | return NULL; | ||
430 | } | 161 | } |
431 | return output->workspaces->items[0]; | ||
432 | } | ||
433 | return focus->sway_workspace; | ||
434 | } | ||
435 | 162 | ||
436 | bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { | 163 | if (!current->parent) { |
437 | struct sway_layer_surface *sway_layer_surface; | 164 | break; |
438 | wl_list_for_each(sway_layer_surface, | ||
439 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | ||
440 | struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface; | ||
441 | pixman_box32_t output_box = { | ||
442 | .x2 = output->width, | ||
443 | .y2 = output->height, | ||
444 | }; | ||
445 | pixman_region32_t surface_opaque_box; | ||
446 | pixman_region32_init(&surface_opaque_box); | ||
447 | pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); | ||
448 | pixman_region32_translate(&surface_opaque_box, | ||
449 | sway_layer_surface->geo.x, sway_layer_surface->geo.y); | ||
450 | pixman_region_overlap_t contains = | ||
451 | pixman_region32_contains_rectangle(&surface_opaque_box, &output_box); | ||
452 | pixman_region32_fini(&surface_opaque_box); | ||
453 | |||
454 | if (contains == PIXMAN_REGION_IN) { | ||
455 | return true; | ||
456 | } | 165 | } |
457 | } | ||
458 | return false; | ||
459 | } | ||
460 | |||
461 | struct send_frame_done_data { | ||
462 | struct timespec when; | ||
463 | int msec_until_refresh; | ||
464 | }; | ||
465 | 166 | ||
466 | static void send_frame_done_iterator(struct sway_output *output, struct sway_view *view, | 167 | current = ¤t->parent->node; |
467 | struct wlr_surface *surface, struct wlr_box *box, float rotation, | ||
468 | void *user_data) { | ||
469 | int view_max_render_time = 0; | ||
470 | if (view != NULL) { | ||
471 | view_max_render_time = view->max_render_time; | ||
472 | } | 168 | } |
473 | 169 | ||
474 | struct send_frame_done_data *data = user_data; | ||
475 | |||
476 | int delay = data->msec_until_refresh - output->max_render_time | 170 | int delay = data->msec_until_refresh - output->max_render_time |
477 | - view_max_render_time; | 171 | - view_max_render_time; |
478 | 172 | ||
479 | if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { | 173 | struct buffer_timer *timer = NULL; |
480 | wlr_surface_send_frame_done(surface, &data->when); | ||
481 | } else { | ||
482 | struct sway_surface *sway_surface = surface->data; | ||
483 | wl_event_source_timer_update(sway_surface->frame_done_timer, delay); | ||
484 | } | ||
485 | } | ||
486 | |||
487 | static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { | ||
488 | output_for_each_surface(output, send_frame_done_iterator, data); | ||
489 | } | ||
490 | |||
491 | static void count_surface_iterator(struct sway_output *output, struct sway_view *view, | ||
492 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
493 | void *data) { | ||
494 | size_t *n = data; | ||
495 | (*n)++; | ||
496 | } | ||
497 | 174 | ||
498 | static bool scan_out_fullscreen_view(struct sway_output *output, | 175 | if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { |
499 | struct sway_view *view) { | 176 | timer = buffer_timer_get_or_create(buffer); |
500 | struct wlr_output *wlr_output = output->wlr_output; | ||
501 | struct sway_workspace *workspace = output->current.active_workspace; | ||
502 | if (!sway_assert(workspace, "Expected an active workspace")) { | ||
503 | return false; | ||
504 | } | 177 | } |
505 | 178 | ||
506 | if (!wl_list_empty(&view->saved_buffers)) { | 179 | if (timer) { |
507 | 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); | ||
508 | } | 183 | } |
184 | } | ||
509 | 185 | ||
510 | for (int i = 0; i < workspace->current.floating->length; ++i) { | 186 | static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, |
511 | struct sway_container *floater = | 187 | struct wlr_scene_buffer *buffer) { |
512 | workspace->current.floating->items[i]; | 188 | // if we are scaling down, we should always choose linear |
513 | if (container_is_transient_for(floater, view->container)) { | 189 | if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( |
514 | return false; | 190 | buffer->dst_width < buffer->buffer_width || |
515 | } | 191 | buffer->dst_height < buffer->buffer_height)) { |
192 | return WLR_SCALE_FILTER_BILINEAR; | ||
516 | } | 193 | } |
517 | 194 | ||
518 | #if HAVE_XWAYLAND | 195 | switch (output->scale_filter) { |
519 | if (!wl_list_empty(&root->xwayland_unmanaged)) { | 196 | case SCALE_FILTER_LINEAR: |
520 | return false; | 197 | return WLR_SCALE_FILTER_BILINEAR; |
198 | case SCALE_FILTER_NEAREST: | ||
199 | return WLR_SCALE_FILTER_NEAREST; | ||
200 | default: | ||
201 | abort(); // unreachable | ||
521 | } | 202 | } |
522 | #endif | 203 | } |
523 | 204 | ||
524 | if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { | 205 | static void output_configure_scene(struct sway_output *output, |
525 | return false; | 206 | struct wlr_scene_node *node, float opacity) { |
526 | } | 207 | if (!node->enabled) { |
527 | if (!wl_list_empty(&root->drag_icons)) { | 208 | return; |
528 | return false; | ||
529 | } | 209 | } |
530 | 210 | ||
531 | struct wlr_surface *surface = view->surface; | 211 | struct sway_container *con = |
532 | if (surface == NULL) { | 212 | scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER); |
533 | return false; | 213 | if (con) { |
534 | } | 214 | opacity = con->alpha; |
535 | size_t n_surfaces = 0; | ||
536 | output_view_for_each_surface(output, view, | ||
537 | count_surface_iterator, &n_surfaces); | ||
538 | if (n_surfaces != 1) { | ||
539 | return false; | ||
540 | } | 215 | } |
541 | 216 | ||
542 | if (surface->buffer == NULL) { | 217 | if (node->type == WLR_SCENE_NODE_BUFFER) { |
543 | return false; | 218 | struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); |
544 | } | ||
545 | 219 | ||
546 | if ((float)surface->current.scale != wlr_output->scale || | 220 | // hack: don't call the scene setter because that will damage all outputs |
547 | surface->current.transform != wlr_output->transform) { | 221 | // We don't want to damage outputs that aren't our current output that |
548 | return false; | 222 | // we're configuring |
549 | } | 223 | buffer->filter_mode = get_scale_filter(output, buffer); |
550 | 224 | ||
551 | wlr_output_attach_buffer(wlr_output, &surface->buffer->base); | 225 | wlr_scene_buffer_set_opacity(buffer, opacity); |
552 | if (!wlr_output_test(wlr_output)) { | 226 | } else if (node->type == WLR_SCENE_NODE_TREE) { |
553 | 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 | } | ||
554 | } | 232 | } |
555 | |||
556 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
557 | wlr_output); | ||
558 | |||
559 | return wlr_output_commit(wlr_output); | ||
560 | } | 233 | } |
561 | 234 | ||
562 | static int output_repaint_timer_handler(void *data) { | 235 | static int output_repaint_timer_handler(void *data) { |
563 | struct sway_output *output = data; | 236 | struct sway_output *output = data; |
564 | if (output->wlr_output == NULL) { | ||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | output->wlr_output->frame_pending = false; | ||
569 | 237 | ||
570 | struct sway_workspace *workspace = output->current.active_workspace; | 238 | if (!output->enabled) { |
571 | if (workspace == NULL) { | ||
572 | return 0; | 239 | return 0; |
573 | } | 240 | } |
574 | 241 | ||
575 | struct sway_container *fullscreen_con = root->fullscreen_global; | 242 | output->wlr_output->frame_pending = false; |
576 | if (!fullscreen_con) { | ||
577 | fullscreen_con = workspace->current.fullscreen; | ||
578 | } | ||
579 | 243 | ||
580 | if (fullscreen_con && fullscreen_con->view) { | 244 | output_configure_scene(output, &root->root_scene->tree.node, 1.0f); |
581 | // Try to scan-out the fullscreen view | ||
582 | static bool last_scanned_out = false; | ||
583 | bool scanned_out = | ||
584 | scan_out_fullscreen_view(output, fullscreen_con->view); | ||
585 | 245 | ||
586 | if (scanned_out && !last_scanned_out) { | 246 | if (output->gamma_lut_changed) { |
587 | sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", | 247 | struct wlr_output_state pending; |
588 | 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; | ||
589 | } | 251 | } |
590 | if (last_scanned_out && !scanned_out) { | 252 | |
591 | sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", | 253 | output->gamma_lut_changed = false; |
592 | output->wlr_output->name); | 254 | struct wlr_gamma_control_v1 *gamma_control = |
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; | ||
593 | } | 260 | } |
594 | last_scanned_out = scanned_out; | ||
595 | 261 | ||
596 | 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); | ||
597 | return 0; | 265 | return 0; |
598 | } | 266 | } |
599 | } | ||
600 | 267 | ||
601 | bool needs_frame; | 268 | wlr_output_state_finish(&pending); |
602 | pixman_region32_t damage; | ||
603 | pixman_region32_init(&damage); | ||
604 | if (!wlr_output_damage_attach_render(output->damage, | ||
605 | &needs_frame, &damage)) { | ||
606 | return 0; | 269 | return 0; |
607 | } | 270 | } |
608 | 271 | ||
609 | if (needs_frame) { | 272 | wlr_scene_output_commit(output->scene_output, NULL); |
610 | struct timespec now; | ||
611 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
612 | |||
613 | output_render(output, &now, &damage); | ||
614 | } else { | ||
615 | wlr_output_rollback(output->wlr_output); | ||
616 | } | ||
617 | |||
618 | pixman_region32_fini(&damage); | ||
619 | |||
620 | return 0; | 273 | return 0; |
621 | } | 274 | } |
622 | 275 | ||
623 | static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | 276 | static void handle_frame(struct wl_listener *listener, void *user_data) { |
624 | struct sway_output *output = | 277 | struct sway_output *output = |
625 | wl_container_of(listener, output, damage_frame); | 278 | wl_container_of(listener, output, frame); |
626 | if (!output->enabled || !output->wlr_output->enabled) { | 279 | if (!output->enabled || !output->wlr_output->enabled) { |
627 | return; | 280 | return; |
628 | } | 281 | } |
@@ -633,9 +286,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
633 | 286 | ||
634 | if (output->max_render_time != 0) { | 287 | if (output->max_render_time != 0) { |
635 | struct timespec now; | 288 | struct timespec now; |
636 | clockid_t presentation_clock | 289 | clock_gettime(CLOCK_MONOTONIC, &now); |
637 | = wlr_backend_get_presentation_clock(server.backend); | ||
638 | clock_gettime(presentation_clock, &now); | ||
639 | 290 | ||
640 | const long NSEC_IN_SECONDS = 1000000000; | 291 | const long NSEC_IN_SECONDS = 1000000000; |
641 | struct timespec predicted_refresh = output->last_presentation; | 292 | struct timespec predicted_refresh = output->last_presentation; |
@@ -682,124 +333,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
682 | struct send_frame_done_data data = {0}; | 333 | struct send_frame_done_data data = {0}; |
683 | clock_gettime(CLOCK_MONOTONIC, &data.when); | 334 | clock_gettime(CLOCK_MONOTONIC, &data.when); |
684 | data.msec_until_refresh = msec_until_refresh; | 335 | data.msec_until_refresh = msec_until_refresh; |
685 | send_frame_done(output, &data); | 336 | data.output = output; |
686 | } | 337 | wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); |
687 | |||
688 | void output_damage_whole(struct sway_output *output) { | ||
689 | // The output can exist with no wlr_output if it's just been disconnected | ||
690 | // and the transaction to evacuate it has't completed yet. | ||
691 | if (output && output->wlr_output && output->damage) { | ||
692 | wlr_output_damage_add_whole(output->damage); | ||
693 | } | ||
694 | } | ||
695 | |||
696 | static void damage_surface_iterator(struct sway_output *output, struct sway_view *view, | ||
697 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
698 | void *_data) { | ||
699 | bool *data = _data; | ||
700 | bool whole = *data; | ||
701 | |||
702 | struct wlr_box box = *_box; | ||
703 | scale_box(&box, output->wlr_output->scale); | ||
704 | |||
705 | int center_x = box.x + box.width/2; | ||
706 | int center_y = box.y + box.height/2; | ||
707 | |||
708 | if (pixman_region32_not_empty(&surface->buffer_damage)) { | ||
709 | pixman_region32_t damage; | ||
710 | pixman_region32_init(&damage); | ||
711 | wlr_surface_get_effective_damage(surface, &damage); | ||
712 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); | ||
713 | if (ceil(output->wlr_output->scale) > surface->current.scale) { | ||
714 | // When scaling up a surface, it'll become blurry so we need to | ||
715 | // expand the damage region | ||
716 | wlr_region_expand(&damage, &damage, | ||
717 | ceil(output->wlr_output->scale) - surface->current.scale); | ||
718 | } | ||
719 | pixman_region32_translate(&damage, box.x, box.y); | ||
720 | wlr_region_rotated_bounds(&damage, &damage, rotation, | ||
721 | center_x, center_y); | ||
722 | wlr_output_damage_add(output->damage, &damage); | ||
723 | pixman_region32_fini(&damage); | ||
724 | } | ||
725 | |||
726 | if (whole) { | ||
727 | wlr_box_rotated_bounds(&box, &box, rotation); | ||
728 | wlr_output_damage_add_box(output->damage, &box); | ||
729 | } | ||
730 | |||
731 | if (!wl_list_empty(&surface->current.frame_callback_list)) { | ||
732 | wlr_output_schedule_frame(output->wlr_output); | ||
733 | } | ||
734 | } | ||
735 | |||
736 | void output_damage_surface(struct sway_output *output, double ox, double oy, | ||
737 | struct wlr_surface *surface, bool whole) { | ||
738 | output_surface_for_each_surface(output, surface, ox, oy, | ||
739 | damage_surface_iterator, &whole); | ||
740 | } | ||
741 | |||
742 | void output_damage_from_view(struct sway_output *output, | ||
743 | struct sway_view *view) { | ||
744 | if (!view_is_visible(view)) { | ||
745 | return; | ||
746 | } | ||
747 | bool whole = false; | ||
748 | output_view_for_each_surface(output, view, damage_surface_iterator, &whole); | ||
749 | } | ||
750 | |||
751 | // Expecting an unscaled box in layout coordinates | ||
752 | void output_damage_box(struct sway_output *output, struct wlr_box *_box) { | ||
753 | struct wlr_box box; | ||
754 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
755 | box.x -= output->lx; | ||
756 | box.y -= output->ly; | ||
757 | scale_box(&box, output->wlr_output->scale); | ||
758 | wlr_output_damage_add_box(output->damage, &box); | ||
759 | } | ||
760 | |||
761 | static void damage_child_views_iterator(struct sway_container *con, | ||
762 | void *data) { | ||
763 | if (!con->view || !view_is_visible(con->view)) { | ||
764 | return; | ||
765 | } | ||
766 | struct sway_output *output = data; | ||
767 | bool whole = true; | ||
768 | output_view_for_each_surface(output, con->view, damage_surface_iterator, | ||
769 | &whole); | ||
770 | } | ||
771 | |||
772 | void output_damage_whole_container(struct sway_output *output, | ||
773 | struct sway_container *con) { | ||
774 | // Pad the box by 1px, because the width is a double and might be a fraction | ||
775 | struct wlr_box box = { | ||
776 | .x = con->current.x - output->lx - 1, | ||
777 | .y = con->current.y - output->ly - 1, | ||
778 | .width = con->current.width + 2, | ||
779 | .height = con->current.height + 2, | ||
780 | }; | ||
781 | scale_box(&box, output->wlr_output->scale); | ||
782 | wlr_output_damage_add_box(output->damage, &box); | ||
783 | // Damage subsurfaces as well, which may extend outside the box | ||
784 | if (con->view) { | ||
785 | damage_child_views_iterator(con, output); | ||
786 | } else { | ||
787 | container_for_each_child(con, damage_child_views_iterator, output); | ||
788 | } | ||
789 | } | ||
790 | |||
791 | static void damage_handle_destroy(struct wl_listener *listener, void *data) { | ||
792 | struct sway_output *output = | ||
793 | wl_container_of(listener, output, damage_destroy); | ||
794 | if (!output->enabled) { | ||
795 | return; | ||
796 | } | ||
797 | output_disable(output); | ||
798 | |||
799 | wl_list_remove(&output->damage_destroy.link); | ||
800 | wl_list_remove(&output->damage_frame.link); | ||
801 | |||
802 | transaction_commit_dirty(); | ||
803 | } | 338 | } |
804 | 339 | ||
805 | static void update_output_manager_config(struct sway_server *server) { | 340 | static void update_output_manager_config(struct sway_server *server) { |
@@ -808,73 +343,61 @@ static void update_output_manager_config(struct sway_server *server) { | |||
808 | 343 | ||
809 | struct sway_output *output; | 344 | struct sway_output *output; |
810 | wl_list_for_each(output, &root->all_outputs, link) { | 345 | wl_list_for_each(output, &root->all_outputs, link) { |
811 | if (output == root->noop_output) { | 346 | if (output == root->fallback_output) { |
812 | continue; | 347 | continue; |
813 | } | 348 | } |
814 | struct wlr_output_configuration_head_v1 *config_head = | 349 | struct wlr_output_configuration_head_v1 *config_head = |
815 | wlr_output_configuration_head_v1_create(config, output->wlr_output); | 350 | wlr_output_configuration_head_v1_create(config, output->wlr_output); |
816 | struct wlr_box *output_box = wlr_output_layout_get_box( | 351 | struct wlr_box output_box; |
817 | root->output_layout, output->wlr_output); | 352 | wlr_output_layout_get_box(root->output_layout, |
818 | // We mark the output enabled even if it is switched off by DPMS | 353 | output->wlr_output, &output_box); |
819 | config_head->state.enabled = output->enabled; | 354 | // We mark the output enabled when it's switched off but not disabled |
820 | config_head->state.mode = output->current_mode; | 355 | config_head->state.enabled = !wlr_box_empty(&output_box); |
821 | if (output_box) { | 356 | config_head->state.x = output_box.x; |
822 | config_head->state.x = output_box->x; | 357 | config_head->state.y = output_box.y; |
823 | config_head->state.y = output_box->y; | ||
824 | } | ||
825 | } | 358 | } |
826 | 359 | ||
827 | 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(); | ||
828 | } | 363 | } |
829 | 364 | ||
830 | static void handle_destroy(struct wl_listener *listener, void *data) { | 365 | static void begin_destroy(struct sway_output *output) { |
831 | struct sway_output *output = wl_container_of(listener, output, destroy); | ||
832 | struct sway_server *server = output->server; | 366 | struct sway_server *server = output->server; |
833 | wl_signal_emit(&output->events.destroy, output); | ||
834 | 367 | ||
835 | if (output->enabled) { | 368 | if (output->enabled) { |
836 | output_disable(output); | 369 | output_disable(output); |
837 | } | 370 | } |
371 | |||
838 | output_begin_destroy(output); | 372 | output_begin_destroy(output); |
839 | 373 | ||
374 | wl_list_remove(&output->link); | ||
375 | |||
376 | wl_list_remove(&output->layout_destroy.link); | ||
840 | wl_list_remove(&output->destroy.link); | 377 | wl_list_remove(&output->destroy.link); |
841 | wl_list_remove(&output->commit.link); | 378 | wl_list_remove(&output->commit.link); |
842 | wl_list_remove(&output->mode.link); | ||
843 | wl_list_remove(&output->present.link); | 379 | wl_list_remove(&output->present.link); |
380 | wl_list_remove(&output->frame.link); | ||
381 | wl_list_remove(&output->request_state.link); | ||
382 | |||
383 | wlr_scene_output_destroy(output->scene_output); | ||
384 | output->scene_output = NULL; | ||
385 | output->wlr_output->data = NULL; | ||
386 | output->wlr_output = NULL; | ||
844 | 387 | ||
845 | transaction_commit_dirty(); | 388 | transaction_commit_dirty(); |
846 | 389 | ||
847 | update_output_manager_config(server); | 390 | update_output_manager_config(server); |
848 | } | 391 | } |
849 | 392 | ||
850 | static void handle_mode(struct wl_listener *listener, void *data) { | 393 | static void handle_destroy(struct wl_listener *listener, void *data) { |
851 | struct sway_output *output = wl_container_of(listener, output, mode); | 394 | struct sway_output *output = wl_container_of(listener, output, destroy); |
852 | if (!output->enabled && !output->enabling) { | 395 | begin_destroy(output); |
853 | struct output_config *oc = find_output_config(output); | ||
854 | if (output->wlr_output->current_mode != NULL && | ||
855 | (!oc || oc->enabled)) { | ||
856 | // We want to enable this output, but it didn't work last time, | ||
857 | // possibly because we hadn't enough CRTCs. Try again now that the | ||
858 | // output has a mode. | ||
859 | sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, " | ||
860 | "trying to enable it", output->wlr_output->name); | ||
861 | apply_output_config(oc, output); | ||
862 | } | ||
863 | return; | ||
864 | } | ||
865 | if (!output->enabled) { | ||
866 | return; | ||
867 | } | ||
868 | arrange_layers(output); | ||
869 | arrange_output(output); | ||
870 | transaction_commit_dirty(); | ||
871 | |||
872 | update_output_manager_config(output->server); | ||
873 | } | 396 | } |
874 | 397 | ||
875 | static void update_textures(struct sway_container *con, void *data) { | 398 | static void handle_layout_destroy(struct wl_listener *listener, void *data) { |
876 | container_update_title_textures(con); | 399 | struct sway_output *output = wl_container_of(listener, output, layout_destroy); |
877 | container_update_marks_textures(con); | 400 | begin_destroy(output); |
878 | } | 401 | } |
879 | 402 | ||
880 | static void handle_commit(struct wl_listener *listener, void *data) { | 403 | static void handle_commit(struct wl_listener *listener, void *data) { |
@@ -885,24 +408,28 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
885 | return; | 408 | return; |
886 | } | 409 | } |
887 | 410 | ||
888 | if (event->committed & WLR_OUTPUT_STATE_SCALE) { | 411 | if (event->state->committed & ( |
889 | output_for_each_container(output, update_textures, NULL); | 412 | WLR_OUTPUT_STATE_MODE | |
890 | } | 413 | WLR_OUTPUT_STATE_TRANSFORM | |
891 | 414 | WLR_OUTPUT_STATE_SCALE)) { | |
892 | if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { | ||
893 | arrange_layers(output); | 415 | arrange_layers(output); |
894 | arrange_output(output); | 416 | arrange_output(output); |
895 | transaction_commit_dirty(); | 417 | transaction_commit_dirty(); |
896 | 418 | ||
897 | update_output_manager_config(output->server); | 419 | update_output_manager_config(output->server); |
898 | } | 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 | } | ||
899 | } | 426 | } |
900 | 427 | ||
901 | static void handle_present(struct wl_listener *listener, void *data) { | 428 | static void handle_present(struct wl_listener *listener, void *data) { |
902 | struct sway_output *output = wl_container_of(listener, output, present); | 429 | struct sway_output *output = wl_container_of(listener, output, present); |
903 | struct wlr_output_event_present *output_event = data; | 430 | struct wlr_output_event_present *output_event = data; |
904 | 431 | ||
905 | if (!output->enabled) { | 432 | if (!output->enabled || !output_event->presented) { |
906 | return; | 433 | return; |
907 | } | 434 | } |
908 | 435 | ||
@@ -910,37 +437,91 @@ static void handle_present(struct wl_listener *listener, void *data) { | |||
910 | output->refresh_nsec = output_event->refresh; | 437 | output->refresh_nsec = output_event->refresh; |
911 | } | 438 | } |
912 | 439 | ||
440 | static void handle_request_state(struct wl_listener *listener, void *data) { | ||
441 | struct sway_output *output = | ||
442 | wl_container_of(listener, output, request_state); | ||
443 | const struct wlr_output_event_request_state *event = data; | ||
444 | wlr_output_commit_state(output->wlr_output, event->state); | ||
445 | } | ||
446 | |||
447 | static unsigned int last_headless_num = 0; | ||
448 | |||
913 | void handle_new_output(struct wl_listener *listener, void *data) { | 449 | void handle_new_output(struct wl_listener *listener, void *data) { |
914 | struct sway_server *server = wl_container_of(listener, server, new_output); | 450 | struct sway_server *server = wl_container_of(listener, server, new_output); |
915 | struct wlr_output *wlr_output = data; | 451 | struct wlr_output *wlr_output = data; |
916 | sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); | 452 | |
453 | if (wlr_output == root->fallback_output->wlr_output) { | ||
454 | return; | ||
455 | } | ||
456 | |||
457 | if (wlr_output_is_headless(wlr_output)) { | ||
458 | char name[64]; | ||
459 | snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num); | ||
460 | wlr_output_set_name(wlr_output, name); | ||
461 | } | ||
462 | |||
463 | sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)", | ||
464 | wlr_output, wlr_output->name, wlr_output->non_desktop); | ||
465 | |||
466 | if (wlr_output->non_desktop) { | ||
467 | sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); | ||
468 | struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output); | ||
469 | #if WLR_HAS_DRM_BACKEND | ||
470 | if (server->drm_lease_manager) { | ||
471 | wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, | ||
472 | wlr_output); | ||
473 | } | ||
474 | #endif | ||
475 | list_add(root->non_desktop_outputs, non_desktop); | ||
476 | return; | ||
477 | } | ||
478 | |||
479 | if (!wlr_output_init_render(wlr_output, server->allocator, | ||
480 | server->renderer)) { | ||
481 | sway_log(SWAY_ERROR, "Failed to init output render"); | ||
482 | return; | ||
483 | } | ||
484 | |||
485 | // Create the scene output here so we're not accidentally creating one for | ||
486 | // the fallback output | ||
487 | struct wlr_scene_output *scene_output = | ||
488 | wlr_scene_output_create(root->root_scene, wlr_output); | ||
489 | if (!scene_output) { | ||
490 | sway_log(SWAY_ERROR, "Failed to create a scene output"); | ||
491 | return; | ||
492 | } | ||
917 | 493 | ||
918 | struct sway_output *output = output_create(wlr_output); | 494 | struct sway_output *output = output_create(wlr_output); |
919 | if (!output) { | 495 | if (!output) { |
496 | sway_log(SWAY_ERROR, "Failed to create a sway output"); | ||
497 | wlr_scene_output_destroy(scene_output); | ||
920 | return; | 498 | return; |
921 | } | 499 | } |
500 | |||
922 | output->server = server; | 501 | output->server = server; |
923 | output->damage = wlr_output_damage_create(wlr_output); | 502 | output->scene_output = scene_output; |
924 | 503 | ||
504 | wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); | ||
505 | output->layout_destroy.notify = handle_layout_destroy; | ||
925 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | 506 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); |
926 | output->destroy.notify = handle_destroy; | 507 | output->destroy.notify = handle_destroy; |
927 | wl_signal_add(&wlr_output->events.commit, &output->commit); | 508 | wl_signal_add(&wlr_output->events.commit, &output->commit); |
928 | output->commit.notify = handle_commit; | 509 | output->commit.notify = handle_commit; |
929 | wl_signal_add(&wlr_output->events.mode, &output->mode); | ||
930 | output->mode.notify = handle_mode; | ||
931 | wl_signal_add(&wlr_output->events.present, &output->present); | 510 | wl_signal_add(&wlr_output->events.present, &output->present); |
932 | output->present.notify = handle_present; | 511 | output->present.notify = handle_present; |
933 | wl_signal_add(&output->damage->events.frame, &output->damage_frame); | 512 | wl_signal_add(&wlr_output->events.frame, &output->frame); |
934 | output->damage_frame.notify = damage_handle_frame; | 513 | output->frame.notify = handle_frame; |
935 | wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); | 514 | wl_signal_add(&wlr_output->events.request_state, &output->request_state); |
936 | output->damage_destroy.notify = damage_handle_destroy; | 515 | output->request_state.notify = handle_request_state; |
937 | 516 | ||
938 | 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, |
939 | output_repaint_timer_handler, output); | 518 | output_repaint_timer_handler, output); |
940 | 519 | ||
941 | struct output_config *oc = find_output_config(output); | 520 | if (server->session_lock.lock) { |
942 | apply_output_config(oc, output); | 521 | sway_session_lock_add_output(server->session_lock.lock, output); |
943 | free_output_config(oc); | 522 | } |
523 | |||
524 | apply_all_output_configs(); | ||
944 | 525 | ||
945 | transaction_commit_dirty(); | 526 | transaction_commit_dirty(); |
946 | 527 | ||
@@ -954,62 +535,103 @@ void handle_output_layout_change(struct wl_listener *listener, | |||
954 | update_output_manager_config(server); | 535 | update_output_manager_config(server); |
955 | } | 536 | } |
956 | 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 | |||
957 | static void output_manager_apply(struct sway_server *server, | 581 | static void output_manager_apply(struct sway_server *server, |
958 | struct wlr_output_configuration_v1 *config, bool test_only) { | 582 | struct wlr_output_configuration_v1 *config, bool test_only) { |
959 | // TODO: perform atomic tests on the whole backend atomically | 583 | size_t configs_len = wl_list_length(&root->all_outputs); |
960 | 584 | struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); | |
961 | struct wlr_output_configuration_head_v1 *config_head; | 585 | if (!configs) { |
962 | // First disable outputs we need to disable | 586 | return; |
963 | bool ok = true; | 587 | } |
964 | wl_list_for_each(config_head, &config->heads, link) { | 588 | |
965 | struct wlr_output *wlr_output = config_head->state.output; | 589 | int config_idx = 0; |
966 | struct sway_output *output = wlr_output->data; | 590 | struct sway_output *sway_output; |
967 | 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--; | ||
968 | continue; | 594 | continue; |
969 | } | 595 | } |
970 | struct output_config *oc = new_output_config(output->wlr_output->name); | ||
971 | oc->enabled = false; | ||
972 | 596 | ||
973 | if (test_only) { | 597 | struct matched_output_config *cfg = &configs[config_idx++]; |
974 | ok &= test_output_config(oc, output); | 598 | cfg->output = sway_output; |
975 | } else { | ||
976 | oc = store_output_config(oc); | ||
977 | ok &= apply_output_config(oc, output); | ||
978 | } | ||
979 | } | ||
980 | 599 | ||
981 | // Then enable outputs that need to | 600 | struct wlr_output_configuration_head_v1 *config_head; |
982 | wl_list_for_each(config_head, &config->heads, link) { | 601 | wl_list_for_each(config_head, &config->heads, link) { |
983 | struct wlr_output *wlr_output = config_head->state.output; | 602 | if (config_head->state.output == sway_output->wlr_output) { |
984 | struct sway_output *output = wlr_output->data; | 603 | cfg->config = output_config_for_config_head(config_head, sway_output); |
985 | if (!config_head->state.enabled) { | 604 | break; |
986 | continue; | 605 | } |
987 | } | 606 | } |
988 | struct output_config *oc = new_output_config(output->wlr_output->name); | 607 | if (!cfg->config) { |
989 | oc->enabled = true; | 608 | cfg->config = find_output_config(sway_output); |
990 | if (config_head->state.mode != NULL) { | ||
991 | struct wlr_output_mode *mode = config_head->state.mode; | ||
992 | oc->width = mode->width; | ||
993 | oc->height = mode->height; | ||
994 | oc->refresh_rate = mode->refresh / 1000.f; | ||
995 | } else { | ||
996 | oc->width = config_head->state.custom_mode.width; | ||
997 | oc->height = config_head->state.custom_mode.height; | ||
998 | oc->refresh_rate = | ||
999 | config_head->state.custom_mode.refresh / 1000.f; | ||
1000 | } | 609 | } |
1001 | oc->x = config_head->state.x; | 610 | } |
1002 | oc->y = config_head->state.y; | 611 | |
1003 | oc->transform = config_head->state.transform; | 612 | bool ok = apply_output_configs(configs, configs_len, test_only); |
1004 | oc->scale = config_head->state.scale; | 613 | for (size_t idx = 0; idx < configs_len; idx++) { |
614 | struct matched_output_config *cfg = &configs[idx]; | ||
1005 | 615 | ||
1006 | if (test_only) { | 616 | // Only store new configs for successful non-test commits. Old configs, |
1007 | ok &= test_output_config(oc, output); | 617 | // test-only and failed commits just get freed. |
618 | bool store_config = false; | ||
619 | if (!test_only && ok) { | ||
620 | struct wlr_output_configuration_head_v1 *config_head; | ||
621 | wl_list_for_each(config_head, &config->heads, link) { | ||
622 | if (config_head->state.output == cfg->output->wlr_output) { | ||
623 | store_config = true; | ||
624 | break; | ||
625 | } | ||
626 | } | ||
627 | } | ||
628 | if (store_config) { | ||
629 | store_output_config(cfg->config); | ||
1008 | } else { | 630 | } else { |
1009 | oc = store_output_config(oc); | 631 | free_output_config(cfg->config); |
1010 | ok &= apply_output_config(oc, output); | ||
1011 | } | 632 | } |
1012 | } | 633 | } |
634 | free(configs); | ||
1013 | 635 | ||
1014 | if (ok) { | 636 | if (ok) { |
1015 | wlr_output_configuration_v1_send_succeeded(config); | 637 | wlr_output_configuration_v1_send_succeeded(config); |
@@ -1047,12 +669,12 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, | |||
1047 | struct output_config *oc = new_output_config(output->wlr_output->name); | 669 | struct output_config *oc = new_output_config(output->wlr_output->name); |
1048 | switch (event->mode) { | 670 | switch (event->mode) { |
1049 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: | 671 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: |
1050 | oc->dpms_state = DPMS_OFF; | 672 | oc->power = 0; |
1051 | break; | 673 | break; |
1052 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: | 674 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: |
1053 | oc->dpms_state = DPMS_ON; | 675 | oc->power = 1; |
1054 | break; | 676 | break; |
1055 | } | 677 | } |
1056 | oc = store_output_config(oc); | 678 | store_output_config(oc); |
1057 | apply_output_config(oc, output); | 679 | apply_all_output_configs(); |
1058 | } | 680 | } |
diff --git a/sway/desktop/render.c b/sway/desktop/render.c deleted file mode 100644 index bd85282c..00000000 --- a/sway/desktop/render.c +++ /dev/null | |||
@@ -1,1125 +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_box.h> | ||
11 | #include <wlr/types/wlr_buffer.h> | ||
12 | #include <wlr/types/wlr_matrix.h> | ||
13 | #include <wlr/types/wlr_output_damage.h> | ||
14 | #include <wlr/types/wlr_output_layout.h> | ||
15 | #include <wlr/types/wlr_output.h> | ||
16 | #include <wlr/types/wlr_surface.h> | ||
17 | #include <wlr/util/region.h> | ||
18 | #include "log.h" | ||
19 | #include "config.h" | ||
20 | #include "sway/config.h" | ||
21 | #include "sway/input/input-manager.h" | ||
22 | #include "sway/input/seat.h" | ||
23 | #include "sway/layers.h" | ||
24 | #include "sway/output.h" | ||
25 | #include "sway/server.h" | ||
26 | #include "sway/tree/arrange.h" | ||
27 | #include "sway/tree/container.h" | ||
28 | #include "sway/tree/root.h" | ||
29 | #include "sway/tree/view.h" | ||
30 | #include "sway/tree/workspace.h" | ||
31 | |||
32 | struct render_data { | ||
33 | pixman_region32_t *damage; | ||
34 | float alpha; | ||
35 | }; | ||
36 | |||
37 | /** | ||
38 | * Apply scale to a width or height. | ||
39 | * | ||
40 | * One does not simply multiply the width by the scale. We allow fractional | ||
41 | * scaling, which means the resulting scaled width might be a decimal. | ||
42 | * So we round it. | ||
43 | * | ||
44 | * But even this can produce undesirable results depending on the X or Y offset | ||
45 | * of the box. For example, with a scale of 1.5, a box with width=1 should not | ||
46 | * scale to 2px if its X coordinate is 1, because the X coordinate would have | ||
47 | * scaled to 2px. | ||
48 | */ | ||
49 | static int scale_length(int length, int offset, float scale) { | ||
50 | return round((offset + length) * scale) - round(offset * scale); | ||
51 | } | ||
52 | |||
53 | static void scissor_output(struct wlr_output *wlr_output, | ||
54 | pixman_box32_t *rect) { | ||
55 | struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); | ||
56 | assert(renderer); | ||
57 | |||
58 | struct wlr_box box = { | ||
59 | .x = rect->x1, | ||
60 | .y = rect->y1, | ||
61 | .width = rect->x2 - rect->x1, | ||
62 | .height = rect->y2 - rect->y1, | ||
63 | }; | ||
64 | |||
65 | int ow, oh; | ||
66 | wlr_output_transformed_resolution(wlr_output, &ow, &oh); | ||
67 | |||
68 | enum wl_output_transform transform = | ||
69 | wlr_output_transform_invert(wlr_output->transform); | ||
70 | wlr_box_transform(&box, &box, transform, ow, oh); | ||
71 | |||
72 | wlr_renderer_scissor(renderer, &box); | ||
73 | } | ||
74 | |||
75 | static void set_scale_filter(struct wlr_output *wlr_output, | ||
76 | struct wlr_texture *texture, enum scale_filter_mode scale_filter) { | ||
77 | if (!wlr_texture_is_gles2(texture)) { | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | struct wlr_gles2_texture_attribs attribs; | ||
82 | wlr_gles2_texture_get_attribs(texture, &attribs); | ||
83 | |||
84 | glBindTexture(attribs.target, attribs.tex); | ||
85 | |||
86 | switch (scale_filter) { | ||
87 | case SCALE_FILTER_LINEAR: | ||
88 | glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
89 | break; | ||
90 | case SCALE_FILTER_NEAREST: | ||
91 | glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
92 | break; | ||
93 | case SCALE_FILTER_DEFAULT: | ||
94 | case SCALE_FILTER_SMART: | ||
95 | assert(false); // unreachable | ||
96 | } | ||
97 | } | ||
98 | |||
99 | static void render_texture(struct wlr_output *wlr_output, | ||
100 | pixman_region32_t *output_damage, struct wlr_texture *texture, | ||
101 | const struct wlr_fbox *src_box, const struct wlr_box *dst_box, | ||
102 | const float matrix[static 9], float alpha) { | ||
103 | struct wlr_renderer *renderer = | ||
104 | wlr_backend_get_renderer(wlr_output->backend); | ||
105 | struct sway_output *output = wlr_output->data; | ||
106 | |||
107 | struct wlr_gles2_texture_attribs attribs; | ||
108 | wlr_gles2_texture_get_attribs(texture, &attribs); | ||
109 | |||
110 | pixman_region32_t damage; | ||
111 | pixman_region32_init(&damage); | ||
112 | pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, | ||
113 | dst_box->width, dst_box->height); | ||
114 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
115 | bool damaged = pixman_region32_not_empty(&damage); | ||
116 | if (!damaged) { | ||
117 | goto damage_finish; | ||
118 | } | ||
119 | |||
120 | int nrects; | ||
121 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
122 | for (int i = 0; i < nrects; ++i) { | ||
123 | scissor_output(wlr_output, &rects[i]); | ||
124 | set_scale_filter(wlr_output, texture, output->scale_filter); | ||
125 | if (src_box != NULL) { | ||
126 | wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha); | ||
127 | } else { | ||
128 | wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | damage_finish: | ||
133 | pixman_region32_fini(&damage); | ||
134 | } | ||
135 | |||
136 | static void render_surface_iterator(struct sway_output *output, struct sway_view *view, | ||
137 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
138 | void *_data) { | ||
139 | struct render_data *data = _data; | ||
140 | struct wlr_output *wlr_output = output->wlr_output; | ||
141 | pixman_region32_t *output_damage = data->damage; | ||
142 | float alpha = data->alpha; | ||
143 | |||
144 | struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
145 | if (!texture) { | ||
146 | return; | ||
147 | } | ||
148 | |||
149 | struct wlr_fbox src_box; | ||
150 | wlr_surface_get_buffer_source_box(surface, &src_box); | ||
151 | |||
152 | struct wlr_box dst_box = *_box; | ||
153 | scale_box(&dst_box, wlr_output->scale); | ||
154 | |||
155 | float matrix[9]; | ||
156 | enum wl_output_transform transform = | ||
157 | wlr_output_transform_invert(surface->current.transform); | ||
158 | wlr_matrix_project_box(matrix, &dst_box, transform, rotation, | ||
159 | wlr_output->transform_matrix); | ||
160 | |||
161 | render_texture(wlr_output, output_damage, texture, | ||
162 | &src_box, &dst_box, matrix, alpha); | ||
163 | |||
164 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
165 | wlr_output); | ||
166 | } | ||
167 | |||
168 | static void render_layer_toplevel(struct sway_output *output, | ||
169 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
170 | struct render_data data = { | ||
171 | .damage = damage, | ||
172 | .alpha = 1.0f, | ||
173 | }; | ||
174 | output_layer_for_each_toplevel_surface(output, layer_surfaces, | ||
175 | render_surface_iterator, &data); | ||
176 | } | ||
177 | |||
178 | static void render_layer_popups(struct sway_output *output, | ||
179 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
180 | struct render_data data = { | ||
181 | .damage = damage, | ||
182 | .alpha = 1.0f, | ||
183 | }; | ||
184 | output_layer_for_each_popup_surface(output, layer_surfaces, | ||
185 | render_surface_iterator, &data); | ||
186 | } | ||
187 | |||
188 | #if HAVE_XWAYLAND | ||
189 | static void render_unmanaged(struct sway_output *output, | ||
190 | pixman_region32_t *damage, struct wl_list *unmanaged) { | ||
191 | struct render_data data = { | ||
192 | .damage = damage, | ||
193 | .alpha = 1.0f, | ||
194 | }; | ||
195 | output_unmanaged_for_each_surface(output, unmanaged, | ||
196 | render_surface_iterator, &data); | ||
197 | } | ||
198 | #endif | ||
199 | |||
200 | static void render_drag_icons(struct sway_output *output, | ||
201 | pixman_region32_t *damage, struct wl_list *drag_icons) { | ||
202 | struct render_data data = { | ||
203 | .damage = damage, | ||
204 | .alpha = 1.0f, | ||
205 | }; | ||
206 | output_drag_icons_for_each_surface(output, drag_icons, | ||
207 | render_surface_iterator, &data); | ||
208 | } | ||
209 | |||
210 | // _box.x and .y are expected to be layout-local | ||
211 | // _box.width and .height are expected to be output-buffer-local | ||
212 | void render_rect(struct sway_output *output, | ||
213 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
214 | float color[static 4]) { | ||
215 | struct wlr_output *wlr_output = output->wlr_output; | ||
216 | struct wlr_renderer *renderer = | ||
217 | wlr_backend_get_renderer(wlr_output->backend); | ||
218 | |||
219 | struct wlr_box box; | ||
220 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
221 | box.x -= output->lx * wlr_output->scale; | ||
222 | box.y -= output->ly * wlr_output->scale; | ||
223 | |||
224 | pixman_region32_t damage; | ||
225 | pixman_region32_init(&damage); | ||
226 | pixman_region32_union_rect(&damage, &damage, box.x, box.y, | ||
227 | box.width, box.height); | ||
228 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
229 | bool damaged = pixman_region32_not_empty(&damage); | ||
230 | if (!damaged) { | ||
231 | goto damage_finish; | ||
232 | } | ||
233 | |||
234 | int nrects; | ||
235 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
236 | for (int i = 0; i < nrects; ++i) { | ||
237 | scissor_output(wlr_output, &rects[i]); | ||
238 | wlr_render_rect(renderer, &box, color, | ||
239 | wlr_output->transform_matrix); | ||
240 | } | ||
241 | |||
242 | damage_finish: | ||
243 | pixman_region32_fini(&damage); | ||
244 | } | ||
245 | |||
246 | void premultiply_alpha(float color[4], float opacity) { | ||
247 | color[3] *= opacity; | ||
248 | color[0] *= color[3]; | ||
249 | color[1] *= color[3]; | ||
250 | color[2] *= color[3]; | ||
251 | } | ||
252 | |||
253 | static void render_view_toplevels(struct sway_view *view, | ||
254 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
255 | struct render_data data = { | ||
256 | .damage = damage, | ||
257 | .alpha = alpha, | ||
258 | }; | ||
259 | // Render all toplevels without descending into popups | ||
260 | double ox = view->container->surface_x - | ||
261 | output->lx - view->geometry.x; | ||
262 | double oy = view->container->surface_y - | ||
263 | output->ly - view->geometry.y; | ||
264 | output_surface_for_each_surface(output, view->surface, ox, oy, | ||
265 | render_surface_iterator, &data); | ||
266 | } | ||
267 | |||
268 | static void render_view_popups(struct sway_view *view, | ||
269 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
270 | struct render_data data = { | ||
271 | .damage = damage, | ||
272 | .alpha = alpha, | ||
273 | }; | ||
274 | output_view_for_each_popup_surface(output, view, | ||
275 | render_surface_iterator, &data); | ||
276 | } | ||
277 | |||
278 | static void render_saved_view(struct sway_view *view, | ||
279 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
280 | struct wlr_output *wlr_output = output->wlr_output; | ||
281 | |||
282 | if (wl_list_empty(&view->saved_buffers)) { | ||
283 | return; | ||
284 | } | ||
285 | struct sway_saved_buffer *saved_buf; | ||
286 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
287 | if (!saved_buf->buffer->texture) { | ||
288 | continue; | ||
289 | } | ||
290 | |||
291 | struct wlr_box box = { | ||
292 | .x = view->container->surface_x - output->lx - | ||
293 | view->saved_geometry.x + saved_buf->x, | ||
294 | .y = view->container->surface_y - output->ly - | ||
295 | view->saved_geometry.y + saved_buf->y, | ||
296 | .width = saved_buf->width, | ||
297 | .height = saved_buf->height, | ||
298 | }; | ||
299 | |||
300 | struct wlr_box output_box = { | ||
301 | .width = output->width, | ||
302 | .height = output->height, | ||
303 | }; | ||
304 | |||
305 | struct wlr_box intersection; | ||
306 | bool intersects = wlr_box_intersection(&intersection, &output_box, &box); | ||
307 | if (!intersects) { | ||
308 | continue; | ||
309 | } | ||
310 | |||
311 | scale_box(&box, wlr_output->scale); | ||
312 | |||
313 | float matrix[9]; | ||
314 | enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); | ||
315 | wlr_matrix_project_box(matrix, &box, transform, 0, | ||
316 | wlr_output->transform_matrix); | ||
317 | |||
318 | render_texture(wlr_output, damage, saved_buf->buffer->texture, | ||
319 | &saved_buf->source_box, &box, matrix, alpha); | ||
320 | } | ||
321 | |||
322 | // FIXME: we should set the surface that this saved buffer originates from | ||
323 | // as sampled here. | ||
324 | // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * Render a view's surface and left/bottom/right borders. | ||
329 | */ | ||
330 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | ||
331 | struct sway_container *con, struct border_colors *colors) { | ||
332 | struct sway_view *view = con->view; | ||
333 | if (!wl_list_empty(&view->saved_buffers)) { | ||
334 | render_saved_view(view, output, damage, view->container->alpha); | ||
335 | } else if (view->surface) { | ||
336 | render_view_toplevels(view, output, damage, view->container->alpha); | ||
337 | } | ||
338 | |||
339 | if (con->current.border == B_NONE || con->current.border == B_CSD) { | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | struct wlr_box box; | ||
344 | float output_scale = output->wlr_output->scale; | ||
345 | float color[4]; | ||
346 | struct sway_container_state *state = &con->current; | ||
347 | |||
348 | if (state->border_left) { | ||
349 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
350 | premultiply_alpha(color, con->alpha); | ||
351 | box.x = state->x; | ||
352 | box.y = state->content_y; | ||
353 | box.width = state->border_thickness; | ||
354 | box.height = state->content_height; | ||
355 | scale_box(&box, output_scale); | ||
356 | render_rect(output, damage, &box, color); | ||
357 | } | ||
358 | |||
359 | list_t *siblings = container_get_current_siblings(con); | ||
360 | enum sway_container_layout layout = | ||
361 | container_current_parent_layout(con); | ||
362 | |||
363 | if (state->border_right) { | ||
364 | if (!container_is_floating(con) && siblings->length == 1 && layout == L_HORIZ) { | ||
365 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
366 | } else { | ||
367 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
368 | } | ||
369 | premultiply_alpha(color, con->alpha); | ||
370 | box.x = state->content_x + state->content_width; | ||
371 | box.y = state->content_y; | ||
372 | box.width = state->border_thickness; | ||
373 | box.height = state->content_height; | ||
374 | scale_box(&box, output_scale); | ||
375 | render_rect(output, damage, &box, color); | ||
376 | } | ||
377 | |||
378 | if (state->border_bottom) { | ||
379 | if (!container_is_floating(con) && siblings->length == 1 && layout == L_VERT) { | ||
380 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
381 | } else { | ||
382 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
383 | } | ||
384 | premultiply_alpha(color, con->alpha); | ||
385 | box.x = state->x; | ||
386 | box.y = state->content_y + state->content_height; | ||
387 | box.width = state->width; | ||
388 | box.height = state->border_thickness; | ||
389 | scale_box(&box, output_scale); | ||
390 | render_rect(output, damage, &box, color); | ||
391 | } | ||
392 | } | ||
393 | |||
394 | /** | ||
395 | * Render a titlebar. | ||
396 | * | ||
397 | * Care must be taken not to render over the same pixel multiple times, | ||
398 | * otherwise the colors will be incorrect when using opacity. | ||
399 | * | ||
400 | * The height is: 1px border, 3px padding, font height, 3px padding, 1px border | ||
401 | * The left side is: 1px border, 2px padding, title | ||
402 | */ | ||
403 | static void render_titlebar(struct sway_output *output, | ||
404 | pixman_region32_t *output_damage, struct sway_container *con, | ||
405 | int x, int y, int width, | ||
406 | struct border_colors *colors, struct wlr_texture *title_texture, | ||
407 | struct wlr_texture *marks_texture) { | ||
408 | struct wlr_box box; | ||
409 | float color[4]; | ||
410 | float output_scale = output->wlr_output->scale; | ||
411 | double output_x = output->lx; | ||
412 | double output_y = output->ly; | ||
413 | int titlebar_border_thickness = config->titlebar_border_thickness; | ||
414 | int titlebar_h_padding = config->titlebar_h_padding; | ||
415 | int titlebar_v_padding = config->titlebar_v_padding; | ||
416 | enum alignment title_align = config->title_align; | ||
417 | |||
418 | // Single pixel bar above title | ||
419 | memcpy(&color, colors->border, sizeof(float) * 4); | ||
420 | premultiply_alpha(color, con->alpha); | ||
421 | box.x = x; | ||
422 | box.y = y; | ||
423 | box.width = width; | ||
424 | box.height = titlebar_border_thickness; | ||
425 | scale_box(&box, output_scale); | ||
426 | render_rect(output, output_damage, &box, color); | ||
427 | |||
428 | // Single pixel bar below title | ||
429 | box.x = x; | ||
430 | box.y = y + container_titlebar_height() - titlebar_border_thickness; | ||
431 | box.width = width; | ||
432 | box.height = titlebar_border_thickness; | ||
433 | scale_box(&box, output_scale); | ||
434 | render_rect(output, output_damage, &box, color); | ||
435 | |||
436 | // Single pixel left edge | ||
437 | box.x = x; | ||
438 | box.y = y + titlebar_border_thickness; | ||
439 | box.width = titlebar_border_thickness; | ||
440 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
441 | scale_box(&box, output_scale); | ||
442 | render_rect(output, output_damage, &box, color); | ||
443 | |||
444 | // Single pixel right edge | ||
445 | box.x = x + width - titlebar_border_thickness; | ||
446 | box.y = y + titlebar_border_thickness; | ||
447 | box.width = titlebar_border_thickness; | ||
448 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
449 | scale_box(&box, output_scale); | ||
450 | render_rect(output, output_damage, &box, color); | ||
451 | |||
452 | int inner_x = x - output_x + titlebar_h_padding; | ||
453 | int bg_y = y + titlebar_border_thickness; | ||
454 | size_t inner_width = width - titlebar_h_padding * 2; | ||
455 | |||
456 | // output-buffer local | ||
457 | int ob_inner_x = round(inner_x * output_scale); | ||
458 | int ob_inner_width = scale_length(inner_width, inner_x, output_scale); | ||
459 | int ob_bg_height = scale_length( | ||
460 | (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
461 | config->font_height, bg_y, output_scale); | ||
462 | |||
463 | // Marks | ||
464 | int ob_marks_x = 0; // output-buffer-local | ||
465 | int ob_marks_width = 0; // output-buffer-local | ||
466 | if (config->show_marks && marks_texture) { | ||
467 | struct wlr_box texture_box; | ||
468 | wlr_texture_get_size(marks_texture, | ||
469 | &texture_box.width, &texture_box.height); | ||
470 | ob_marks_width = texture_box.width; | ||
471 | |||
472 | // The marks texture might be shorter than the config->font_height, in | ||
473 | // which case we need to pad it as evenly as possible above and below. | ||
474 | int ob_padding_total = ob_bg_height - texture_box.height; | ||
475 | int ob_padding_above = floor(ob_padding_total / 2.0); | ||
476 | int ob_padding_below = ceil(ob_padding_total / 2.0); | ||
477 | |||
478 | // Render texture. If the title is on the right, the marks will be on | ||
479 | // the left. Otherwise, they will be on the right. | ||
480 | if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) { | ||
481 | texture_box.x = ob_inner_x; | ||
482 | } else { | ||
483 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
484 | } | ||
485 | ob_marks_x = texture_box.x; | ||
486 | |||
487 | texture_box.y = round((bg_y - output_y) * output_scale) + | ||
488 | ob_padding_above; | ||
489 | |||
490 | float matrix[9]; | ||
491 | wlr_matrix_project_box(matrix, &texture_box, | ||
492 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
493 | 0.0, output->wlr_output->transform_matrix); | ||
494 | |||
495 | if (ob_inner_width < texture_box.width) { | ||
496 | texture_box.width = ob_inner_width; | ||
497 | } | ||
498 | render_texture(output->wlr_output, output_damage, marks_texture, | ||
499 | NULL, &texture_box, matrix, con->alpha); | ||
500 | |||
501 | // Padding above | ||
502 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
503 | premultiply_alpha(color, con->alpha); | ||
504 | box.x = texture_box.x + round(output_x * output_scale); | ||
505 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
506 | box.width = texture_box.width; | ||
507 | box.height = ob_padding_above; | ||
508 | render_rect(output, output_damage, &box, color); | ||
509 | |||
510 | // Padding below | ||
511 | box.y += ob_padding_above + texture_box.height; | ||
512 | box.height = ob_padding_below; | ||
513 | render_rect(output, output_damage, &box, color); | ||
514 | } | ||
515 | |||
516 | // Title text | ||
517 | int ob_title_x = 0; // output-buffer-local | ||
518 | int ob_title_width = 0; // output-buffer-local | ||
519 | if (title_texture) { | ||
520 | struct wlr_box texture_box; | ||
521 | wlr_texture_get_size(title_texture, | ||
522 | &texture_box.width, &texture_box.height); | ||
523 | ob_title_width = texture_box.width; | ||
524 | |||
525 | // The title texture might be shorter than the config->font_height, | ||
526 | // in which case we need to pad it above and below. | ||
527 | int ob_padding_above = round((config->font_baseline - | ||
528 | con->title_baseline + titlebar_v_padding - | ||
529 | titlebar_border_thickness) * output_scale); | ||
530 | int ob_padding_below = ob_bg_height - ob_padding_above - | ||
531 | texture_box.height; | ||
532 | |||
533 | // Render texture | ||
534 | if (texture_box.width > ob_inner_width - ob_marks_width) { | ||
535 | texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width) | ||
536 | ? ob_marks_x + ob_marks_width : ob_inner_x; | ||
537 | } else if (title_align == ALIGN_LEFT) { | ||
538 | texture_box.x = ob_inner_x; | ||
539 | } else if (title_align == ALIGN_CENTER) { | ||
540 | // If there are marks visible, center between the edge and marks. | ||
541 | // Otherwise, center in the inner area. | ||
542 | if (ob_marks_width) { | ||
543 | texture_box.x = (ob_inner_x + ob_marks_x) / 2 | ||
544 | - texture_box.width / 2; | ||
545 | } else { | ||
546 | texture_box.x = ob_inner_x + ob_inner_width / 2 | ||
547 | - texture_box.width / 2; | ||
548 | } | ||
549 | } else { | ||
550 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
551 | } | ||
552 | ob_title_x = texture_box.x; | ||
553 | |||
554 | texture_box.y = | ||
555 | round((bg_y - output_y) * output_scale) + ob_padding_above; | ||
556 | |||
557 | float matrix[9]; | ||
558 | wlr_matrix_project_box(matrix, &texture_box, | ||
559 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
560 | 0.0, output->wlr_output->transform_matrix); | ||
561 | |||
562 | if (ob_inner_width - ob_marks_width < texture_box.width) { | ||
563 | texture_box.width = ob_inner_width - ob_marks_width; | ||
564 | } | ||
565 | |||
566 | render_texture(output->wlr_output, output_damage, title_texture, | ||
567 | NULL, &texture_box, matrix, con->alpha); | ||
568 | |||
569 | // Padding above | ||
570 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
571 | premultiply_alpha(color, con->alpha); | ||
572 | box.x = texture_box.x + round(output_x * output_scale); | ||
573 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
574 | box.width = texture_box.width; | ||
575 | box.height = ob_padding_above; | ||
576 | render_rect(output, output_damage, &box, color); | ||
577 | |||
578 | // Padding below | ||
579 | box.y += ob_padding_above + texture_box.height; | ||
580 | box.height = ob_padding_below; | ||
581 | render_rect(output, output_damage, &box, color); | ||
582 | } | ||
583 | |||
584 | // Determine the left + right extends of the textures (output-buffer local) | ||
585 | int ob_left_x, ob_left_width, ob_right_x, ob_right_width; | ||
586 | if (ob_title_width == 0 && ob_marks_width == 0) { | ||
587 | ob_left_x = ob_inner_x; | ||
588 | ob_left_width = 0; | ||
589 | ob_right_x = ob_inner_x; | ||
590 | ob_right_width = 0; | ||
591 | } else if (ob_title_x < ob_marks_x) { | ||
592 | ob_left_x = ob_title_x; | ||
593 | ob_left_width = ob_title_width; | ||
594 | ob_right_x = ob_marks_x; | ||
595 | ob_right_width = ob_marks_width; | ||
596 | } else { | ||
597 | ob_left_x = ob_marks_x; | ||
598 | ob_left_width = ob_marks_width; | ||
599 | ob_right_x = ob_title_x; | ||
600 | ob_right_width = ob_title_width; | ||
601 | } | ||
602 | if (ob_left_x < ob_inner_x) { | ||
603 | ob_left_x = ob_inner_x; | ||
604 | } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) { | ||
605 | ob_right_x = ob_left_x; | ||
606 | ob_right_width = ob_left_width; | ||
607 | } | ||
608 | |||
609 | // Filler between title and marks | ||
610 | box.width = ob_right_x - ob_left_x - ob_left_width; | ||
611 | if (box.width > 0) { | ||
612 | box.x = ob_left_x + ob_left_width + round(output_x * output_scale); | ||
613 | box.y = round(bg_y * output_scale); | ||
614 | box.height = ob_bg_height; | ||
615 | render_rect(output, output_damage, &box, color); | ||
616 | } | ||
617 | |||
618 | // Padding on left side | ||
619 | box.x = x + titlebar_border_thickness; | ||
620 | box.y = y + titlebar_border_thickness; | ||
621 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
622 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
623 | config->font_height; | ||
624 | scale_box(&box, output_scale); | ||
625 | int left_x = ob_left_x + round(output_x * output_scale); | ||
626 | if (box.x + box.width < left_x) { | ||
627 | box.width += left_x - box.x - box.width; | ||
628 | } | ||
629 | render_rect(output, output_damage, &box, color); | ||
630 | |||
631 | // Padding on right side | ||
632 | box.x = x + width - titlebar_h_padding; | ||
633 | box.y = y + titlebar_border_thickness; | ||
634 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
635 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
636 | config->font_height; | ||
637 | scale_box(&box, output_scale); | ||
638 | int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale); | ||
639 | if (right_rx < box.x) { | ||
640 | box.width += box.x - right_rx; | ||
641 | box.x = right_rx; | ||
642 | } | ||
643 | render_rect(output, output_damage, &box, color); | ||
644 | } | ||
645 | |||
646 | /** | ||
647 | * Render the top border line for a view using "border pixel". | ||
648 | */ | ||
649 | static void render_top_border(struct sway_output *output, | ||
650 | pixman_region32_t *output_damage, struct sway_container *con, | ||
651 | struct border_colors *colors) { | ||
652 | struct sway_container_state *state = &con->current; | ||
653 | if (!state->border_top) { | ||
654 | return; | ||
655 | } | ||
656 | struct wlr_box box; | ||
657 | float color[4]; | ||
658 | float output_scale = output->wlr_output->scale; | ||
659 | |||
660 | // Child border - top edge | ||
661 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
662 | premultiply_alpha(color, con->alpha); | ||
663 | box.x = state->x; | ||
664 | box.y = state->y; | ||
665 | box.width = state->width; | ||
666 | box.height = state->border_thickness; | ||
667 | scale_box(&box, output_scale); | ||
668 | render_rect(output, output_damage, &box, color); | ||
669 | } | ||
670 | |||
671 | struct parent_data { | ||
672 | enum sway_container_layout layout; | ||
673 | struct wlr_box box; | ||
674 | list_t *children; | ||
675 | bool focused; | ||
676 | struct sway_container *active_child; | ||
677 | }; | ||
678 | |||
679 | static void render_container(struct sway_output *output, | ||
680 | pixman_region32_t *damage, struct sway_container *con, bool parent_focused); | ||
681 | |||
682 | /** | ||
683 | * Render a container's children using a L_HORIZ or L_VERT layout. | ||
684 | * | ||
685 | * Wrap child views in borders and leave child containers borderless because | ||
686 | * they'll apply their own borders to their children. | ||
687 | */ | ||
688 | static void render_containers_linear(struct sway_output *output, | ||
689 | pixman_region32_t *damage, struct parent_data *parent) { | ||
690 | for (int i = 0; i < parent->children->length; ++i) { | ||
691 | struct sway_container *child = parent->children->items[i]; | ||
692 | |||
693 | if (child->view) { | ||
694 | struct sway_view *view = child->view; | ||
695 | struct border_colors *colors; | ||
696 | struct wlr_texture *title_texture; | ||
697 | struct wlr_texture *marks_texture; | ||
698 | struct sway_container_state *state = &child->current; | ||
699 | |||
700 | if (view_is_urgent(view)) { | ||
701 | colors = &config->border_colors.urgent; | ||
702 | title_texture = child->title_urgent; | ||
703 | marks_texture = child->marks_urgent; | ||
704 | } else if (state->focused || parent->focused) { | ||
705 | colors = &config->border_colors.focused; | ||
706 | title_texture = child->title_focused; | ||
707 | marks_texture = child->marks_focused; | ||
708 | } else if (child == parent->active_child) { | ||
709 | colors = &config->border_colors.focused_inactive; | ||
710 | title_texture = child->title_focused_inactive; | ||
711 | marks_texture = child->marks_focused_inactive; | ||
712 | } else { | ||
713 | colors = &config->border_colors.unfocused; | ||
714 | title_texture = child->title_unfocused; | ||
715 | marks_texture = child->marks_unfocused; | ||
716 | } | ||
717 | |||
718 | if (state->border == B_NORMAL) { | ||
719 | render_titlebar(output, damage, child, state->x, | ||
720 | state->y, state->width, colors, | ||
721 | title_texture, marks_texture); | ||
722 | } else if (state->border == B_PIXEL) { | ||
723 | render_top_border(output, damage, child, colors); | ||
724 | } | ||
725 | render_view(output, damage, child, colors); | ||
726 | } else { | ||
727 | render_container(output, damage, child, | ||
728 | parent->focused || child->current.focused); | ||
729 | } | ||
730 | } | ||
731 | } | ||
732 | |||
733 | /** | ||
734 | * Render a container's children using the L_TABBED layout. | ||
735 | */ | ||
736 | static void render_containers_tabbed(struct sway_output *output, | ||
737 | pixman_region32_t *damage, struct parent_data *parent) { | ||
738 | if (!parent->children->length) { | ||
739 | return; | ||
740 | } | ||
741 | struct sway_container *current = parent->active_child; | ||
742 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
743 | int tab_width = parent->box.width / parent->children->length; | ||
744 | |||
745 | // Render tabs | ||
746 | for (int i = 0; i < parent->children->length; ++i) { | ||
747 | struct sway_container *child = parent->children->items[i]; | ||
748 | struct sway_view *view = child->view; | ||
749 | struct sway_container_state *cstate = &child->current; | ||
750 | struct border_colors *colors; | ||
751 | struct wlr_texture *title_texture; | ||
752 | struct wlr_texture *marks_texture; | ||
753 | bool urgent = view ? | ||
754 | view_is_urgent(view) : container_has_urgent_child(child); | ||
755 | |||
756 | if (urgent) { | ||
757 | colors = &config->border_colors.urgent; | ||
758 | title_texture = child->title_urgent; | ||
759 | marks_texture = child->marks_urgent; | ||
760 | } else if (cstate->focused || parent->focused) { | ||
761 | colors = &config->border_colors.focused; | ||
762 | title_texture = child->title_focused; | ||
763 | marks_texture = child->marks_focused; | ||
764 | } else if (child == parent->active_child) { | ||
765 | colors = &config->border_colors.focused_inactive; | ||
766 | title_texture = child->title_focused_inactive; | ||
767 | marks_texture = child->marks_focused_inactive; | ||
768 | } else { | ||
769 | colors = &config->border_colors.unfocused; | ||
770 | title_texture = child->title_unfocused; | ||
771 | marks_texture = child->marks_unfocused; | ||
772 | } | ||
773 | |||
774 | int x = cstate->x + tab_width * i; | ||
775 | |||
776 | // Make last tab use the remaining width of the parent | ||
777 | if (i == parent->children->length - 1) { | ||
778 | tab_width = parent->box.width - tab_width * i; | ||
779 | } | ||
780 | |||
781 | render_titlebar(output, damage, child, x, parent->box.y, tab_width, | ||
782 | colors, title_texture, marks_texture); | ||
783 | |||
784 | if (child == current) { | ||
785 | current_colors = colors; | ||
786 | } | ||
787 | } | ||
788 | |||
789 | // Render surface and left/right/bottom borders | ||
790 | if (current->view) { | ||
791 | render_view(output, damage, current, current_colors); | ||
792 | } else { | ||
793 | render_container(output, damage, current, | ||
794 | parent->focused || current->current.focused); | ||
795 | } | ||
796 | } | ||
797 | |||
798 | /** | ||
799 | * Render a container's children using the L_STACKED layout. | ||
800 | */ | ||
801 | static void render_containers_stacked(struct sway_output *output, | ||
802 | pixman_region32_t *damage, struct parent_data *parent) { | ||
803 | if (!parent->children->length) { | ||
804 | return; | ||
805 | } | ||
806 | struct sway_container *current = parent->active_child; | ||
807 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
808 | size_t titlebar_height = container_titlebar_height(); | ||
809 | |||
810 | // Render titles | ||
811 | for (int i = 0; i < parent->children->length; ++i) { | ||
812 | struct sway_container *child = parent->children->items[i]; | ||
813 | struct sway_view *view = child->view; | ||
814 | struct sway_container_state *cstate = &child->current; | ||
815 | struct border_colors *colors; | ||
816 | struct wlr_texture *title_texture; | ||
817 | struct wlr_texture *marks_texture; | ||
818 | bool urgent = view ? | ||
819 | view_is_urgent(view) : container_has_urgent_child(child); | ||
820 | |||
821 | if (urgent) { | ||
822 | colors = &config->border_colors.urgent; | ||
823 | title_texture = child->title_urgent; | ||
824 | marks_texture = child->marks_urgent; | ||
825 | } else if (cstate->focused || parent->focused) { | ||
826 | colors = &config->border_colors.focused; | ||
827 | title_texture = child->title_focused; | ||
828 | marks_texture = child->marks_focused; | ||
829 | } else if (child == parent->active_child) { | ||
830 | colors = &config->border_colors.focused_inactive; | ||
831 | title_texture = child->title_focused_inactive; | ||
832 | marks_texture = child->marks_focused_inactive; | ||
833 | } else { | ||
834 | colors = &config->border_colors.unfocused; | ||
835 | title_texture = child->title_unfocused; | ||
836 | marks_texture = child->marks_unfocused; | ||
837 | } | ||
838 | |||
839 | int y = parent->box.y + titlebar_height * i; | ||
840 | render_titlebar(output, damage, child, parent->box.x, y, | ||
841 | parent->box.width, colors, title_texture, marks_texture); | ||
842 | |||
843 | if (child == current) { | ||
844 | current_colors = colors; | ||
845 | } | ||
846 | } | ||
847 | |||
848 | // Render surface and left/right/bottom borders | ||
849 | if (current->view) { | ||
850 | render_view(output, damage, current, current_colors); | ||
851 | } else { | ||
852 | render_container(output, damage, current, | ||
853 | parent->focused || current->current.focused); | ||
854 | } | ||
855 | } | ||
856 | |||
857 | static void render_containers(struct sway_output *output, | ||
858 | pixman_region32_t *damage, struct parent_data *parent) { | ||
859 | if (config->hide_lone_tab && parent->children->length == 1) { | ||
860 | struct sway_container *child = parent->children->items[0]; | ||
861 | if (child->view) { | ||
862 | render_containers_linear(output,damage, parent); | ||
863 | return; | ||
864 | } | ||
865 | } | ||
866 | |||
867 | switch (parent->layout) { | ||
868 | case L_NONE: | ||
869 | case L_HORIZ: | ||
870 | case L_VERT: | ||
871 | render_containers_linear(output, damage, parent); | ||
872 | break; | ||
873 | case L_STACKED: | ||
874 | render_containers_stacked(output, damage, parent); | ||
875 | break; | ||
876 | case L_TABBED: | ||
877 | render_containers_tabbed(output, damage, parent); | ||
878 | break; | ||
879 | } | ||
880 | } | ||
881 | |||
882 | static void render_container(struct sway_output *output, | ||
883 | pixman_region32_t *damage, struct sway_container *con, bool focused) { | ||
884 | struct parent_data data = { | ||
885 | .layout = con->current.layout, | ||
886 | .box = { | ||
887 | .x = con->current.x, | ||
888 | .y = con->current.y, | ||
889 | .width = con->current.width, | ||
890 | .height = con->current.height, | ||
891 | }, | ||
892 | .children = con->current.children, | ||
893 | .focused = focused, | ||
894 | .active_child = con->current.focused_inactive_child, | ||
895 | }; | ||
896 | render_containers(output, damage, &data); | ||
897 | } | ||
898 | |||
899 | static void render_workspace(struct sway_output *output, | ||
900 | pixman_region32_t *damage, struct sway_workspace *ws, bool focused) { | ||
901 | struct parent_data data = { | ||
902 | .layout = ws->current.layout, | ||
903 | .box = { | ||
904 | .x = ws->current.x, | ||
905 | .y = ws->current.y, | ||
906 | .width = ws->current.width, | ||
907 | .height = ws->current.height, | ||
908 | }, | ||
909 | .children = ws->current.tiling, | ||
910 | .focused = focused, | ||
911 | .active_child = ws->current.focused_inactive_child, | ||
912 | }; | ||
913 | render_containers(output, damage, &data); | ||
914 | } | ||
915 | |||
916 | static void render_floating_container(struct sway_output *soutput, | ||
917 | pixman_region32_t *damage, struct sway_container *con) { | ||
918 | if (con->view) { | ||
919 | struct sway_view *view = con->view; | ||
920 | struct border_colors *colors; | ||
921 | struct wlr_texture *title_texture; | ||
922 | struct wlr_texture *marks_texture; | ||
923 | |||
924 | if (view_is_urgent(view)) { | ||
925 | colors = &config->border_colors.urgent; | ||
926 | title_texture = con->title_urgent; | ||
927 | marks_texture = con->marks_urgent; | ||
928 | } else if (con->current.focused) { | ||
929 | colors = &config->border_colors.focused; | ||
930 | title_texture = con->title_focused; | ||
931 | marks_texture = con->marks_focused; | ||
932 | } else { | ||
933 | colors = &config->border_colors.unfocused; | ||
934 | title_texture = con->title_unfocused; | ||
935 | marks_texture = con->marks_unfocused; | ||
936 | } | ||
937 | |||
938 | if (con->current.border == B_NORMAL) { | ||
939 | render_titlebar(soutput, damage, con, con->current.x, | ||
940 | con->current.y, con->current.width, colors, | ||
941 | title_texture, marks_texture); | ||
942 | } else if (con->current.border == B_PIXEL) { | ||
943 | render_top_border(soutput, damage, con, colors); | ||
944 | } | ||
945 | render_view(soutput, damage, con, colors); | ||
946 | } else { | ||
947 | render_container(soutput, damage, con, con->current.focused); | ||
948 | } | ||
949 | } | ||
950 | |||
951 | static void render_floating(struct sway_output *soutput, | ||
952 | pixman_region32_t *damage) { | ||
953 | for (int i = 0; i < root->outputs->length; ++i) { | ||
954 | struct sway_output *output = root->outputs->items[i]; | ||
955 | for (int j = 0; j < output->current.workspaces->length; ++j) { | ||
956 | struct sway_workspace *ws = output->current.workspaces->items[j]; | ||
957 | if (!workspace_is_visible(ws)) { | ||
958 | continue; | ||
959 | } | ||
960 | for (int k = 0; k < ws->current.floating->length; ++k) { | ||
961 | struct sway_container *floater = ws->current.floating->items[k]; | ||
962 | if (floater->fullscreen_mode != FULLSCREEN_NONE) { | ||
963 | continue; | ||
964 | } | ||
965 | render_floating_container(soutput, damage, floater); | ||
966 | } | ||
967 | } | ||
968 | } | ||
969 | } | ||
970 | |||
971 | static void render_seatops(struct sway_output *output, | ||
972 | pixman_region32_t *damage) { | ||
973 | struct sway_seat *seat; | ||
974 | wl_list_for_each(seat, &server.input->seats, link) { | ||
975 | seatop_render(seat, output, damage); | ||
976 | } | ||
977 | } | ||
978 | |||
979 | void output_render(struct sway_output *output, struct timespec *when, | ||
980 | pixman_region32_t *damage) { | ||
981 | struct wlr_output *wlr_output = output->wlr_output; | ||
982 | |||
983 | struct wlr_renderer *renderer = | ||
984 | wlr_backend_get_renderer(wlr_output->backend); | ||
985 | if (!sway_assert(renderer != NULL, | ||
986 | "expected the output backend to have a renderer")) { | ||
987 | return; | ||
988 | } | ||
989 | |||
990 | struct sway_workspace *workspace = output->current.active_workspace; | ||
991 | if (workspace == NULL) { | ||
992 | return; | ||
993 | } | ||
994 | |||
995 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
996 | if (!fullscreen_con) { | ||
997 | fullscreen_con = workspace->current.fullscreen; | ||
998 | } | ||
999 | |||
1000 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
1001 | |||
1002 | if (!pixman_region32_not_empty(damage)) { | ||
1003 | // Output isn't damaged but needs buffer swap | ||
1004 | goto renderer_end; | ||
1005 | } | ||
1006 | |||
1007 | if (debug.damage == DAMAGE_HIGHLIGHT) { | ||
1008 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | ||
1009 | } else if (debug.damage == DAMAGE_RERENDER) { | ||
1010 | int width, height; | ||
1011 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1012 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
1013 | } | ||
1014 | |||
1015 | if (output_has_opaque_overlay_layer_surface(output)) { | ||
1016 | goto render_overlay; | ||
1017 | } | ||
1018 | |||
1019 | if (fullscreen_con) { | ||
1020 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
1021 | |||
1022 | int nrects; | ||
1023 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1024 | for (int i = 0; i < nrects; ++i) { | ||
1025 | scissor_output(wlr_output, &rects[i]); | ||
1026 | wlr_renderer_clear(renderer, clear_color); | ||
1027 | } | ||
1028 | |||
1029 | if (fullscreen_con->view) { | ||
1030 | if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { | ||
1031 | render_saved_view(fullscreen_con->view, output, damage, 1.0f); | ||
1032 | } else if (fullscreen_con->view->surface) { | ||
1033 | render_view_toplevels(fullscreen_con->view, | ||
1034 | output, damage, 1.0f); | ||
1035 | } | ||
1036 | } else { | ||
1037 | render_container(output, damage, fullscreen_con, | ||
1038 | fullscreen_con->current.focused); | ||
1039 | } | ||
1040 | |||
1041 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
1042 | struct sway_container *floater = | ||
1043 | workspace->current.floating->items[i]; | ||
1044 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
1045 | render_floating_container(output, damage, floater); | ||
1046 | } | ||
1047 | } | ||
1048 | #if HAVE_XWAYLAND | ||
1049 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1050 | #endif | ||
1051 | } else { | ||
1052 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | ||
1053 | |||
1054 | int nrects; | ||
1055 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1056 | for (int i = 0; i < nrects; ++i) { | ||
1057 | scissor_output(wlr_output, &rects[i]); | ||
1058 | wlr_renderer_clear(renderer, clear_color); | ||
1059 | } | ||
1060 | |||
1061 | render_layer_toplevel(output, damage, | ||
1062 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1063 | render_layer_toplevel(output, damage, | ||
1064 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1065 | |||
1066 | render_workspace(output, damage, workspace, workspace->current.focused); | ||
1067 | render_floating(output, damage); | ||
1068 | #if HAVE_XWAYLAND | ||
1069 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1070 | #endif | ||
1071 | render_layer_toplevel(output, damage, | ||
1072 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1073 | |||
1074 | render_layer_popups(output, damage, | ||
1075 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1076 | render_layer_popups(output, damage, | ||
1077 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1078 | render_layer_popups(output, damage, | ||
1079 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1080 | } | ||
1081 | |||
1082 | render_seatops(output, damage); | ||
1083 | |||
1084 | struct sway_seat *seat = input_manager_current_seat(); | ||
1085 | struct sway_container *focus = seat_get_focused_container(seat); | ||
1086 | if (focus && focus->view) { | ||
1087 | render_view_popups(focus->view, output, damage, focus->alpha); | ||
1088 | } | ||
1089 | |||
1090 | render_overlay: | ||
1091 | render_layer_toplevel(output, damage, | ||
1092 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1093 | render_layer_popups(output, damage, | ||
1094 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1095 | render_drag_icons(output, damage, &root->drag_icons); | ||
1096 | |||
1097 | renderer_end: | ||
1098 | wlr_renderer_scissor(renderer, NULL); | ||
1099 | wlr_output_render_software_cursors(wlr_output, damage); | ||
1100 | wlr_renderer_end(renderer); | ||
1101 | |||
1102 | int width, height; | ||
1103 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1104 | |||
1105 | pixman_region32_t frame_damage; | ||
1106 | pixman_region32_init(&frame_damage); | ||
1107 | |||
1108 | enum wl_output_transform transform = | ||
1109 | wlr_output_transform_invert(wlr_output->transform); | ||
1110 | wlr_region_transform(&frame_damage, &output->damage->current, | ||
1111 | transform, width, height); | ||
1112 | |||
1113 | if (debug.damage == DAMAGE_HIGHLIGHT) { | ||
1114 | pixman_region32_union_rect(&frame_damage, &frame_damage, | ||
1115 | 0, 0, wlr_output->width, wlr_output->height); | ||
1116 | } | ||
1117 | |||
1118 | wlr_output_set_damage(wlr_output, &frame_damage); | ||
1119 | pixman_region32_fini(&frame_damage); | ||
1120 | |||
1121 | if (!wlr_output_commit(wlr_output)) { | ||
1122 | return; | ||
1123 | } | ||
1124 | output->last_frame = *when; | ||
1125 | } | ||
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c deleted file mode 100644 index 767b2045..00000000 --- a/sway/desktop/surface.c +++ /dev/null | |||
@@ -1,46 +0,0 @@ | |||
1 | #define _POSIX_C_SOURCE 200112L | ||
2 | #include <stdlib.h> | ||
3 | #include <time.h> | ||
4 | #include <wlr/types/wlr_surface.h> | ||
5 | #include "sway/server.h" | ||
6 | #include "sway/surface.h" | ||
7 | |||
8 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
9 | struct sway_surface *surface = wl_container_of(listener, surface, destroy); | ||
10 | |||
11 | surface->wlr_surface->data = NULL; | ||
12 | wl_list_remove(&surface->destroy.link); | ||
13 | |||
14 | if (surface->frame_done_timer) { | ||
15 | wl_event_source_remove(surface->frame_done_timer); | ||
16 | } | ||
17 | |||
18 | free(surface); | ||
19 | } | ||
20 | |||
21 | static int surface_frame_done_timer_handler(void *data) { | ||
22 | struct sway_surface *surface = data; | ||
23 | |||
24 | struct timespec now; | ||
25 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
26 | wlr_surface_send_frame_done(surface->wlr_surface, &now); | ||
27 | |||
28 | return 0; | ||
29 | } | ||
30 | |||
31 | void handle_compositor_new_surface(struct wl_listener *listener, void *data) { | ||
32 | struct wlr_surface *wlr_surface = data; | ||
33 | |||
34 | struct sway_surface *surface = calloc(1, sizeof(struct sway_surface)); | ||
35 | surface->wlr_surface = wlr_surface; | ||
36 | wlr_surface->data = surface; | ||
37 | |||
38 | surface->destroy.notify = handle_destroy; | ||
39 | wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); | ||
40 | |||
41 | surface->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, | ||
42 | surface_frame_done_timer_handler, surface); | ||
43 | if (!surface->frame_done_timer) { | ||
44 | wl_resource_post_no_memory(wlr_surface->resource); | ||
45 | } | ||
46 | } | ||
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index eac38991..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" |
@@ -35,6 +34,8 @@ struct sway_transaction_instruction { | |||
35 | struct sway_container_state container_state; | 34 | struct sway_container_state container_state; |
36 | }; | 35 | }; |
37 | uint32_t serial; | 36 | uint32_t serial; |
37 | bool server_request; | ||
38 | bool waiting; | ||
38 | }; | 39 | }; |
39 | 40 | ||
40 | static struct sway_transaction *transaction_create(void) { | 41 | static struct sway_transaction *transaction_create(void) { |
@@ -86,7 +87,11 @@ static void transaction_destroy(struct sway_transaction *transaction) { | |||
86 | static void copy_output_state(struct sway_output *output, | 87 | static void copy_output_state(struct sway_output *output, |
87 | struct sway_transaction_instruction *instruction) { | 88 | struct sway_transaction_instruction *instruction) { |
88 | struct sway_output_state *state = &instruction->output_state; | 89 | struct sway_output_state *state = &instruction->output_state; |
89 | state->workspaces = create_list(); | 90 | if (state->workspaces) { |
91 | state->workspaces->length = 0; | ||
92 | } else { | ||
93 | state->workspaces = create_list(); | ||
94 | } | ||
90 | list_cat(state->workspaces, output->workspaces); | 95 | list_cat(state->workspaces, output->workspaces); |
91 | 96 | ||
92 | state->active_workspace = output_get_active_workspace(output); | 97 | state->active_workspace = output_get_active_workspace(output); |
@@ -104,8 +109,16 @@ static void copy_workspace_state(struct sway_workspace *ws, | |||
104 | state->layout = ws->layout; | 109 | state->layout = ws->layout; |
105 | 110 | ||
106 | state->output = ws->output; | 111 | state->output = ws->output; |
107 | state->floating = create_list(); | 112 | if (state->floating) { |
108 | state->tiling = create_list(); | 113 | state->floating->length = 0; |
114 | } else { | ||
115 | state->floating = create_list(); | ||
116 | } | ||
117 | if (state->tiling) { | ||
118 | state->tiling->length = 0; | ||
119 | } else { | ||
120 | state->tiling = create_list(); | ||
121 | } | ||
109 | list_cat(state->floating, ws->floating); | 122 | list_cat(state->floating, ws->floating); |
110 | list_cat(state->tiling, ws->tiling); | 123 | list_cat(state->tiling, ws->tiling); |
111 | 124 | ||
@@ -115,8 +128,8 @@ static void copy_workspace_state(struct sway_workspace *ws, | |||
115 | // Set focused_inactive_child to the direct tiling child | 128 | // Set focused_inactive_child to the direct tiling child |
116 | struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); | 129 | struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); |
117 | if (focus) { | 130 | if (focus) { |
118 | while (focus->parent) { | 131 | while (focus->pending.parent) { |
119 | focus = focus->parent; | 132 | focus = focus->pending.parent; |
120 | } | 133 | } |
121 | } | 134 | } |
122 | state->focused_inactive_child = focus; | 135 | state->focused_inactive_child = focus; |
@@ -126,28 +139,19 @@ static void copy_container_state(struct sway_container *container, | |||
126 | struct sway_transaction_instruction *instruction) { | 139 | struct sway_transaction_instruction *instruction) { |
127 | struct sway_container_state *state = &instruction->container_state; | 140 | struct sway_container_state *state = &instruction->container_state; |
128 | 141 | ||
129 | state->layout = container->layout; | 142 | if (state->children) { |
130 | state->x = container->x; | 143 | list_free(state->children); |
131 | state->y = container->y; | 144 | } |
132 | state->width = container->width; | 145 | |
133 | state->height = container->height; | 146 | memcpy(state, &container->pending, sizeof(struct sway_container_state)); |
134 | state->fullscreen_mode = container->fullscreen_mode; | ||
135 | state->parent = container->parent; | ||
136 | state->workspace = container->workspace; | ||
137 | state->border = container->border; | ||
138 | state->border_thickness = container->border_thickness; | ||
139 | state->border_top = container->border_top; | ||
140 | state->border_left = container->border_left; | ||
141 | state->border_right = container->border_right; | ||
142 | state->border_bottom = container->border_bottom; | ||
143 | state->content_x = container->content_x; | ||
144 | state->content_y = container->content_y; | ||
145 | state->content_width = container->content_width; | ||
146 | state->content_height = container->content_height; | ||
147 | 147 | ||
148 | if (!container->view) { | 148 | if (!container->view) { |
149 | // We store a copy of the child list to avoid having it mutated after | ||
150 | // we copy the state. | ||
149 | state->children = create_list(); | 151 | state->children = create_list(); |
150 | list_cat(state->children, container->children); | 152 | list_cat(state->children, container->pending.children); |
153 | } else { | ||
154 | state->children = NULL; | ||
151 | } | 155 | } |
152 | 156 | ||
153 | struct sway_seat *seat = input_manager_current_seat(); | 157 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -161,14 +165,36 @@ static void copy_container_state(struct sway_container *container, | |||
161 | } | 165 | } |
162 | 166 | ||
163 | static void transaction_add_node(struct sway_transaction *transaction, | 167 | static void transaction_add_node(struct sway_transaction *transaction, |
164 | struct sway_node *node) { | 168 | struct sway_node *node, bool server_request) { |
165 | struct sway_transaction_instruction *instruction = | 169 | struct sway_transaction_instruction *instruction = NULL; |
166 | calloc(1, sizeof(struct sway_transaction_instruction)); | 170 | |
167 | if (!sway_assert(instruction, "Unable to allocate instruction")) { | 171 | // Check if we have an instruction for this node already, in which case we |
168 | return; | 172 | // update that instead of creating a new one. |
173 | if (node->ntxnrefs > 0) { | ||
174 | for (int idx = 0; idx < transaction->instructions->length; idx++) { | ||
175 | struct sway_transaction_instruction *other = | ||
176 | transaction->instructions->items[idx]; | ||
177 | if (other->node == node) { | ||
178 | instruction = other; | ||
179 | break; | ||
180 | } | ||
181 | } | ||
182 | } | ||
183 | |||
184 | if (!instruction) { | ||
185 | instruction = calloc(1, sizeof(struct sway_transaction_instruction)); | ||
186 | if (!sway_assert(instruction, "Unable to allocate instruction")) { | ||
187 | return; | ||
188 | } | ||
189 | instruction->transaction = transaction; | ||
190 | instruction->node = node; | ||
191 | instruction->server_request = server_request; | ||
192 | |||
193 | list_add(transaction->instructions, instruction); | ||
194 | node->ntxnrefs++; | ||
195 | } else if (server_request) { | ||
196 | instruction->server_request = true; | ||
169 | } | 197 | } |
170 | instruction->transaction = transaction; | ||
171 | instruction->node = node; | ||
172 | 198 | ||
173 | switch (node->type) { | 199 | switch (node->type) { |
174 | case N_ROOT: | 200 | case N_ROOT: |
@@ -183,46 +209,24 @@ static void transaction_add_node(struct sway_transaction *transaction, | |||
183 | copy_container_state(node->sway_container, instruction); | 209 | copy_container_state(node->sway_container, instruction); |
184 | break; | 210 | break; |
185 | } | 211 | } |
186 | |||
187 | list_add(transaction->instructions, instruction); | ||
188 | node->ntxnrefs++; | ||
189 | } | 212 | } |
190 | 213 | ||
191 | static void apply_output_state(struct sway_output *output, | 214 | static void apply_output_state(struct sway_output *output, |
192 | struct sway_output_state *state) { | 215 | struct sway_output_state *state) { |
193 | output_damage_whole(output); | ||
194 | list_free(output->current.workspaces); | 216 | list_free(output->current.workspaces); |
195 | memcpy(&output->current, state, sizeof(struct sway_output_state)); | 217 | memcpy(&output->current, state, sizeof(struct sway_output_state)); |
196 | output_damage_whole(output); | ||
197 | } | 218 | } |
198 | 219 | ||
199 | static void apply_workspace_state(struct sway_workspace *ws, | 220 | static void apply_workspace_state(struct sway_workspace *ws, |
200 | struct sway_workspace_state *state) { | 221 | struct sway_workspace_state *state) { |
201 | output_damage_whole(ws->current.output); | ||
202 | list_free(ws->current.floating); | 222 | list_free(ws->current.floating); |
203 | list_free(ws->current.tiling); | 223 | list_free(ws->current.tiling); |
204 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); | 224 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); |
205 | output_damage_whole(ws->current.output); | ||
206 | } | 225 | } |
207 | 226 | ||
208 | static void apply_container_state(struct sway_container *container, | 227 | static void apply_container_state(struct sway_container *container, |
209 | struct sway_container_state *state) { | 228 | struct sway_container_state *state) { |
210 | struct sway_view *view = container->view; | 229 | struct sway_view *view = container->view; |
211 | // Damage the old location | ||
212 | desktop_damage_whole_container(container); | ||
213 | if (view && !wl_list_empty(&view->saved_buffers)) { | ||
214 | struct sway_saved_buffer *saved_buf; | ||
215 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
216 | struct wlr_box box = { | ||
217 | .x = container->current.content_x - view->saved_geometry.x + saved_buf->x, | ||
218 | .y = container->current.content_y - view->saved_geometry.y + saved_buf->y, | ||
219 | .width = saved_buf->width, | ||
220 | .height = saved_buf->height, | ||
221 | }; | ||
222 | desktop_damage_box(&box); | ||
223 | } | ||
224 | } | ||
225 | |||
226 | // There are separate children lists for each instruction state, the | 230 | // There are separate children lists for each instruction state, the |
227 | // container's current state and the container's pending state | 231 | // container's current state and the container's pending state |
228 | // (ie. con->children). The list itself needs to be freed here. | 232 | // (ie. con->children). The list itself needs to be freed here. |
@@ -232,48 +236,445 @@ static void apply_container_state(struct sway_container *container, | |||
232 | 236 | ||
233 | memcpy(&container->current, state, sizeof(struct sway_container_state)); | 237 | memcpy(&container->current, state, sizeof(struct sway_container_state)); |
234 | 238 | ||
235 | if (view && !wl_list_empty(&view->saved_buffers)) { | 239 | if (view) { |
236 | if (!container->node.destroying || container->node.ntxnrefs == 1) { | 240 | if (view->saved_surface_tree) { |
237 | 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; | ||
238 | } | 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); | ||
239 | } | 389 | } |
240 | 390 | ||
241 | // Damage the new location | 391 | if (con->view) { |
242 | desktop_damage_whole_container(container); | 392 | int border_top = container_titlebar_height(); |
243 | if (view && view->surface) { | 393 | int border_width = con->current.border_thickness; |
244 | struct wlr_surface *surface = view->surface; | 394 | |
245 | struct wlr_box box = { | 395 | if (title_bar && con->current.border != B_NORMAL) { |
246 | .x = container->current.content_x - view->geometry.x, | 396 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); |
247 | .y = container->current.content_y - view->geometry.y, | 397 | wlr_scene_node_set_enabled(&con->border.top->node, true); |
248 | .width = surface->current.width, | ||
249 | .height = surface->current.height, | ||
250 | }; | ||
251 | desktop_damage_box(&box); | ||
252 | } | ||
253 | |||
254 | // If the view hasn't responded to the configure, center it within | ||
255 | // the container. This is important for fullscreen views which | ||
256 | // refuse to resize to the size of the output. | ||
257 | if (view && view->surface) { | ||
258 | if (view->geometry.width < container->current.content_width) { | ||
259 | container->surface_x = container->current.content_x + | ||
260 | (container->current.content_width - view->geometry.width) / 2; | ||
261 | } else { | 398 | } else { |
262 | container->surface_x = container->current.content_x; | 399 | wlr_scene_node_set_enabled(&con->border.top->node, false); |
263 | } | 400 | } |
264 | if (view->geometry.height < container->current.content_height) { | 401 | |
265 | container->surface_y = container->current.content_y + | 402 | if (con->current.border == B_NORMAL) { |
266 | (container->current.content_height - view->geometry.height) / 2; | 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; | ||
267 | } else { | 466 | } else { |
268 | container->surface_y = container->current.content_y; | 467 | layout = ws->current.layout; |
269 | } | 468 | } |
469 | if (layout == L_TABBED || layout == L_STACKED) { | ||
470 | return 0; | ||
471 | } | ||
472 | temp = temp->pending.parent; | ||
270 | } | 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; | ||
271 | 500 | ||
272 | if (!container->node.destroying) { | 501 | if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { |
273 | container_discover_outputs(container); | 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); | ||
274 | } | 528 | } |
275 | } | 529 | } |
276 | 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); | ||
604 | } | ||
605 | } | ||
606 | } | ||
607 | |||
608 | void arrange_popups(struct wlr_scene_tree *popups) { | ||
609 | struct wlr_scene_node *node; | ||
610 | wl_list_for_each(node, &popups->children, link) { | ||
611 | struct sway_popup_desc *popup = scene_descriptor_try_get(node, | ||
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); | ||
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); | ||
629 | |||
630 | // hide all contents in the scratchpad | ||
631 | for (int i = 0; i < root->scratchpad->length; i++) { | ||
632 | struct sway_container *con = root->scratchpad->items[i]; | ||
633 | |||
634 | wlr_scene_node_set_enabled(&con->scene_tree->node, false); | ||
635 | } | ||
636 | |||
637 | if (fs) { | ||
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 | } | ||
673 | } | ||
674 | |||
675 | arrange_popups(root->layers.popup); | ||
676 | } | ||
677 | |||
277 | /** | 678 | /** |
278 | * Apply a transaction to the "current" state of the tree. | 679 | * Apply a transaction to the "current" state of the tree. |
279 | */ | 680 | */ |
@@ -313,74 +714,29 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
313 | 714 | ||
314 | node->instruction = NULL; | 715 | node->instruction = NULL; |
315 | } | 716 | } |
316 | |||
317 | cursor_rebase_all(); | ||
318 | } | 717 | } |
319 | 718 | ||
320 | static void transaction_commit(struct sway_transaction *transaction); | 719 | static void transaction_commit_pending(void); |
321 | 720 | ||
322 | // Return true if both transactions operate on the same nodes | 721 | static void transaction_progress(void) { |
323 | static bool transaction_same_nodes(struct sway_transaction *a, | 722 | if (!server.queued_transaction) { |
324 | struct sway_transaction *b) { | ||
325 | if (a->instructions->length != b->instructions->length) { | ||
326 | return false; | ||
327 | } | ||
328 | for (int i = 0; i < a->instructions->length; ++i) { | ||
329 | struct sway_transaction_instruction *a_inst = a->instructions->items[i]; | ||
330 | struct sway_transaction_instruction *b_inst = b->instructions->items[i]; | ||
331 | if (a_inst->node != b_inst->node) { | ||
332 | return false; | ||
333 | } | ||
334 | } | ||
335 | return true; | ||
336 | } | ||
337 | |||
338 | static void transaction_progress_queue(void) { | ||
339 | if (!server.transactions->length) { | ||
340 | return; | 723 | return; |
341 | } | 724 | } |
342 | // Only the first transaction in the queue is committed, so that's the one | 725 | if (server.queued_transaction->num_waiting > 0) { |
343 | // we try to process. | ||
344 | struct sway_transaction *transaction = server.transactions->items[0]; | ||
345 | if (transaction->num_waiting) { | ||
346 | return; | 726 | return; |
347 | } | 727 | } |
348 | transaction_apply(transaction); | 728 | transaction_apply(server.queued_transaction); |
349 | transaction_destroy(transaction); | 729 | arrange_root(root); |
350 | list_del(server.transactions, 0); | 730 | cursor_rebase_all(); |
731 | transaction_destroy(server.queued_transaction); | ||
732 | server.queued_transaction = NULL; | ||
351 | 733 | ||
352 | if (server.transactions->length == 0) { | 734 | if (!server.pending_transaction) { |
353 | // The transaction queue is empty, so we're done. | 735 | sway_idle_inhibit_v1_check_active(); |
354 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | ||
355 | return; | 736 | return; |
356 | } | 737 | } |
357 | 738 | ||
358 | // If there's a bunch of consecutive transactions which all apply to the | 739 | transaction_commit_pending(); |
359 | // same views, skip all except the last one. | ||
360 | while (server.transactions->length >= 2) { | ||
361 | struct sway_transaction *txn = server.transactions->items[0]; | ||
362 | struct sway_transaction *dup = NULL; | ||
363 | |||
364 | for (int i = 1; i < server.transactions->length; i++) { | ||
365 | struct sway_transaction *maybe_dup = server.transactions->items[i]; | ||
366 | if (transaction_same_nodes(txn, maybe_dup)) { | ||
367 | dup = maybe_dup; | ||
368 | break; | ||
369 | } | ||
370 | } | ||
371 | |||
372 | if (dup) { | ||
373 | list_del(server.transactions, 0); | ||
374 | transaction_destroy(txn); | ||
375 | } else { | ||
376 | break; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | // We again commit the first transaction in the queue to process it. | ||
381 | transaction = server.transactions->items[0]; | ||
382 | transaction_commit(transaction); | ||
383 | transaction_progress_queue(); | ||
384 | } | 740 | } |
385 | 741 | ||
386 | static int handle_timeout(void *data) { | 742 | static int handle_timeout(void *data) { |
@@ -388,7 +744,7 @@ static int handle_timeout(void *data) { | |||
388 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", | 744 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", |
389 | transaction, transaction->num_waiting); | 745 | transaction, transaction->num_waiting); |
390 | transaction->num_waiting = 0; | 746 | transaction->num_waiting = 0; |
391 | transaction_progress_queue(); | 747 | transaction_progress(); |
392 | return 0; | 748 | return 0; |
393 | } | 749 | } |
394 | 750 | ||
@@ -400,6 +756,9 @@ static bool should_configure(struct sway_node *node, | |||
400 | if (node->destroying) { | 756 | if (node->destroying) { |
401 | return false; | 757 | return false; |
402 | } | 758 | } |
759 | if (!instruction->server_request) { | ||
760 | return false; | ||
761 | } | ||
403 | struct sway_container_state *cstate = &node->sway_container->current; | 762 | struct sway_container_state *cstate = &node->sway_container->current; |
404 | struct sway_container_state *istate = &instruction->container_state; | 763 | struct sway_container_state *istate = &instruction->container_state; |
405 | #if HAVE_XWAYLAND | 764 | #if HAVE_XWAYLAND |
@@ -431,28 +790,24 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
431 | struct sway_transaction_instruction *instruction = | 790 | struct sway_transaction_instruction *instruction = |
432 | transaction->instructions->items[i]; | 791 | transaction->instructions->items[i]; |
433 | struct sway_node *node = instruction->node; | 792 | struct sway_node *node = instruction->node; |
793 | bool hidden = node_is_view(node) && !node->destroying && | ||
794 | !view_is_visible(node->sway_container->view); | ||
434 | if (should_configure(node, instruction)) { | 795 | if (should_configure(node, instruction)) { |
435 | instruction->serial = view_configure(node->sway_container->view, | 796 | instruction->serial = view_configure(node->sway_container->view, |
436 | instruction->container_state.content_x, | 797 | instruction->container_state.content_x, |
437 | instruction->container_state.content_y, | 798 | instruction->container_state.content_y, |
438 | instruction->container_state.content_width, | 799 | instruction->container_state.content_width, |
439 | instruction->container_state.content_height); | 800 | instruction->container_state.content_height); |
440 | ++transaction->num_waiting; | 801 | if (!hidden) { |
441 | 802 | instruction->waiting = true; | |
442 | // From here on we are rendering a saved buffer of the view, which | 803 | ++transaction->num_waiting; |
443 | // means we can send a frame done event to make the client redraw it | 804 | } |
444 | // as soon as possible. Additionally, this is required if a view is | 805 | |
445 | // mapping and its default geometry doesn't intersect an output. | 806 | view_send_frame_done(node->sway_container->view); |
446 | struct timespec now; | ||
447 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
448 | wlr_surface_send_frame_done( | ||
449 | node->sway_container->view->surface, &now); | ||
450 | } | 807 | } |
451 | if (node_is_view(node) && wl_list_empty(&node->sway_container->view->saved_buffers)) { | 808 | if (!hidden && node_is_view(node) && |
809 | !node->sway_container->view->saved_surface_tree) { | ||
452 | view_save_buffer(node->sway_container->view); | 810 | view_save_buffer(node->sway_container->view); |
453 | memcpy(&node->sway_container->view->saved_geometry, | ||
454 | &node->sway_container->view->geometry, | ||
455 | sizeof(struct wlr_box)); | ||
456 | } | 811 | } |
457 | node->instruction = instruction; | 812 | node->instruction = instruction; |
458 | } | 813 | } |
@@ -483,6 +838,17 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
483 | } | 838 | } |
484 | } | 839 | } |
485 | 840 | ||
841 | static void transaction_commit_pending(void) { | ||
842 | if (server.queued_transaction) { | ||
843 | return; | ||
844 | } | ||
845 | struct sway_transaction *transaction = server.pending_transaction; | ||
846 | server.pending_transaction = NULL; | ||
847 | server.queued_transaction = transaction; | ||
848 | transaction_commit(transaction); | ||
849 | transaction_progress(); | ||
850 | } | ||
851 | |||
486 | static void set_instruction_ready( | 852 | static void set_instruction_ready( |
487 | struct sway_transaction_instruction *instruction) { | 853 | struct sway_transaction_instruction *instruction) { |
488 | struct sway_transaction *transaction = instruction->transaction; | 854 | struct sway_transaction *transaction = instruction->transaction; |
@@ -501,25 +867,28 @@ static void set_instruction_ready( | |||
501 | } | 867 | } |
502 | 868 | ||
503 | // If the transaction has timed out then its num_waiting will be 0 already. | 869 | // If the transaction has timed out then its num_waiting will be 0 already. |
504 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { | 870 | if (instruction->waiting && transaction->num_waiting > 0 && |
871 | --transaction->num_waiting == 0) { | ||
505 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); | 872 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); |
506 | wl_event_source_timer_update(transaction->timer, 0); | 873 | wl_event_source_timer_update(transaction->timer, 0); |
507 | } | 874 | } |
508 | 875 | ||
509 | instruction->node->instruction = NULL; | 876 | instruction->node->instruction = NULL; |
510 | transaction_progress_queue(); | 877 | transaction_progress(); |
511 | } | 878 | } |
512 | 879 | ||
513 | void transaction_notify_view_ready_by_serial(struct sway_view *view, | 880 | bool transaction_notify_view_ready_by_serial(struct sway_view *view, |
514 | uint32_t serial) { | 881 | uint32_t serial) { |
515 | struct sway_transaction_instruction *instruction = | 882 | struct sway_transaction_instruction *instruction = |
516 | view->container->node.instruction; | 883 | view->container->node.instruction; |
517 | if (instruction != NULL && instruction->serial == serial) { | 884 | if (instruction != NULL && instruction->serial == serial) { |
518 | set_instruction_ready(instruction); | 885 | set_instruction_ready(instruction); |
886 | return true; | ||
519 | } | 887 | } |
888 | return false; | ||
520 | } | 889 | } |
521 | 890 | ||
522 | void transaction_notify_view_ready_by_geometry(struct sway_view *view, | 891 | bool transaction_notify_view_ready_by_geometry(struct sway_view *view, |
523 | double x, double y, int width, int height) { | 892 | double x, double y, int width, int height) { |
524 | struct sway_transaction_instruction *instruction = | 893 | struct sway_transaction_instruction *instruction = |
525 | view->container->node.instruction; | 894 | view->container->node.instruction; |
@@ -529,39 +898,37 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view, | |||
529 | instruction->container_state.content_width == width && | 898 | instruction->container_state.content_width == width && |
530 | instruction->container_state.content_height == height) { | 899 | instruction->container_state.content_height == height) { |
531 | set_instruction_ready(instruction); | 900 | set_instruction_ready(instruction); |
901 | return true; | ||
532 | } | 902 | } |
903 | return false; | ||
533 | } | 904 | } |
534 | 905 | ||
535 | void transaction_notify_view_ready_immediately(struct sway_view *view) { | 906 | static void _transaction_commit_dirty(bool server_request) { |
536 | struct sway_transaction_instruction *instruction = | ||
537 | view->container->node.instruction; | ||
538 | if (instruction != NULL) { | ||
539 | set_instruction_ready(instruction); | ||
540 | } | ||
541 | } | ||
542 | |||
543 | void transaction_commit_dirty(void) { | ||
544 | if (!server.dirty_nodes->length) { | 907 | if (!server.dirty_nodes->length) { |
545 | return; | 908 | return; |
546 | } | 909 | } |
547 | struct sway_transaction *transaction = transaction_create(); | 910 | |
548 | if (!transaction) { | 911 | if (!server.pending_transaction) { |
549 | return; | 912 | server.pending_transaction = transaction_create(); |
913 | if (!server.pending_transaction) { | ||
914 | return; | ||
915 | } | ||
550 | } | 916 | } |
917 | |||
551 | for (int i = 0; i < server.dirty_nodes->length; ++i) { | 918 | for (int i = 0; i < server.dirty_nodes->length; ++i) { |
552 | struct sway_node *node = server.dirty_nodes->items[i]; | 919 | struct sway_node *node = server.dirty_nodes->items[i]; |
553 | transaction_add_node(transaction, node); | 920 | transaction_add_node(server.pending_transaction, node, server_request); |
554 | node->dirty = false; | 921 | node->dirty = false; |
555 | } | 922 | } |
556 | server.dirty_nodes->length = 0; | 923 | server.dirty_nodes->length = 0; |
557 | 924 | ||
558 | list_add(server.transactions, transaction); | 925 | transaction_commit_pending(); |
926 | } | ||
559 | 927 | ||
560 | // We only commit the first transaction added to the queue. | 928 | void transaction_commit_dirty(void) { |
561 | if (server.transactions->length == 1) { | 929 | _transaction_commit_dirty(true); |
562 | transaction_commit(transaction); | 930 | } |
563 | // Attempting to progress the queue here is useful | 931 | |
564 | // if the transaction has nothing to wait for. | 932 | void transaction_commit_dirty_client(void) { |
565 | transaction_progress_queue(); | 933 | _transaction_commit_dirty(false); |
566 | } | ||
567 | } | 934 | } |
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 667fb9e5..7c417891 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -7,7 +6,7 @@ | |||
7 | #include <wlr/util/edges.h> | 6 | #include <wlr/util/edges.h> |
8 | #include "log.h" | 7 | #include "log.h" |
9 | #include "sway/decoration.h" | 8 | #include "sway/decoration.h" |
10 | #include "sway/desktop.h" | 9 | #include "sway/scene_descriptor.h" |
11 | #include "sway/desktop/transaction.h" | 10 | #include "sway/desktop/transaction.h" |
12 | #include "sway/input/cursor.h" | 11 | #include "sway/input/cursor.h" |
13 | #include "sway/input/input-manager.h" | 12 | #include "sway/input/input-manager.h" |
@@ -19,64 +18,45 @@ | |||
19 | #include "sway/tree/workspace.h" | 18 | #include "sway/tree/workspace.h" |
20 | #include "sway/xdg_decoration.h" | 19 | #include "sway/xdg_decoration.h" |
21 | 20 | ||
22 | static const struct sway_view_child_impl popup_impl; | ||
23 | |||
24 | static void popup_get_root_coords(struct sway_view_child *child, | ||
25 | int *root_sx, int *root_sy) { | ||
26 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
27 | struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; | ||
28 | |||
29 | int x_offset = -child->view->geometry.x - surface->geometry.x; | ||
30 | int y_offset = -child->view->geometry.y - surface->geometry.y; | ||
31 | |||
32 | wlr_xdg_popup_get_toplevel_coords(surface->popup, | ||
33 | x_offset + surface->popup->geometry.x, | ||
34 | y_offset + surface->popup->geometry.y, | ||
35 | root_sx, root_sy); | ||
36 | } | ||
37 | |||
38 | static void popup_destroy(struct sway_view_child *child) { | ||
39 | if (!sway_assert(child->impl == &popup_impl, | ||
40 | "Expected an xdg_shell popup")) { | ||
41 | return; | ||
42 | } | ||
43 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
44 | wl_list_remove(&popup->new_popup.link); | ||
45 | wl_list_remove(&popup->destroy.link); | ||
46 | free(popup); | ||
47 | } | ||
48 | |||
49 | static const struct sway_view_child_impl popup_impl = { | ||
50 | .get_root_coords = popup_get_root_coords, | ||
51 | .destroy = popup_destroy, | ||
52 | }; | ||
53 | |||
54 | static struct sway_xdg_popup *popup_create( | 21 | static struct sway_xdg_popup *popup_create( |
55 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view); | 22 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view, |
23 | struct wlr_scene_tree *parent); | ||
56 | 24 | ||
57 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | 25 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { |
58 | struct sway_xdg_popup *popup = | 26 | struct sway_xdg_popup *popup = |
59 | wl_container_of(listener, popup, new_popup); | 27 | wl_container_of(listener, popup, new_popup); |
60 | struct wlr_xdg_popup *wlr_popup = data; | 28 | struct wlr_xdg_popup *wlr_popup = data; |
61 | popup_create(wlr_popup, popup->child.view); | 29 | popup_create(wlr_popup, popup->view, popup->xdg_surface_tree); |
62 | } | 30 | } |
63 | 31 | ||
64 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 32 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
65 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); | 33 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); |
66 | view_child_destroy(&popup->child); | 34 | |
35 | wl_list_remove(&popup->new_popup.link); | ||
36 | wl_list_remove(&popup->destroy.link); | ||
37 | wl_list_remove(&popup->surface_commit.link); | ||
38 | wl_list_remove(&popup->reposition.link); | ||
39 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
40 | free(popup); | ||
67 | } | 41 | } |
68 | 42 | ||
69 | static void popup_unconstrain(struct sway_xdg_popup *popup) { | 43 | static void popup_unconstrain(struct sway_xdg_popup *popup) { |
70 | struct sway_view *view = popup->child.view; | 44 | struct sway_view *view = popup->view; |
71 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; | 45 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; |
46 | |||
47 | struct sway_workspace *workspace = view->container->pending.workspace; | ||
48 | if (!workspace) { | ||
49 | // is null if in the scratchpad | ||
50 | return; | ||
51 | } | ||
72 | 52 | ||
73 | struct sway_output *output = view->container->workspace->output; | 53 | struct sway_output *output = workspace->output; |
74 | 54 | ||
75 | // the output box expressed in the coordinate system of the toplevel parent | 55 | // the output box expressed in the coordinate system of the toplevel parent |
76 | // of the popup | 56 | // of the popup |
77 | struct wlr_box output_toplevel_sx_box = { | 57 | struct wlr_box output_toplevel_sx_box = { |
78 | .x = output->lx - view->container->content_x, | 58 | .x = output->lx - view->container->pending.content_x + view->geometry.x, |
79 | .y = output->ly - view->container->content_y, | 59 | .y = output->ly - view->container->pending.content_y + view->geometry.y, |
80 | .width = output->width, | 60 | .width = output->width, |
81 | .height = output->height, | 61 | .height = output->height, |
82 | }; | 62 | }; |
@@ -84,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { | |||
84 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | 64 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); |
85 | } | 65 | } |
86 | 66 | ||
87 | static struct sway_xdg_popup *popup_create( | 67 | static void popup_handle_surface_commit(struct wl_listener *listener, void *data) { |
88 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { | 68 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit); |
69 | if (popup->wlr_xdg_popup->base->initial_commit) { | ||
70 | popup_unconstrain(popup); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | static void popup_handle_reposition(struct wl_listener *listener, void *data) { | ||
75 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, reposition); | ||
76 | popup_unconstrain(popup); | ||
77 | } | ||
78 | |||
79 | static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, | ||
80 | struct sway_view *view, struct wlr_scene_tree *parent) { | ||
89 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; | 81 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; |
90 | 82 | ||
91 | struct sway_xdg_popup *popup = | 83 | struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); |
92 | calloc(1, sizeof(struct sway_xdg_popup)); | 84 | if (!popup) { |
93 | if (popup == NULL) { | 85 | return NULL; |
86 | } | ||
87 | |||
88 | popup->wlr_xdg_popup = wlr_popup; | ||
89 | popup->view = view; | ||
90 | |||
91 | popup->scene_tree = wlr_scene_tree_create(parent); | ||
92 | if (!popup->scene_tree) { | ||
93 | free(popup); | ||
94 | return NULL; | ||
95 | } | ||
96 | |||
97 | popup->xdg_surface_tree = wlr_scene_xdg_surface_create( | ||
98 | popup->scene_tree, xdg_surface); | ||
99 | if (!popup->xdg_surface_tree) { | ||
100 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
101 | free(popup); | ||
94 | return NULL; | 102 | return NULL; |
95 | } | 103 | } |
96 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | ||
97 | popup->wlr_xdg_surface = xdg_surface; | ||
98 | 104 | ||
105 | popup->desc.relative = &view->content_tree->node; | ||
106 | popup->desc.view = view; | ||
107 | |||
108 | if (!scene_descriptor_assign(&popup->scene_tree->node, | ||
109 | SWAY_SCENE_DESC_POPUP, &popup->desc)) { | ||
110 | sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); | ||
111 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
112 | free(popup); | ||
113 | return NULL; | ||
114 | } | ||
115 | |||
116 | popup->wlr_xdg_popup = xdg_surface->popup; | ||
117 | struct sway_xdg_shell_view *shell_view = | ||
118 | wl_container_of(view, shell_view, view); | ||
119 | xdg_surface->data = shell_view; | ||
120 | |||
121 | wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit); | ||
122 | popup->surface_commit.notify = popup_handle_surface_commit; | ||
99 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 123 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
100 | popup->new_popup.notify = popup_handle_new_popup; | 124 | popup->new_popup.notify = popup_handle_new_popup; |
101 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); | 125 | wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); |
126 | popup->reposition.notify = popup_handle_reposition; | ||
127 | wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); | ||
102 | popup->destroy.notify = popup_handle_destroy; | 128 | popup->destroy.notify = popup_handle_destroy; |
103 | 129 | ||
104 | wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); | ||
105 | wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); | ||
106 | |||
107 | popup_unconstrain(popup); | ||
108 | |||
109 | return popup; | 130 | return popup; |
110 | } | 131 | } |
111 | 132 | ||
112 | |||
113 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( | 133 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( |
114 | struct sway_view *view) { | 134 | struct sway_view *view) { |
115 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, | 135 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, |
@@ -122,7 +142,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( | |||
122 | static void get_constraints(struct sway_view *view, double *min_width, | 142 | static void get_constraints(struct sway_view *view, double *min_width, |
123 | double *max_width, double *min_height, double *max_height) { | 143 | double *max_width, double *min_height, double *max_height) { |
124 | struct wlr_xdg_toplevel_state *state = | 144 | struct wlr_xdg_toplevel_state *state = |
125 | &view->wlr_xdg_surface->toplevel->current; | 145 | &view->wlr_xdg_toplevel->current; |
126 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | 146 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; |
127 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | 147 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; |
128 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | 148 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; |
@@ -136,9 +156,9 @@ static const char *get_string_prop(struct sway_view *view, | |||
136 | } | 156 | } |
137 | switch (prop) { | 157 | switch (prop) { |
138 | case VIEW_PROP_TITLE: | 158 | case VIEW_PROP_TITLE: |
139 | return view->wlr_xdg_surface->toplevel->title; | 159 | return view->wlr_xdg_toplevel->title; |
140 | case VIEW_PROP_APP_ID: | 160 | case VIEW_PROP_APP_ID: |
141 | return view->wlr_xdg_surface->toplevel->app_id; | 161 | return view->wlr_xdg_toplevel->app_id; |
142 | default: | 162 | default: |
143 | return NULL; | 163 | return NULL; |
144 | } | 164 | } |
@@ -151,50 +171,52 @@ static uint32_t configure(struct sway_view *view, double lx, double ly, | |||
151 | if (xdg_shell_view == NULL) { | 171 | if (xdg_shell_view == NULL) { |
152 | return 0; | 172 | return 0; |
153 | } | 173 | } |
154 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); | 174 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, |
175 | width, height); | ||
155 | } | 176 | } |
156 | 177 | ||
157 | static void set_activated(struct sway_view *view, bool activated) { | 178 | static void set_activated(struct sway_view *view, bool activated) { |
158 | if (xdg_shell_view_from_view(view) == NULL) { | 179 | if (xdg_shell_view_from_view(view) == NULL) { |
159 | return; | 180 | return; |
160 | } | 181 | } |
161 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 182 | wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated); |
162 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | ||
163 | wlr_xdg_toplevel_set_activated(surface, activated); | ||
164 | } | ||
165 | } | 183 | } |
166 | 184 | ||
167 | static void set_tiled(struct sway_view *view, bool tiled) { | 185 | static void set_tiled(struct sway_view *view, bool tiled) { |
168 | if (xdg_shell_view_from_view(view) == NULL) { | 186 | if (xdg_shell_view_from_view(view) == NULL) { |
169 | return; | 187 | return; |
170 | } | 188 | } |
171 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 189 | if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >= |
172 | enum wlr_edges edges = WLR_EDGE_NONE; | 190 | XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { |
173 | if (tiled) { | 191 | enum wlr_edges edges = WLR_EDGE_NONE; |
174 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | | 192 | if (tiled) { |
175 | WLR_EDGE_BOTTOM; | 193 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | |
194 | WLR_EDGE_BOTTOM; | ||
195 | } | ||
196 | wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); | ||
197 | } else { | ||
198 | // The version is too low for the tiled state; configure as maximized instead | ||
199 | // to stop the client from drawing decorations outside of the toplevel geometry. | ||
200 | wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled); | ||
176 | } | 201 | } |
177 | wlr_xdg_toplevel_set_tiled(surface, edges); | ||
178 | } | 202 | } |
179 | 203 | ||
180 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { | 204 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { |
181 | if (xdg_shell_view_from_view(view) == NULL) { | 205 | if (xdg_shell_view_from_view(view) == NULL) { |
182 | return; | 206 | return; |
183 | } | 207 | } |
184 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 208 | wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen); |
185 | wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); | ||
186 | } | 209 | } |
187 | 210 | ||
188 | static void set_resizing(struct sway_view *view, bool resizing) { | 211 | static void set_resizing(struct sway_view *view, bool resizing) { |
189 | if (xdg_shell_view_from_view(view) == NULL) { | 212 | if (xdg_shell_view_from_view(view) == NULL) { |
190 | return; | 213 | return; |
191 | } | 214 | } |
192 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 215 | wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing); |
193 | wlr_xdg_toplevel_set_resizing(surface, resizing); | ||
194 | } | 216 | } |
195 | 217 | ||
196 | static bool wants_floating(struct sway_view *view) { | 218 | static bool wants_floating(struct sway_view *view) { |
197 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; | 219 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
198 | struct wlr_xdg_toplevel_state *state = &toplevel->current; | 220 | struct wlr_xdg_toplevel_state *state = &toplevel->current; |
199 | return (state->min_width != 0 && state->min_height != 0 | 221 | return (state->min_width != 0 && state->min_height != 0 |
200 | && (state->min_width == state->max_width | 222 | && (state->min_width == state->max_width |
@@ -202,35 +224,17 @@ static bool wants_floating(struct sway_view *view) { | |||
202 | || toplevel->parent; | 224 | || toplevel->parent; |
203 | } | 225 | } |
204 | 226 | ||
205 | static void for_each_surface(struct sway_view *view, | ||
206 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
207 | if (xdg_shell_view_from_view(view) == NULL) { | ||
208 | return; | ||
209 | } | ||
210 | wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, | ||
211 | user_data); | ||
212 | } | ||
213 | |||
214 | static void for_each_popup_surface(struct sway_view *view, | ||
215 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
216 | if (xdg_shell_view_from_view(view) == NULL) { | ||
217 | return; | ||
218 | } | ||
219 | wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator, | ||
220 | user_data); | ||
221 | } | ||
222 | |||
223 | static bool is_transient_for(struct sway_view *child, | 227 | static bool is_transient_for(struct sway_view *child, |
224 | struct sway_view *ancestor) { | 228 | struct sway_view *ancestor) { |
225 | if (xdg_shell_view_from_view(child) == NULL) { | 229 | if (xdg_shell_view_from_view(child) == NULL) { |
226 | return false; | 230 | return false; |
227 | } | 231 | } |
228 | struct wlr_xdg_surface *surface = child->wlr_xdg_surface; | 232 | struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel; |
229 | while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | 233 | while (toplevel) { |
230 | if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { | 234 | if (toplevel->parent == ancestor->wlr_xdg_toplevel) { |
231 | return true; | 235 | return true; |
232 | } | 236 | } |
233 | surface = surface->toplevel->parent; | 237 | toplevel = toplevel->parent; |
234 | } | 238 | } |
235 | return false; | 239 | return false; |
236 | } | 240 | } |
@@ -239,17 +243,13 @@ static void _close(struct sway_view *view) { | |||
239 | if (xdg_shell_view_from_view(view) == NULL) { | 243 | if (xdg_shell_view_from_view(view) == NULL) { |
240 | return; | 244 | return; |
241 | } | 245 | } |
242 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 246 | wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel); |
243 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL | ||
244 | && surface->toplevel) { | ||
245 | wlr_xdg_toplevel_send_close(surface); | ||
246 | } | ||
247 | } | 247 | } |
248 | 248 | ||
249 | static void close_popups(struct sway_view *view) { | 249 | static void close_popups(struct sway_view *view) { |
250 | struct wlr_xdg_popup *popup, *tmp; | 250 | struct wlr_xdg_popup *popup, *tmp; |
251 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { | 251 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) { |
252 | wlr_xdg_popup_destroy(popup->base); | 252 | wlr_xdg_popup_destroy(popup); |
253 | } | 253 | } |
254 | } | 254 | } |
255 | 255 | ||
@@ -271,8 +271,6 @@ static const struct sway_view_impl view_impl = { | |||
271 | .set_fullscreen = set_fullscreen, | 271 | .set_fullscreen = set_fullscreen, |
272 | .set_resizing = set_resizing, | 272 | .set_resizing = set_resizing, |
273 | .wants_floating = wants_floating, | 273 | .wants_floating = wants_floating, |
274 | .for_each_surface = for_each_surface, | ||
275 | .for_each_popup_surface = for_each_popup_surface, | ||
276 | .is_transient_for = is_transient_for, | 274 | .is_transient_for = is_transient_for, |
277 | .close = _close, | 275 | .close = _close, |
278 | .close_popups = close_popups, | 276 | .close_popups = close_popups, |
@@ -283,7 +281,21 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
283 | struct sway_xdg_shell_view *xdg_shell_view = | 281 | struct sway_xdg_shell_view *xdg_shell_view = |
284 | wl_container_of(listener, xdg_shell_view, commit); | 282 | wl_container_of(listener, xdg_shell_view, commit); |
285 | struct sway_view *view = &xdg_shell_view->view; | 283 | struct sway_view *view = &xdg_shell_view->view; |
286 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 284 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; |
285 | |||
286 | if (xdg_surface->initial_commit) { | ||
287 | if (view->xdg_decoration != NULL) { | ||
288 | set_xdg_decoration_mode(view->xdg_decoration); | ||
289 | } | ||
290 | // XXX: https://github.com/swaywm/sway/issues/2176 | ||
291 | wlr_xdg_surface_schedule_configure(xdg_surface); | ||
292 | // TODO: wlr_xdg_toplevel_set_bounds() | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | if (!xdg_surface->surface->mapped) { | ||
297 | return; | ||
298 | } | ||
287 | 299 | ||
288 | struct wlr_box new_geo; | 300 | struct wlr_box new_geo; |
289 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); | 301 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); |
@@ -293,22 +305,35 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
293 | new_geo.y != view->geometry.y; | 305 | new_geo.y != view->geometry.y; |
294 | 306 | ||
295 | if (new_size) { | 307 | if (new_size) { |
296 | // The view has unexpectedly sent a new size | 308 | // The client changed its surface size in this commit. For floating |
297 | desktop_damage_view(view); | 309 | // containers, we resize the container to match. For tiling containers, |
298 | view_update_size(view, new_geo.width, new_geo.height); | 310 | // we only recenter the surface. |
299 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | 311 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); |
300 | desktop_damage_view(view); | 312 | if (container_is_floating(view->container)) { |
301 | transaction_commit_dirty(); | 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 | } | ||
319 | transaction_commit_dirty_client(); | ||
320 | } | ||
321 | |||
322 | view_center_and_clip_surface(view); | ||
302 | } | 323 | } |
303 | 324 | ||
304 | if (view->container->node.instruction) { | 325 | if (view->container->node.instruction) { |
305 | transaction_notify_view_ready_by_serial(view, | 326 | bool successful = transaction_notify_view_ready_by_serial(view, |
306 | xdg_surface->configure_serial); | 327 | xdg_surface->current.configure_serial); |
307 | } else if (new_size) { | 328 | |
308 | transaction_notify_view_ready_immediately(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 | } | ||
309 | } | 336 | } |
310 | |||
311 | view_damage_from(view); | ||
312 | } | 337 | } |
313 | 338 | ||
314 | static void handle_set_title(struct wl_listener *listener, void *data) { | 339 | static void handle_set_title(struct wl_listener *listener, void *data) { |
@@ -323,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) { | |||
323 | struct sway_xdg_shell_view *xdg_shell_view = | 348 | struct sway_xdg_shell_view *xdg_shell_view = |
324 | wl_container_of(listener, xdg_shell_view, set_app_id); | 349 | wl_container_of(listener, xdg_shell_view, set_app_id); |
325 | struct sway_view *view = &xdg_shell_view->view; | 350 | struct sway_view *view = &xdg_shell_view->view; |
351 | view_update_app_id(view); | ||
326 | view_execute_criteria(view); | 352 | view_execute_criteria(view); |
327 | } | 353 | } |
328 | 354 | ||
@@ -330,31 +356,42 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { | |||
330 | struct sway_xdg_shell_view *xdg_shell_view = | 356 | struct sway_xdg_shell_view *xdg_shell_view = |
331 | wl_container_of(listener, xdg_shell_view, new_popup); | 357 | wl_container_of(listener, xdg_shell_view, new_popup); |
332 | struct wlr_xdg_popup *wlr_popup = data; | 358 | struct wlr_xdg_popup *wlr_popup = data; |
333 | popup_create(wlr_popup, &xdg_shell_view->view); | 359 | |
360 | struct sway_xdg_popup *popup = popup_create(wlr_popup, | ||
361 | &xdg_shell_view->view, root->layers.popup); | ||
362 | if (!popup) { | ||
363 | return; | ||
364 | } | ||
365 | |||
366 | int lx, ly; | ||
367 | wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly); | ||
368 | wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); | ||
369 | } | ||
370 | |||
371 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | ||
372 | struct sway_xdg_shell_view *xdg_shell_view = | ||
373 | wl_container_of(listener, xdg_shell_view, request_maximize); | ||
374 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; | ||
375 | wlr_xdg_surface_schedule_configure(toplevel->base); | ||
334 | } | 376 | } |
335 | 377 | ||
336 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { | 378 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { |
337 | struct sway_xdg_shell_view *xdg_shell_view = | 379 | struct sway_xdg_shell_view *xdg_shell_view = |
338 | wl_container_of(listener, xdg_shell_view, request_fullscreen); | 380 | wl_container_of(listener, xdg_shell_view, request_fullscreen); |
339 | struct wlr_xdg_toplevel_set_fullscreen_event *e = data; | 381 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; |
340 | struct wlr_xdg_surface *xdg_surface = | ||
341 | xdg_shell_view->view.wlr_xdg_surface; | ||
342 | struct sway_view *view = &xdg_shell_view->view; | 382 | struct sway_view *view = &xdg_shell_view->view; |
343 | 383 | ||
344 | if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, | 384 | if (!toplevel->base->surface->mapped) { |
345 | "xdg_shell requested fullscreen of surface with role %i", | ||
346 | xdg_surface->role)) { | ||
347 | return; | ||
348 | } | ||
349 | if (!xdg_surface->mapped) { | ||
350 | return; | 385 | return; |
351 | } | 386 | } |
352 | 387 | ||
353 | struct sway_container *container = view->container; | 388 | struct sway_container *container = view->container; |
354 | if (e->fullscreen && e->output && e->output->data) { | 389 | struct wlr_xdg_toplevel_requested *req = &toplevel->requested; |
355 | struct sway_output *output = e->output->data; | 390 | if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) { |
391 | struct sway_output *output = req->fullscreen_output->data; | ||
356 | struct sway_workspace *ws = output_get_active_workspace(output); | 392 | struct sway_workspace *ws = output_get_active_workspace(output); |
357 | if (ws && !container_is_scratchpad_hidden(container)) { | 393 | if (ws && !container_is_scratchpad_hidden(container) && |
394 | container->pending.workspace != ws) { | ||
358 | if (container_is_floating(container)) { | 395 | if (container_is_floating(container)) { |
359 | workspace_add_floating(ws, container); | 396 | workspace_add_floating(ws, container); |
360 | } else { | 397 | } else { |
@@ -363,22 +400,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
363 | } | 400 | } |
364 | } | 401 | } |
365 | 402 | ||
366 | container_set_fullscreen(container, e->fullscreen); | 403 | container_set_fullscreen(container, req->fullscreen); |
367 | 404 | ||
368 | arrange_root(); | 405 | arrange_root(); |
369 | transaction_commit_dirty(); | 406 | transaction_commit_dirty(); |
370 | } | 407 | } |
371 | 408 | ||
372 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | ||
373 | struct wlr_xdg_surface *surface = data; | ||
374 | wlr_xdg_surface_schedule_configure(surface); | ||
375 | } | ||
376 | |||
377 | static void handle_request_move(struct wl_listener *listener, void *data) { | 409 | static void handle_request_move(struct wl_listener *listener, void *data) { |
378 | struct sway_xdg_shell_view *xdg_shell_view = | 410 | struct sway_xdg_shell_view *xdg_shell_view = |
379 | wl_container_of(listener, xdg_shell_view, request_move); | 411 | wl_container_of(listener, xdg_shell_view, request_move); |
380 | struct sway_view *view = &xdg_shell_view->view; | 412 | struct sway_view *view = &xdg_shell_view->view; |
381 | if (!container_is_floating(view->container)) { | 413 | if (!container_is_floating(view->container) || |
414 | view->container->pending.fullscreen_mode) { | ||
382 | return; | 415 | return; |
383 | } | 416 | } |
384 | struct wlr_xdg_toplevel_move_event *e = data; | 417 | struct wlr_xdg_toplevel_move_event *e = data; |
@@ -413,10 +446,9 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
413 | 446 | ||
414 | view_unmap(view); | 447 | view_unmap(view); |
415 | 448 | ||
416 | wl_list_remove(&xdg_shell_view->commit.link); | ||
417 | wl_list_remove(&xdg_shell_view->new_popup.link); | 449 | wl_list_remove(&xdg_shell_view->new_popup.link); |
418 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
419 | wl_list_remove(&xdg_shell_view->request_maximize.link); | 450 | wl_list_remove(&xdg_shell_view->request_maximize.link); |
451 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
420 | wl_list_remove(&xdg_shell_view->request_move.link); | 452 | wl_list_remove(&xdg_shell_view->request_move.link); |
421 | wl_list_remove(&xdg_shell_view->request_resize.link); | 453 | wl_list_remove(&xdg_shell_view->request_resize.link); |
422 | wl_list_remove(&xdg_shell_view->set_title.link); | 454 | wl_list_remove(&xdg_shell_view->set_title.link); |
@@ -427,62 +459,61 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
427 | struct sway_xdg_shell_view *xdg_shell_view = | 459 | struct sway_xdg_shell_view *xdg_shell_view = |
428 | wl_container_of(listener, xdg_shell_view, map); | 460 | wl_container_of(listener, xdg_shell_view, map); |
429 | struct sway_view *view = &xdg_shell_view->view; | 461 | struct sway_view *view = &xdg_shell_view->view; |
430 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 462 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
431 | 463 | ||
432 | view->natural_width = view->wlr_xdg_surface->geometry.width; | 464 | view->natural_width = toplevel->base->current.geometry.width; |
433 | view->natural_height = view->wlr_xdg_surface->geometry.height; | 465 | view->natural_height = toplevel->base->current.geometry.height; |
434 | if (!view->natural_width && !view->natural_height) { | 466 | if (!view->natural_width && !view->natural_height) { |
435 | view->natural_width = view->wlr_xdg_surface->surface->current.width; | 467 | view->natural_width = toplevel->base->surface->current.width; |
436 | view->natural_height = view->wlr_xdg_surface->surface->current.height; | 468 | view->natural_height = toplevel->base->surface->current.height; |
437 | } | 469 | } |
438 | 470 | ||
439 | bool csd = false; | 471 | bool csd = false; |
440 | 472 | ||
441 | if (!view->xdg_decoration) { | 473 | if (view->xdg_decoration) { |
474 | enum wlr_xdg_toplevel_decoration_v1_mode mode = | ||
475 | view->xdg_decoration->wlr_xdg_decoration->requested_mode; | ||
476 | csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; | ||
477 | } else { | ||
442 | struct sway_server_decoration *deco = | 478 | struct sway_server_decoration *deco = |
443 | decoration_from_surface(xdg_surface->surface); | 479 | decoration_from_surface(toplevel->base->surface); |
444 | csd = !deco || deco->wlr_server_decoration->mode == | 480 | csd = !deco || deco->wlr_server_decoration->mode == |
445 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; | 481 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; |
446 | |||
447 | } | 482 | } |
448 | 483 | ||
449 | view_map(view, view->wlr_xdg_surface->surface, | 484 | view_map(view, toplevel->base->surface, |
450 | xdg_surface->toplevel->client_pending.fullscreen, | 485 | toplevel->requested.fullscreen, |
451 | xdg_surface->toplevel->client_pending.fullscreen_output, | 486 | toplevel->requested.fullscreen_output, |
452 | csd); | 487 | csd); |
453 | 488 | ||
454 | transaction_commit_dirty(); | 489 | transaction_commit_dirty(); |
455 | 490 | ||
456 | xdg_shell_view->commit.notify = handle_commit; | ||
457 | wl_signal_add(&xdg_surface->surface->events.commit, | ||
458 | &xdg_shell_view->commit); | ||
459 | |||
460 | xdg_shell_view->new_popup.notify = handle_new_popup; | 491 | xdg_shell_view->new_popup.notify = handle_new_popup; |
461 | wl_signal_add(&xdg_surface->events.new_popup, | 492 | wl_signal_add(&toplevel->base->events.new_popup, |
462 | &xdg_shell_view->new_popup); | 493 | &xdg_shell_view->new_popup); |
463 | 494 | ||
464 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
465 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | ||
466 | &xdg_shell_view->request_fullscreen); | ||
467 | |||
468 | xdg_shell_view->request_maximize.notify = handle_request_maximize; | 495 | xdg_shell_view->request_maximize.notify = handle_request_maximize; |
469 | wl_signal_add(&xdg_surface->toplevel->events.request_maximize, | 496 | wl_signal_add(&toplevel->events.request_maximize, |
470 | &xdg_shell_view->request_maximize); | 497 | &xdg_shell_view->request_maximize); |
471 | 498 | ||
499 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
500 | wl_signal_add(&toplevel->events.request_fullscreen, | ||
501 | &xdg_shell_view->request_fullscreen); | ||
502 | |||
472 | xdg_shell_view->request_move.notify = handle_request_move; | 503 | xdg_shell_view->request_move.notify = handle_request_move; |
473 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | 504 | wl_signal_add(&toplevel->events.request_move, |
474 | &xdg_shell_view->request_move); | 505 | &xdg_shell_view->request_move); |
475 | 506 | ||
476 | xdg_shell_view->request_resize.notify = handle_request_resize; | 507 | xdg_shell_view->request_resize.notify = handle_request_resize; |
477 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | 508 | wl_signal_add(&toplevel->events.request_resize, |
478 | &xdg_shell_view->request_resize); | 509 | &xdg_shell_view->request_resize); |
479 | 510 | ||
480 | xdg_shell_view->set_title.notify = handle_set_title; | 511 | xdg_shell_view->set_title.notify = handle_set_title; |
481 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | 512 | wl_signal_add(&toplevel->events.set_title, |
482 | &xdg_shell_view->set_title); | 513 | &xdg_shell_view->set_title); |
483 | 514 | ||
484 | xdg_shell_view->set_app_id.notify = handle_set_app_id; | 515 | xdg_shell_view->set_app_id.notify = handle_set_app_id; |
485 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | 516 | wl_signal_add(&toplevel->events.set_app_id, |
486 | &xdg_shell_view->set_app_id); | 517 | &xdg_shell_view->set_app_id); |
487 | } | 518 | } |
488 | 519 | ||
@@ -496,7 +527,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
496 | wl_list_remove(&xdg_shell_view->destroy.link); | 527 | wl_list_remove(&xdg_shell_view->destroy.link); |
497 | wl_list_remove(&xdg_shell_view->map.link); | 528 | wl_list_remove(&xdg_shell_view->map.link); |
498 | wl_list_remove(&xdg_shell_view->unmap.link); | 529 | wl_list_remove(&xdg_shell_view->unmap.link); |
499 | view->wlr_xdg_surface = NULL; | 530 | wl_list_remove(&xdg_shell_view->commit.link); |
531 | view->wlr_xdg_toplevel = NULL; | ||
500 | if (view->xdg_decoration) { | 532 | if (view->xdg_decoration) { |
501 | view->xdg_decoration->view = NULL; | 533 | view->xdg_decoration->view = NULL; |
502 | } | 534 | } |
@@ -508,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface( | |||
508 | return xdg_surface->data; | 540 | return xdg_surface->data; |
509 | } | 541 | } |
510 | 542 | ||
511 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | 543 | void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { |
512 | struct wlr_xdg_surface *xdg_surface = data; | 544 | struct wlr_xdg_toplevel *xdg_toplevel = data; |
513 | |||
514 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
515 | sway_log(SWAY_DEBUG, "New xdg_shell popup"); | ||
516 | return; | ||
517 | } | ||
518 | 545 | ||
519 | 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'", |
520 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 547 | xdg_toplevel->title, xdg_toplevel->app_id); |
521 | wlr_xdg_surface_ping(xdg_surface); | 548 | wlr_xdg_surface_ping(xdg_toplevel->base); |
522 | 549 | ||
523 | struct sway_xdg_shell_view *xdg_shell_view = | 550 | struct sway_xdg_shell_view *xdg_shell_view = |
524 | calloc(1, sizeof(struct sway_xdg_shell_view)); | 551 | calloc(1, sizeof(struct sway_xdg_shell_view)); |
@@ -526,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
526 | return; | 553 | return; |
527 | } | 554 | } |
528 | 555 | ||
529 | 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)) { |
530 | xdg_shell_view->view.wlr_xdg_surface = xdg_surface; | 557 | free(xdg_shell_view); |
558 | return; | ||
559 | } | ||
560 | xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel; | ||
531 | 561 | ||
532 | xdg_shell_view->map.notify = handle_map; | 562 | xdg_shell_view->map.notify = handle_map; |
533 | 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); |
534 | 564 | ||
535 | xdg_shell_view->unmap.notify = handle_unmap; | 565 | xdg_shell_view->unmap.notify = handle_unmap; |
536 | 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); | ||
537 | 571 | ||
538 | xdg_shell_view->destroy.notify = handle_destroy; | 572 | xdg_shell_view->destroy.notify = handle_destroy; |
539 | 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; | ||
540 | 578 | ||
541 | xdg_surface->data = xdg_shell_view; | 579 | wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel, |
580 | XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); | ||
542 | } | 581 | } |
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index e1a2e463..270cf08f 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -1,20 +1,23 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
5 | #include <wayland-server-core.h> | 4 | #include <wayland-server-core.h> |
6 | #include <wlr/types/wlr_output_layout.h> | 5 | #include <wlr/types/wlr_output_layout.h> |
7 | #include <wlr/types/wlr_output.h> | 6 | #include <wlr/types/wlr_output.h> |
7 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
8 | #include <wlr/types/wlr_scene.h> | ||
8 | #include <wlr/xwayland.h> | 9 | #include <wlr/xwayland.h> |
10 | #include <xcb/xcb_icccm.h> | ||
9 | #include "log.h" | 11 | #include "log.h" |
10 | #include "sway/desktop.h" | ||
11 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
12 | #include "sway/input/cursor.h" | 13 | #include "sway/input/cursor.h" |
13 | #include "sway/input/input-manager.h" | 14 | #include "sway/input/input-manager.h" |
14 | #include "sway/input/seat.h" | 15 | #include "sway/input/seat.h" |
15 | #include "sway/output.h" | 16 | #include "sway/output.h" |
17 | #include "sway/scene_descriptor.h" | ||
16 | #include "sway/tree/arrange.h" | 18 | #include "sway/tree/arrange.h" |
17 | #include "sway/tree/container.h" | 19 | #include "sway/tree/container.h" |
20 | #include "sway/server.h" | ||
18 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
19 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
20 | 23 | ||
@@ -42,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener, | |||
42 | ev->width, ev->height); | 45 | ev->width, ev->height); |
43 | } | 46 | } |
44 | 47 | ||
45 | static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { | ||
46 | struct sway_xwayland_unmanaged *surface = | ||
47 | wl_container_of(listener, surface, commit); | ||
48 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
49 | |||
50 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
51 | false); | ||
52 | } | ||
53 | |||
54 | static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { | 48 | static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { |
55 | struct sway_xwayland_unmanaged *surface = | 49 | struct sway_xwayland_unmanaged *surface = |
56 | wl_container_of(listener, surface, set_geometry); | 50 | wl_container_of(listener, surface, set_geometry); |
57 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 51 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
58 | 52 | ||
59 | if (xsurface->x != surface->lx || xsurface->y != surface->ly) { | 53 | wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y); |
60 | // Surface has moved | ||
61 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
62 | true); | ||
63 | surface->lx = xsurface->x; | ||
64 | surface->ly = xsurface->y; | ||
65 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
66 | true); | ||
67 | } | ||
68 | } | 54 | } |
69 | 55 | ||
70 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | 56 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { |
@@ -72,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | |||
72 | wl_container_of(listener, surface, map); | 58 | wl_container_of(listener, surface, map); |
73 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 59 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
74 | 60 | ||
75 | wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); | 61 | surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged, |
76 | 62 | xsurface->surface); | |
77 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); | ||
78 | surface->set_geometry.notify = unmanaged_handle_set_geometry; | ||
79 | 63 | ||
80 | wl_signal_add(&xsurface->surface->events.commit, &surface->commit); | 64 | if (surface->surface_scene) { |
81 | surface->commit.notify = unmanaged_handle_commit; | 65 | scene_descriptor_assign(&surface->surface_scene->buffer->node, |
66 | SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface); | ||
67 | wlr_scene_node_set_position(&surface->surface_scene->buffer->node, | ||
68 | xsurface->x, xsurface->y); | ||
82 | 69 | ||
83 | surface->lx = xsurface->x; | 70 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); |
84 | surface->ly = xsurface->y; | 71 | surface->set_geometry.notify = unmanaged_handle_set_geometry; |
85 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); | 72 | } |
86 | 73 | ||
87 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { | 74 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { |
88 | struct sway_seat *seat = input_manager_current_seat(); | 75 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -96,23 +83,22 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
96 | struct sway_xwayland_unmanaged *surface = | 83 | struct sway_xwayland_unmanaged *surface = |
97 | wl_container_of(listener, surface, unmap); | 84 | wl_container_of(listener, surface, unmap); |
98 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 85 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
99 | desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); | 86 | |
100 | wl_list_remove(&surface->link); | 87 | if (surface->surface_scene) { |
101 | wl_list_remove(&surface->set_geometry.link); | 88 | wl_list_remove(&surface->set_geometry.link); |
102 | wl_list_remove(&surface->commit.link); | 89 | |
90 | wlr_scene_node_destroy(&surface->surface_scene->buffer->node); | ||
91 | surface->surface_scene = NULL; | ||
92 | } | ||
103 | 93 | ||
104 | struct sway_seat *seat = input_manager_current_seat(); | 94 | struct sway_seat *seat = input_manager_current_seat(); |
105 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { | 95 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { |
106 | // This simply returns focus to the parent surface if there's one available. | 96 | // This simply returns focus to the parent surface if there's one available. |
107 | // This seems to handle JetBrains issues. | 97 | // This seems to handle JetBrains issues. |
108 | if (xsurface->parent && xsurface->parent->surface && | 98 | if (xsurface->parent && xsurface->parent->surface |
109 | wlr_surface_is_xwayland_surface(xsurface->parent->surface)) { | 99 | && wlr_xwayland_or_surface_wants_focus(xsurface->parent)) { |
110 | struct wlr_xwayland_surface *next_surface = | 100 | seat_set_focus_surface(seat, xsurface->parent->surface, false); |
111 | wlr_xwayland_surface_from_wlr_surface(xsurface->parent->surface); | 101 | return; |
112 | if (wlr_xwayland_or_surface_wants_focus(next_surface)) { | ||
113 | seat_set_focus_surface(seat, xsurface->parent->surface, false); | ||
114 | return; | ||
115 | } | ||
116 | } | 102 | } |
117 | 103 | ||
118 | // Restore focus | 104 | // Restore focus |
@@ -125,18 +111,53 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
125 | } | 111 | } |
126 | } | 112 | } |
127 | 113 | ||
114 | static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { | ||
115 | struct sway_xwayland_unmanaged *surface = | ||
116 | wl_container_of(listener, surface, request_activate); | ||
117 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
118 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { | ||
119 | return; | ||
120 | } | ||
121 | struct sway_seat *seat = input_manager_current_seat(); | ||
122 | struct sway_container *focus = seat_get_focused_container(seat); | ||
123 | if (focus && focus->view && focus->view->pid != xsurface->pid) { | ||
124 | return; | ||
125 | } | ||
126 | |||
127 | seat_set_focus_surface(seat, xsurface->surface, false); | ||
128 | } | ||
129 | |||
130 | static void unmanaged_handle_associate(struct wl_listener *listener, void *data) { | ||
131 | struct sway_xwayland_unmanaged *surface = | ||
132 | wl_container_of(listener, surface, associate); | ||
133 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
134 | wl_signal_add(&xsurface->surface->events.map, &surface->map); | ||
135 | surface->map.notify = unmanaged_handle_map; | ||
136 | wl_signal_add(&xsurface->surface->events.unmap, &surface->unmap); | ||
137 | surface->unmap.notify = unmanaged_handle_unmap; | ||
138 | } | ||
139 | |||
140 | static void unmanaged_handle_dissociate(struct wl_listener *listener, void *data) { | ||
141 | struct sway_xwayland_unmanaged *surface = | ||
142 | wl_container_of(listener, surface, dissociate); | ||
143 | wl_list_remove(&surface->map.link); | ||
144 | wl_list_remove(&surface->unmap.link); | ||
145 | } | ||
146 | |||
128 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { | 147 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { |
129 | struct sway_xwayland_unmanaged *surface = | 148 | struct sway_xwayland_unmanaged *surface = |
130 | wl_container_of(listener, surface, destroy); | 149 | wl_container_of(listener, surface, destroy); |
131 | wl_list_remove(&surface->request_configure.link); | 150 | wl_list_remove(&surface->request_configure.link); |
132 | wl_list_remove(&surface->map.link); | 151 | wl_list_remove(&surface->associate.link); |
133 | wl_list_remove(&surface->unmap.link); | 152 | wl_list_remove(&surface->dissociate.link); |
134 | wl_list_remove(&surface->destroy.link); | 153 | wl_list_remove(&surface->destroy.link); |
135 | wl_list_remove(&surface->override_redirect.link); | 154 | wl_list_remove(&surface->override_redirect.link); |
155 | wl_list_remove(&surface->request_activate.link); | ||
136 | free(surface); | 156 | free(surface); |
137 | } | 157 | } |
138 | 158 | ||
139 | 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); | ||
140 | 161 | ||
141 | 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); |
142 | 163 | ||
@@ -145,14 +166,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi | |||
145 | wl_container_of(listener, surface, override_redirect); | 166 | wl_container_of(listener, surface, override_redirect); |
146 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 167 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
147 | 168 | ||
148 | bool mapped = xsurface->mapped; | 169 | bool associated = xsurface->surface != NULL; |
170 | bool mapped = associated && xsurface->surface->mapped; | ||
149 | if (mapped) { | 171 | if (mapped) { |
150 | unmanaged_handle_unmap(&surface->unmap, NULL); | 172 | unmanaged_handle_unmap(&surface->unmap, NULL); |
151 | } | 173 | } |
174 | if (associated) { | ||
175 | unmanaged_handle_dissociate(&surface->dissociate, NULL); | ||
176 | } | ||
152 | 177 | ||
153 | unmanaged_handle_destroy(&surface->destroy, NULL); | 178 | unmanaged_handle_destroy(&surface->destroy, NULL); |
154 | xsurface->data = NULL; | 179 | xsurface->data = NULL; |
180 | |||
155 | 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 | } | ||
156 | if (mapped) { | 185 | if (mapped) { |
157 | handle_map(&xwayland_view->map, xsurface); | 186 | handle_map(&xwayland_view->map, xsurface); |
158 | } | 187 | } |
@@ -172,14 +201,16 @@ static struct sway_xwayland_unmanaged *create_unmanaged( | |||
172 | wl_signal_add(&xsurface->events.request_configure, | 201 | wl_signal_add(&xsurface->events.request_configure, |
173 | &surface->request_configure); | 202 | &surface->request_configure); |
174 | surface->request_configure.notify = unmanaged_handle_request_configure; | 203 | surface->request_configure.notify = unmanaged_handle_request_configure; |
175 | wl_signal_add(&xsurface->events.map, &surface->map); | 204 | wl_signal_add(&xsurface->events.associate, &surface->associate); |
176 | surface->map.notify = unmanaged_handle_map; | 205 | surface->associate.notify = unmanaged_handle_associate; |
177 | wl_signal_add(&xsurface->events.unmap, &surface->unmap); | 206 | wl_signal_add(&xsurface->events.dissociate, &surface->dissociate); |
178 | surface->unmap.notify = unmanaged_handle_unmap; | 207 | surface->dissociate.notify = unmanaged_handle_dissociate; |
179 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); | 208 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); |
180 | surface->destroy.notify = unmanaged_handle_destroy; | 209 | surface->destroy.notify = unmanaged_handle_destroy; |
181 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); | 210 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); |
182 | surface->override_redirect.notify = unmanaged_handle_override_redirect; | 211 | surface->override_redirect.notify = unmanaged_handle_override_redirect; |
212 | wl_signal_add(&xsurface->events.request_activate, &surface->request_activate); | ||
213 | surface->request_activate.notify = unmanaged_handle_request_activate; | ||
183 | 214 | ||
184 | return surface; | 215 | return surface; |
185 | } | 216 | } |
@@ -258,6 +289,7 @@ static void set_activated(struct sway_view *view, bool activated) { | |||
258 | } | 289 | } |
259 | 290 | ||
260 | wlr_xwayland_surface_activate(surface, activated); | 291 | wlr_xwayland_surface_activate(surface, activated); |
292 | wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); | ||
261 | } | 293 | } |
262 | 294 | ||
263 | static void set_tiled(struct sway_view *view, bool tiled) { | 295 | static void set_tiled(struct sway_view *view, bool tiled) { |
@@ -297,7 +329,7 @@ static bool wants_floating(struct sway_view *view) { | |||
297 | } | 329 | } |
298 | } | 330 | } |
299 | 331 | ||
300 | struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; | 332 | xcb_size_hints_t *size_hints = surface->size_hints; |
301 | if (size_hints != NULL && | 333 | if (size_hints != NULL && |
302 | size_hints->min_width > 0 && size_hints->min_height > 0 && | 334 | size_hints->min_width > 0 && size_hints->min_height > 0 && |
303 | (size_hints->max_width == size_hints->min_width || | 335 | (size_hints->max_width == size_hints->min_width || |
@@ -351,7 +383,7 @@ static void destroy(struct sway_view *view) { | |||
351 | static void get_constraints(struct sway_view *view, double *min_width, | 383 | static void get_constraints(struct sway_view *view, double *min_width, |
352 | double *max_width, double *min_height, double *max_height) { | 384 | double *max_width, double *min_height, double *max_height) { |
353 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; | 385 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; |
354 | struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; | 386 | xcb_size_hints_t *size_hints = surface->size_hints; |
355 | 387 | ||
356 | if (size_hints == NULL) { | 388 | if (size_hints == NULL) { |
357 | *min_width = DBL_MIN; | 389 | *min_width = DBL_MIN; |
@@ -381,17 +413,6 @@ static const struct sway_view_impl view_impl = { | |||
381 | .destroy = destroy, | 413 | .destroy = destroy, |
382 | }; | 414 | }; |
383 | 415 | ||
384 | static void get_geometry(struct sway_view *view, struct wlr_box *box) { | ||
385 | box->x = box->y = 0; | ||
386 | if (view->surface) { | ||
387 | box->width = view->surface->current.width; | ||
388 | box->height = view->surface->current.height; | ||
389 | } else { | ||
390 | box->width = 0; | ||
391 | box->height = 0; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | static void handle_commit(struct wl_listener *listener, void *data) { | 416 | static void handle_commit(struct wl_listener *listener, void *data) { |
396 | struct sway_xwayland_view *xwayland_view = | 417 | struct sway_xwayland_view *xwayland_view = |
397 | wl_container_of(listener, xwayland_view, commit); | 418 | wl_container_of(listener, xwayland_view, commit); |
@@ -399,33 +420,38 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
399 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 420 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
400 | struct wlr_surface_state *state = &xsurface->surface->current; | 421 | struct wlr_surface_state *state = &xsurface->surface->current; |
401 | 422 | ||
423 | struct wlr_box new_geo = {0}; | ||
424 | new_geo.width = state->width; | ||
425 | new_geo.height = state->height; | ||
426 | |||
427 | bool new_size = new_geo.width != view->geometry.width || | ||
428 | new_geo.height != view->geometry.height; | ||
429 | |||
430 | if (new_size) { | ||
431 | // The client changed its surface size in this commit. For floating | ||
432 | // containers, we resize the container to match. For tiling containers, | ||
433 | // we only recenter the surface. | ||
434 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | ||
435 | if (container_is_floating(view->container)) { | ||
436 | view_update_size(view); | ||
437 | transaction_commit_dirty_client(); | ||
438 | } | ||
439 | |||
440 | view_center_and_clip_surface(view); | ||
441 | } | ||
442 | |||
402 | if (view->container->node.instruction) { | 443 | if (view->container->node.instruction) { |
403 | get_geometry(view, &view->geometry); | 444 | bool successful = transaction_notify_view_ready_by_geometry(view, |
404 | transaction_notify_view_ready_by_geometry(view, | ||
405 | xsurface->x, xsurface->y, state->width, state->height); | 445 | xsurface->x, xsurface->y, state->width, state->height); |
406 | } else { | 446 | |
407 | struct wlr_box new_geo; | 447 | // If we saved the view and this commit isn't what we're looking for |
408 | get_geometry(view, &new_geo); | 448 | // that means the user will never actually see the buffers submitted to |
409 | 449 | // us here. Just send frame done events to these surfaces so they can | |
410 | if ((new_geo.width != view->geometry.width || | 450 | // commit another time for us. |
411 | new_geo.height != view->geometry.height || | 451 | if (view->saved_surface_tree && !successful) { |
412 | new_geo.x != view->geometry.x || | 452 | view_send_frame_done(view); |
413 | new_geo.y != view->geometry.y)) { | ||
414 | // The view has unexpectedly sent a new size | ||
415 | // eg. The Firefox "Save As" dialog when downloading a file | ||
416 | desktop_damage_view(view); | ||
417 | view_update_size(view, new_geo.width, new_geo.height); | ||
418 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | ||
419 | desktop_damage_view(view); | ||
420 | transaction_commit_dirty(); | ||
421 | transaction_notify_view_ready_by_geometry(view, | ||
422 | xsurface->x, xsurface->y, new_geo.width, new_geo.height); | ||
423 | } else { | ||
424 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | ||
425 | } | 453 | } |
426 | } | 454 | } |
427 | |||
428 | view_damage_from(view); | ||
429 | } | 455 | } |
430 | 456 | ||
431 | static void handle_destroy(struct wl_listener *listener, void *data) { | 457 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -438,6 +464,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
438 | wl_list_remove(&xwayland_view->commit.link); | 464 | wl_list_remove(&xwayland_view->commit.link); |
439 | } | 465 | } |
440 | 466 | ||
467 | xwayland_view->view.wlr_xwayland_surface = NULL; | ||
468 | |||
441 | wl_list_remove(&xwayland_view->destroy.link); | 469 | wl_list_remove(&xwayland_view->destroy.link); |
442 | wl_list_remove(&xwayland_view->request_configure.link); | 470 | wl_list_remove(&xwayland_view->request_configure.link); |
443 | wl_list_remove(&xwayland_view->request_fullscreen.link); | 471 | wl_list_remove(&xwayland_view->request_fullscreen.link); |
@@ -448,11 +476,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
448 | wl_list_remove(&xwayland_view->set_title.link); | 476 | wl_list_remove(&xwayland_view->set_title.link); |
449 | wl_list_remove(&xwayland_view->set_class.link); | 477 | wl_list_remove(&xwayland_view->set_class.link); |
450 | 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); | ||
451 | wl_list_remove(&xwayland_view->set_window_type.link); | 480 | wl_list_remove(&xwayland_view->set_window_type.link); |
452 | wl_list_remove(&xwayland_view->set_hints.link); | 481 | wl_list_remove(&xwayland_view->set_hints.link); |
453 | wl_list_remove(&xwayland_view->set_decorations.link); | 482 | wl_list_remove(&xwayland_view->set_decorations.link); |
454 | wl_list_remove(&xwayland_view->map.link); | 483 | wl_list_remove(&xwayland_view->associate.link); |
455 | wl_list_remove(&xwayland_view->unmap.link); | 484 | wl_list_remove(&xwayland_view->dissociate.link); |
456 | wl_list_remove(&xwayland_view->override_redirect.link); | 485 | wl_list_remove(&xwayland_view->override_redirect.link); |
457 | view_begin_destroy(&xwayland_view->view); | 486 | view_begin_destroy(&xwayland_view->view); |
458 | } | 487 | } |
@@ -466,16 +495,28 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
466 | return; | 495 | return; |
467 | } | 496 | } |
468 | 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 | |||
469 | view_unmap(view); | 506 | view_unmap(view); |
507 | } | ||
470 | 508 | ||
471 | 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; | ||
472 | } | 513 | } |
473 | 514 | ||
474 | static void handle_map(struct wl_listener *listener, void *data) { | 515 | static void handle_map(struct wl_listener *listener, void *data) { |
475 | struct sway_xwayland_view *xwayland_view = | 516 | struct sway_xwayland_view *xwayland_view = |
476 | wl_container_of(listener, xwayland_view, map); | 517 | wl_container_of(listener, xwayland_view, map); |
477 | struct wlr_xwayland_surface *xsurface = data; | ||
478 | 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; | ||
479 | 520 | ||
480 | view->natural_width = xsurface->width; | 521 | view->natural_width = xsurface->width; |
481 | view->natural_height = xsurface->height; | 522 | view->natural_height = xsurface->height; |
@@ -488,23 +529,42 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
488 | // Put it back into the tree | 529 | // Put it back into the tree |
489 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); | 530 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); |
490 | 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 | |||
491 | transaction_commit_dirty(); | 541 | transaction_commit_dirty(); |
492 | } | 542 | } |
493 | 543 | ||
544 | static void handle_dissociate(struct wl_listener *listener, void *data); | ||
545 | |||
494 | static void handle_override_redirect(struct wl_listener *listener, void *data) { | 546 | static void handle_override_redirect(struct wl_listener *listener, void *data) { |
495 | struct sway_xwayland_view *xwayland_view = | 547 | struct sway_xwayland_view *xwayland_view = |
496 | wl_container_of(listener, xwayland_view, override_redirect); | 548 | wl_container_of(listener, xwayland_view, override_redirect); |
497 | struct wlr_xwayland_surface *xsurface = data; | ||
498 | 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; | ||
499 | 551 | ||
500 | bool mapped = xsurface->mapped; | 552 | bool associated = xsurface->surface != NULL; |
553 | bool mapped = associated && xsurface->surface->mapped; | ||
501 | if (mapped) { | 554 | if (mapped) { |
502 | handle_unmap(&xwayland_view->unmap, NULL); | 555 | handle_unmap(&xwayland_view->unmap, NULL); |
503 | } | 556 | } |
557 | if (associated) { | ||
558 | handle_dissociate(&xwayland_view->dissociate, NULL); | ||
559 | } | ||
504 | 560 | ||
505 | handle_destroy(&xwayland_view->destroy, view); | 561 | handle_destroy(&xwayland_view->destroy, view); |
506 | xsurface->data = NULL; | 562 | xsurface->data = NULL; |
563 | |||
507 | 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 | } | ||
508 | if (mapped) { | 568 | if (mapped) { |
509 | unmanaged_handle_map(&unmanaged->map, xsurface); | 569 | unmanaged_handle_map(&unmanaged->map, xsurface); |
510 | } | 570 | } |
@@ -516,7 +576,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { | |||
516 | struct wlr_xwayland_surface_configure_event *ev = data; | 576 | struct wlr_xwayland_surface_configure_event *ev = data; |
517 | struct sway_view *view = &xwayland_view->view; | 577 | struct sway_view *view = &xwayland_view->view; |
518 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 578 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
519 | if (!xsurface->mapped) { | 579 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
520 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, | 580 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, |
521 | ev->width, ev->height); | 581 | ev->width, ev->height); |
522 | return; | 582 | return; |
@@ -527,10 +587,10 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { | |||
527 | view->natural_height = ev->height; | 587 | view->natural_height = ev->height; |
528 | container_floating_resize_and_center(view->container); | 588 | container_floating_resize_and_center(view->container); |
529 | 589 | ||
530 | configure(view, view->container->content_x, | 590 | configure(view, view->container->pending.content_x, |
531 | view->container->content_y, | 591 | view->container->pending.content_y, |
532 | view->container->content_width, | 592 | view->container->pending.content_width, |
533 | view->container->content_height); | 593 | view->container->pending.content_height); |
534 | node_set_dirty(&view->container->node); | 594 | node_set_dirty(&view->container->node); |
535 | } else { | 595 | } else { |
536 | configure(view, view->container->current.content_x, | 596 | configure(view, view->container->current.content_x, |
@@ -545,7 +605,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
545 | wl_container_of(listener, xwayland_view, request_fullscreen); | 605 | wl_container_of(listener, xwayland_view, request_fullscreen); |
546 | struct sway_view *view = &xwayland_view->view; | 606 | struct sway_view *view = &xwayland_view->view; |
547 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 607 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
548 | if (!xsurface->mapped) { | 608 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
549 | return; | 609 | return; |
550 | } | 610 | } |
551 | container_set_fullscreen(view->container, xsurface->fullscreen); | 611 | container_set_fullscreen(view->container, xsurface->fullscreen); |
@@ -559,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) { | |||
559 | wl_container_of(listener, xwayland_view, request_minimize); | 619 | wl_container_of(listener, xwayland_view, request_minimize); |
560 | struct sway_view *view = &xwayland_view->view; | 620 | struct sway_view *view = &xwayland_view->view; |
561 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 621 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
562 | if (!xsurface->mapped) { | 622 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
563 | return; | 623 | return; |
564 | } | 624 | } |
565 | 625 | ||
@@ -574,10 +634,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) { | |||
574 | wl_container_of(listener, xwayland_view, request_move); | 634 | wl_container_of(listener, xwayland_view, request_move); |
575 | struct sway_view *view = &xwayland_view->view; | 635 | struct sway_view *view = &xwayland_view->view; |
576 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 636 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
577 | if (!xsurface->mapped) { | 637 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
578 | return; | 638 | return; |
579 | } | 639 | } |
580 | if (!container_is_floating(view->container)) { | 640 | if (!container_is_floating(view->container) || |
641 | view->container->pending.fullscreen_mode) { | ||
581 | return; | 642 | return; |
582 | } | 643 | } |
583 | struct sway_seat *seat = input_manager_current_seat(); | 644 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -589,7 +650,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { | |||
589 | wl_container_of(listener, xwayland_view, request_resize); | 650 | wl_container_of(listener, xwayland_view, request_resize); |
590 | struct sway_view *view = &xwayland_view->view; | 651 | struct sway_view *view = &xwayland_view->view; |
591 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 652 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
592 | if (!xsurface->mapped) { | 653 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
593 | return; | 654 | return; |
594 | } | 655 | } |
595 | if (!container_is_floating(view->container)) { | 656 | if (!container_is_floating(view->container)) { |
@@ -605,10 +666,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) { | |||
605 | wl_container_of(listener, xwayland_view, request_activate); | 666 | wl_container_of(listener, xwayland_view, request_activate); |
606 | struct sway_view *view = &xwayland_view->view; | 667 | struct sway_view *view = &xwayland_view->view; |
607 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 668 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
608 | if (!xsurface->mapped) { | 669 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
609 | return; | 670 | return; |
610 | } | 671 | } |
611 | view_request_activate(view); | 672 | view_request_activate(view, NULL); |
612 | 673 | ||
613 | transaction_commit_dirty(); | 674 | transaction_commit_dirty(); |
614 | } | 675 | } |
@@ -618,7 +679,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { | |||
618 | wl_container_of(listener, xwayland_view, set_title); | 679 | wl_container_of(listener, xwayland_view, set_title); |
619 | struct sway_view *view = &xwayland_view->view; | 680 | struct sway_view *view = &xwayland_view->view; |
620 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 681 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
621 | if (!xsurface->mapped) { | 682 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
622 | return; | 683 | return; |
623 | } | 684 | } |
624 | view_update_title(view, false); | 685 | view_update_title(view, false); |
@@ -630,7 +691,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) { | |||
630 | wl_container_of(listener, xwayland_view, set_class); | 691 | wl_container_of(listener, xwayland_view, set_class); |
631 | struct sway_view *view = &xwayland_view->view; | 692 | struct sway_view *view = &xwayland_view->view; |
632 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 693 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
633 | if (!xsurface->mapped) { | 694 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
634 | return; | 695 | return; |
635 | } | 696 | } |
636 | view_execute_criteria(view); | 697 | view_execute_criteria(view); |
@@ -641,18 +702,43 @@ static void handle_set_role(struct wl_listener *listener, void *data) { | |||
641 | wl_container_of(listener, xwayland_view, set_role); | 702 | wl_container_of(listener, xwayland_view, set_role); |
642 | struct sway_view *view = &xwayland_view->view; | 703 | struct sway_view *view = &xwayland_view->view; |
643 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 704 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
644 | if (!xsurface->mapped) { | 705 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
645 | return; | 706 | return; |
646 | } | 707 | } |
647 | view_execute_criteria(view); | 708 | view_execute_criteria(view); |
648 | } | 709 | } |
649 | 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 | |||
650 | 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) { |
651 | struct sway_xwayland_view *xwayland_view = | 737 | struct sway_xwayland_view *xwayland_view = |
652 | wl_container_of(listener, xwayland_view, set_window_type); | 738 | wl_container_of(listener, xwayland_view, set_window_type); |
653 | struct sway_view *view = &xwayland_view->view; | 739 | struct sway_view *view = &xwayland_view->view; |
654 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 740 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
655 | if (!xsurface->mapped) { | 741 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
656 | return; | 742 | return; |
657 | } | 743 | } |
658 | view_execute_criteria(view); | 744 | view_execute_criteria(view); |
@@ -663,20 +749,39 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { | |||
663 | wl_container_of(listener, xwayland_view, set_hints); | 749 | wl_container_of(listener, xwayland_view, set_hints); |
664 | struct sway_view *view = &xwayland_view->view; | 750 | struct sway_view *view = &xwayland_view->view; |
665 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 751 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
666 | if (!xsurface->mapped) { | 752 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
667 | return; | 753 | return; |
668 | } | 754 | } |
669 | if (!xsurface->hints_urgency && view->urgent_timer) { | 755 | const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); |
756 | if (!hints_urgency && view->urgent_timer) { | ||
670 | // The view is in the timeout period. We'll ignore the request to | 757 | // The view is in the timeout period. We'll ignore the request to |
671 | // unset urgency so that the view remains urgent until the timer clears | 758 | // unset urgency so that the view remains urgent until the timer clears |
672 | // it. | 759 | // it. |
673 | return; | 760 | return; |
674 | } | 761 | } |
675 | if (view->allow_request_urgent) { | 762 | if (view->allow_request_urgent) { |
676 | view_set_urgent(view, (bool)xsurface->hints_urgency); | 763 | view_set_urgent(view, hints_urgency); |
677 | } | 764 | } |
678 | } | 765 | } |
679 | 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 | |||
680 | struct sway_view *view_from_wlr_xwayland_surface( | 785 | struct sway_view *view_from_wlr_xwayland_surface( |
681 | struct wlr_xwayland_surface *xsurface) { | 786 | struct wlr_xwayland_surface *xsurface) { |
682 | return xsurface->data; | 787 | return xsurface->data; |
@@ -692,7 +797,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
692 | return NULL; | 797 | return NULL; |
693 | } | 798 | } |
694 | 799 | ||
695 | 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 | } | ||
696 | xwayland_view->view.wlr_xwayland_surface = xsurface; | 804 | xwayland_view->view.wlr_xwayland_surface = xsurface; |
697 | 805 | ||
698 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); | 806 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); |
@@ -731,6 +839,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
731 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); | 839 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); |
732 | xwayland_view->set_role.notify = handle_set_role; | 840 | xwayland_view->set_role.notify = handle_set_role; |
733 | 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 | |||
734 | wl_signal_add(&xsurface->events.set_window_type, | 846 | wl_signal_add(&xsurface->events.set_window_type, |
735 | &xwayland_view->set_window_type); | 847 | &xwayland_view->set_window_type); |
736 | xwayland_view->set_window_type.notify = handle_set_window_type; | 848 | xwayland_view->set_window_type.notify = handle_set_window_type; |
@@ -742,11 +854,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
742 | &xwayland_view->set_decorations); | 854 | &xwayland_view->set_decorations); |
743 | xwayland_view->set_decorations.notify = handle_set_decorations; | 855 | xwayland_view->set_decorations.notify = handle_set_decorations; |
744 | 856 | ||
745 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); | 857 | wl_signal_add(&xsurface->events.associate, &xwayland_view->associate); |
746 | xwayland_view->unmap.notify = handle_unmap; | 858 | xwayland_view->associate.notify = handle_associate; |
747 | 859 | ||
748 | wl_signal_add(&xsurface->events.map, &xwayland_view->map); | 860 | wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate); |
749 | xwayland_view->map.notify = handle_map; | 861 | xwayland_view->dissociate.notify = handle_dissociate; |
750 | 862 | ||
751 | wl_signal_add(&xsurface->events.set_override_redirect, | 863 | wl_signal_add(&xsurface->events.set_override_redirect, |
752 | &xwayland_view->override_redirect); | 864 | &xwayland_view->override_redirect); |
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fa604426..3d04826c 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -1,14 +1,14 @@ | |||
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> |
5 | #include <linux/input-event-codes.h> | 4 | #include <linux/input-event-codes.h> |
6 | #include <errno.h> | 5 | #include <errno.h> |
6 | #include <time.h> | ||
7 | #include <strings.h> | 7 | #include <strings.h> |
8 | #include <wlr/types/wlr_box.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,56 +19,22 @@ | |||
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/desktop/transaction.h" | ||
24 | #include "sway/input/cursor.h" | 22 | #include "sway/input/cursor.h" |
25 | #include "sway/input/keyboard.h" | 23 | #include "sway/input/keyboard.h" |
26 | #include "sway/input/tablet.h" | 24 | #include "sway/input/tablet.h" |
27 | #include "sway/layers.h" | 25 | #include "sway/layers.h" |
28 | #include "sway/output.h" | 26 | #include "sway/output.h" |
27 | #include "sway/scene_descriptor.h" | ||
29 | #include "sway/tree/container.h" | 28 | #include "sway/tree/container.h" |
30 | #include "sway/tree/root.h" | 29 | #include "sway/tree/root.h" |
31 | #include "sway/tree/view.h" | 30 | #include "sway/tree/view.h" |
32 | #include "sway/tree/workspace.h" | 31 | #include "sway/tree/workspace.h" |
33 | #include "wlr-layer-shell-unstable-v1-protocol.h" | 32 | #include "wlr-layer-shell-unstable-v1-protocol.h" |
34 | 33 | ||
35 | static struct wlr_surface *layer_surface_at(struct sway_output *output, | 34 | static uint32_t get_current_time_msec(void) { |
36 | struct wl_list *layer, double ox, double oy, double *sx, double *sy) { | 35 | struct timespec now; |
37 | struct sway_layer_surface *sway_layer; | 36 | clock_gettime(CLOCK_MONOTONIC, &now); |
38 | wl_list_for_each_reverse(sway_layer, layer, link) { | 37 | return now.tv_sec * 1000 + now.tv_nsec / 1000000; |
39 | double _sx = ox - sway_layer->geo.x; | ||
40 | double _sy = oy - sway_layer->geo.y; | ||
41 | struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( | ||
42 | sway_layer->layer_surface, _sx, _sy, sx, sy); | ||
43 | if (sub) { | ||
44 | return sub; | ||
45 | } | ||
46 | } | ||
47 | return NULL; | ||
48 | } | ||
49 | |||
50 | static bool surface_is_xdg_popup(struct wlr_surface *surface) { | ||
51 | if (wlr_surface_is_xdg_surface(surface)) { | ||
52 | struct wlr_xdg_surface *xdg_surface = | ||
53 | wlr_xdg_surface_from_wlr_surface(surface); | ||
54 | return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; | ||
55 | } | ||
56 | return false; | ||
57 | } | ||
58 | |||
59 | static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, | ||
60 | struct wl_list *layer, double ox, double oy, double *sx, double *sy) { | ||
61 | struct sway_layer_surface *sway_layer; | ||
62 | wl_list_for_each_reverse(sway_layer, layer, link) { | ||
63 | double _sx = ox - sway_layer->geo.x; | ||
64 | double _sy = oy - sway_layer->geo.y; | ||
65 | struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( | ||
66 | sway_layer->layer_surface, _sx, _sy, sx, sy); | ||
67 | if (sub && surface_is_xdg_popup(sub)) { | ||
68 | return sub; | ||
69 | } | ||
70 | } | ||
71 | return NULL; | ||
72 | } | 38 | } |
73 | 39 | ||
74 | /** | 40 | /** |
@@ -78,116 +44,101 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, | |||
78 | struct sway_node *node_at_coords( | 44 | struct sway_node *node_at_coords( |
79 | struct sway_seat *seat, double lx, double ly, | 45 | struct sway_seat *seat, double lx, double ly, |
80 | struct wlr_surface **surface, double *sx, double *sy) { | 46 | struct wlr_surface **surface, double *sx, double *sy) { |
81 | // check for unmanaged views first | 47 | struct wlr_scene_node *scene_node = NULL; |
82 | #if HAVE_XWAYLAND | 48 | |
83 | struct wl_list *unmanaged = &root->xwayland_unmanaged; | 49 | struct wlr_scene_node *node; |
84 | struct sway_xwayland_unmanaged *unmanaged_surface; | 50 | wl_list_for_each_reverse(node, &root->layer_tree->children, link) { |
85 | wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { | 51 | struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node); |
86 | struct wlr_xwayland_surface *xsurface = | 52 | |
87 | unmanaged_surface->wlr_xwayland_surface; | 53 | bool non_interactive = scene_descriptor_try_get(&layer->node, |
88 | 54 | SWAY_SCENE_DESC_NON_INTERACTIVE); | |
89 | double _sx = lx - unmanaged_surface->lx; | 55 | if (non_interactive) { |
90 | double _sy = ly - unmanaged_surface->ly; | 56 | continue; |
91 | if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { | 57 | } |
92 | *surface = xsurface->surface; | 58 | |
93 | *sx = _sx; | 59 | scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy); |
94 | *sy = _sy; | 60 | if (scene_node) { |
95 | return NULL; | 61 | break; |
96 | } | 62 | } |
97 | } | 63 | } |
64 | |||
65 | if (scene_node) { | ||
66 | // determine what wlr_surface we clicked on | ||
67 | if (scene_node->type == WLR_SCENE_NODE_BUFFER) { | ||
68 | struct wlr_scene_buffer *scene_buffer = | ||
69 | wlr_scene_buffer_from_node(scene_node); | ||
70 | struct wlr_scene_surface *scene_surface = | ||
71 | wlr_scene_surface_try_from_buffer(scene_buffer); | ||
72 | |||
73 | if (scene_surface) { | ||
74 | *surface = scene_surface->surface; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | // determine what container we clicked on | ||
79 | struct wlr_scene_node *current = scene_node; | ||
80 | while (true) { | ||
81 | struct sway_container *con = scene_descriptor_try_get(current, | ||
82 | SWAY_SCENE_DESC_CONTAINER); | ||
83 | |||
84 | if (!con) { | ||
85 | struct sway_view *view = scene_descriptor_try_get(current, | ||
86 | SWAY_SCENE_DESC_VIEW); | ||
87 | if (view) { | ||
88 | con = view->container; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | if (!con) { | ||
93 | struct sway_popup_desc *popup = | ||
94 | scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP); | ||
95 | if (popup && popup->view) { | ||
96 | con = popup->view->container; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | if (con && (!con->view || con->view->surface)) { | ||
101 | return &con->node; | ||
102 | } | ||
103 | |||
104 | if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) { | ||
105 | // We don't want to feed through the current workspace on | ||
106 | // layer shells | ||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | #if HAVE_XWAYLAND | ||
111 | if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) { | ||
112 | return NULL; | ||
113 | } | ||
98 | #endif | 114 | #endif |
99 | // find the output the cursor is on | 115 | |
116 | if (!current->parent) { | ||
117 | break; | ||
118 | } | ||
119 | |||
120 | current = ¤t->parent->node; | ||
121 | } | ||
122 | } | ||
123 | |||
124 | // if we aren't on a container, determine what workspace we are on | ||
100 | struct wlr_output *wlr_output = wlr_output_layout_output_at( | 125 | struct wlr_output *wlr_output = wlr_output_layout_output_at( |
101 | root->output_layout, lx, ly); | 126 | root->output_layout, lx, ly); |
102 | if (wlr_output == NULL) { | 127 | if (wlr_output == NULL) { |
103 | return NULL; | 128 | return NULL; |
104 | } | 129 | } |
130 | |||
105 | struct sway_output *output = wlr_output->data; | 131 | struct sway_output *output = wlr_output->data; |
106 | if (!output || !output->enabled) { | 132 | if (!output || !output->enabled) { |
107 | // output is being destroyed or is being enabled | 133 | // output is being destroyed or is being enabled |
108 | return NULL; | 134 | return NULL; |
109 | } | 135 | } |
110 | double ox = lx, oy = ly; | ||
111 | wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); | ||
112 | 136 | ||
113 | if (root->fullscreen_global) { | ||
114 | // Try fullscreen container | ||
115 | struct sway_container *con = tiling_container_at( | ||
116 | &root->fullscreen_global->node, lx, ly, surface, sx, sy); | ||
117 | if (con) { | ||
118 | return &con->node; | ||
119 | } | ||
120 | return NULL; | ||
121 | } | ||
122 | |||
123 | // find the focused workspace on the output for this seat | ||
124 | struct sway_workspace *ws = output_get_active_workspace(output); | 137 | struct sway_workspace *ws = output_get_active_workspace(output); |
125 | if (!ws) { | 138 | if (!ws) { |
126 | return NULL; | 139 | return NULL; |
127 | } | 140 | } |
128 | 141 | ||
129 | if ((*surface = layer_surface_at(output, | ||
130 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
131 | ox, oy, sx, sy))) { | ||
132 | return NULL; | ||
133 | } | ||
134 | if (ws->fullscreen) { | ||
135 | // Try transient containers | ||
136 | for (int i = 0; i < ws->floating->length; ++i) { | ||
137 | struct sway_container *floater = ws->floating->items[i]; | ||
138 | if (container_is_transient_for(floater, ws->fullscreen)) { | ||
139 | struct sway_container *con = tiling_container_at( | ||
140 | &floater->node, lx, ly, surface, sx, sy); | ||
141 | if (con) { | ||
142 | return &con->node; | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | // Try fullscreen container | ||
147 | struct sway_container *con = | ||
148 | tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); | ||
149 | if (con) { | ||
150 | return &con->node; | ||
151 | } | ||
152 | return NULL; | ||
153 | } | ||
154 | if ((*surface = layer_surface_popup_at(output, | ||
155 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
156 | ox, oy, sx, sy))) { | ||
157 | return NULL; | ||
158 | } | ||
159 | if ((*surface = layer_surface_popup_at(output, | ||
160 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
161 | ox, oy, sx, sy))) { | ||
162 | return NULL; | ||
163 | } | ||
164 | if ((*surface = layer_surface_popup_at(output, | ||
165 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
166 | ox, oy, sx, sy))) { | ||
167 | return NULL; | ||
168 | } | ||
169 | if ((*surface = layer_surface_at(output, | ||
170 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
171 | ox, oy, sx, sy))) { | ||
172 | return NULL; | ||
173 | } | ||
174 | |||
175 | struct sway_container *c; | ||
176 | if ((c = container_at(ws, lx, ly, surface, sx, sy))) { | ||
177 | return &c->node; | ||
178 | } | ||
179 | |||
180 | if ((*surface = layer_surface_at(output, | ||
181 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
182 | ox, oy, sx, sy))) { | ||
183 | return NULL; | ||
184 | } | ||
185 | if ((*surface = layer_surface_at(output, | ||
186 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
187 | ox, oy, sx, sy))) { | ||
188 | return NULL; | ||
189 | } | ||
190 | |||
191 | return &ws->node; | 142 | return &ws->node; |
192 | } | 143 | } |
193 | 144 | ||
@@ -213,7 +164,7 @@ void cursor_update_image(struct sway_cursor *cursor, | |||
213 | // Try a node's resize edge | 164 | // Try a node's resize edge |
214 | 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); |
215 | if (edge == WLR_EDGE_NONE) { | 166 | if (edge == WLR_EDGE_NONE) { |
216 | cursor_set_image(cursor, "left_ptr", NULL); | 167 | cursor_set_image(cursor, "default", NULL); |
217 | } else if (container_is_floating(node->sway_container)) { | 168 | } else if (container_is_floating(node->sway_container)) { |
218 | cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); | 169 | cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); |
219 | } else { | 170 | } else { |
@@ -224,12 +175,12 @@ void cursor_update_image(struct sway_cursor *cursor, | |||
224 | } | 175 | } |
225 | } | 176 | } |
226 | } else { | 177 | } else { |
227 | cursor_set_image(cursor, "left_ptr", NULL); | 178 | cursor_set_image(cursor, "default", NULL); |
228 | } | 179 | } |
229 | } | 180 | } |
230 | 181 | ||
231 | static void cursor_hide(struct sway_cursor *cursor) { | 182 | static void cursor_hide(struct sway_cursor *cursor) { |
232 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | 183 | wlr_cursor_unset_image(cursor->cursor); |
233 | cursor->hidden = true; | 184 | cursor->hidden = true; |
234 | wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); | 185 | wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); |
235 | } | 186 | } |
@@ -292,7 +243,7 @@ static enum sway_input_idle_source idle_source_from_device( | |||
292 | return IDLE_SOURCE_POINTER; | 243 | return IDLE_SOURCE_POINTER; |
293 | case WLR_INPUT_DEVICE_TOUCH: | 244 | case WLR_INPUT_DEVICE_TOUCH: |
294 | return IDLE_SOURCE_TOUCH; | 245 | return IDLE_SOURCE_TOUCH; |
295 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 246 | case WLR_INPUT_DEVICE_TABLET: |
296 | return IDLE_SOURCE_TABLET_TOOL; | 247 | return IDLE_SOURCE_TABLET_TOOL; |
297 | case WLR_INPUT_DEVICE_TABLET_PAD: | 248 | case WLR_INPUT_DEVICE_TABLET_PAD: |
298 | return IDLE_SOURCE_TABLET_PAD; | 249 | return IDLE_SOURCE_TABLET_PAD; |
@@ -341,7 +292,7 @@ void cursor_unhide(struct sway_cursor *cursor) { | |||
341 | 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)); |
342 | } | 293 | } |
343 | 294 | ||
344 | static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | 295 | void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, |
345 | struct wlr_input_device *device, double dx, double dy, | 296 | struct wlr_input_device *device, double dx, double dy, |
346 | double dx_unaccel, double dy_unaccel) { | 297 | double dx_unaccel, double dy_unaccel) { |
347 | wlr_relative_pointer_manager_v1_send_relative_motion( | 298 | wlr_relative_pointer_manager_v1_send_relative_motion( |
@@ -378,35 +329,34 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
378 | static void handle_pointer_motion_relative( | 329 | static void handle_pointer_motion_relative( |
379 | struct wl_listener *listener, void *data) { | 330 | struct wl_listener *listener, void *data) { |
380 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); | 331 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); |
381 | struct wlr_event_pointer_motion *e = data; | 332 | struct wlr_pointer_motion_event *e = data; |
382 | cursor_handle_activity_from_device(cursor, e->device); | 333 | cursor_handle_activity_from_device(cursor, &e->pointer->base); |
383 | 334 | ||
384 | pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, | 335 | pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x, |
385 | e->unaccel_dx, e->unaccel_dy); | 336 | e->delta_y, e->unaccel_dx, e->unaccel_dy); |
386 | transaction_commit_dirty(); | ||
387 | } | 337 | } |
388 | 338 | ||
389 | static void handle_pointer_motion_absolute( | 339 | static void handle_pointer_motion_absolute( |
390 | struct wl_listener *listener, void *data) { | 340 | struct wl_listener *listener, void *data) { |
391 | struct sway_cursor *cursor = | 341 | struct sway_cursor *cursor = |
392 | wl_container_of(listener, cursor, motion_absolute); | 342 | wl_container_of(listener, cursor, motion_absolute); |
393 | struct wlr_event_pointer_motion_absolute *event = data; | 343 | struct wlr_pointer_motion_absolute_event *event = data; |
394 | cursor_handle_activity_from_device(cursor, event->device); | 344 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
395 | 345 | ||
396 | double lx, ly; | 346 | double lx, ly; |
397 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 347 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base, |
398 | event->x, event->y, &lx, &ly); | 348 | event->x, event->y, &lx, &ly); |
399 | 349 | ||
400 | double dx = lx - cursor->cursor->x; | 350 | double dx = lx - cursor->cursor->x; |
401 | double dy = ly - cursor->cursor->y; | 351 | double dy = ly - cursor->cursor->y; |
402 | 352 | ||
403 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | 353 | pointer_motion(cursor, event->time_msec, &event->pointer->base, dx, dy, |
404 | transaction_commit_dirty(); | 354 | dx, dy); |
405 | } | 355 | } |
406 | 356 | ||
407 | void dispatch_cursor_button(struct sway_cursor *cursor, | 357 | void dispatch_cursor_button(struct sway_cursor *cursor, |
408 | 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, |
409 | enum wlr_button_state state) { | 359 | enum wl_pointer_button_state state) { |
410 | if (time_msec == 0) { | 360 | if (time_msec == 0) { |
411 | time_msec = get_current_time_msec(); | 361 | time_msec = get_current_time_msec(); |
412 | } | 362 | } |
@@ -416,9 +366,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
416 | 366 | ||
417 | static void handle_pointer_button(struct wl_listener *listener, void *data) { | 367 | static void handle_pointer_button(struct wl_listener *listener, void *data) { |
418 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); | 368 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); |
419 | struct wlr_event_pointer_button *event = data; | 369 | struct wlr_pointer_button_event *event = data; |
420 | 370 | ||
421 | if (event->state == WLR_BUTTON_PRESSED) { | 371 | if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) { |
422 | cursor->pressed_button_count++; | 372 | cursor->pressed_button_count++; |
423 | } else { | 373 | } else { |
424 | if (cursor->pressed_button_count > 0) { | 374 | if (cursor->pressed_button_count > 0) { |
@@ -428,23 +378,21 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) { | |||
428 | } | 378 | } |
429 | } | 379 | } |
430 | 380 | ||
431 | cursor_handle_activity_from_device(cursor, event->device); | 381 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
432 | dispatch_cursor_button(cursor, event->device, | 382 | dispatch_cursor_button(cursor, &event->pointer->base, |
433 | event->time_msec, event->button, event->state); | 383 | event->time_msec, event->button, event->state); |
434 | transaction_commit_dirty(); | ||
435 | } | 384 | } |
436 | 385 | ||
437 | void dispatch_cursor_axis(struct sway_cursor *cursor, | 386 | void dispatch_cursor_axis(struct sway_cursor *cursor, |
438 | struct wlr_event_pointer_axis *event) { | 387 | struct wlr_pointer_axis_event *event) { |
439 | seatop_pointer_axis(cursor->seat, event); | 388 | seatop_pointer_axis(cursor->seat, event); |
440 | } | 389 | } |
441 | 390 | ||
442 | static void handle_pointer_axis(struct wl_listener *listener, void *data) { | 391 | static void handle_pointer_axis(struct wl_listener *listener, void *data) { |
443 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); | 392 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); |
444 | struct wlr_event_pointer_axis *event = data; | 393 | struct wlr_pointer_axis_event *event = data; |
445 | cursor_handle_activity_from_device(cursor, event->device); | 394 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
446 | dispatch_cursor_axis(cursor, event); | 395 | dispatch_cursor_axis(cursor, event); |
447 | transaction_commit_dirty(); | ||
448 | } | 396 | } |
449 | 397 | ||
450 | static void handle_pointer_frame(struct wl_listener *listener, void *data) { | 398 | static void handle_pointer_frame(struct wl_listener *listener, void *data) { |
@@ -454,97 +402,76 @@ static void handle_pointer_frame(struct wl_listener *listener, void *data) { | |||
454 | 402 | ||
455 | static void handle_touch_down(struct wl_listener *listener, void *data) { | 403 | static void handle_touch_down(struct wl_listener *listener, void *data) { |
456 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); | 404 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); |
457 | struct wlr_event_touch_down *event = data; | 405 | struct wlr_touch_down_event *event = data; |
458 | cursor_handle_activity_from_device(cursor, event->device); | 406 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
459 | cursor_hide(cursor); | 407 | cursor_hide(cursor); |
460 | 408 | ||
461 | struct sway_seat *seat = cursor->seat; | 409 | struct sway_seat *seat = cursor->seat; |
462 | struct wlr_seat *wlr_seat = seat->wlr_seat; | ||
463 | struct wlr_surface *surface = NULL; | ||
464 | 410 | ||
465 | double lx, ly; | 411 | double lx, ly; |
466 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 412 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, |
467 | event->x, event->y, &lx, &ly); | 413 | event->x, event->y, &lx, &ly); |
468 | double sx, sy; | ||
469 | struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy); | ||
470 | 414 | ||
471 | seat->touch_id = event->touch_id; | 415 | seat->touch_id = event->touch_id; |
472 | seat->touch_x = lx; | 416 | seat->touch_x = lx; |
473 | seat->touch_y = ly; | 417 | seat->touch_y = ly; |
474 | 418 | ||
475 | if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { | 419 | seatop_touch_down(seat, event, lx, ly); |
476 | if (seat_is_input_allowed(seat, surface)) { | 420 | } |
477 | wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, | ||
478 | event->touch_id, sx, sy); | ||
479 | 421 | ||
480 | if (focused_node) { | 422 | static void handle_touch_up(struct wl_listener *listener, void *data) { |
481 | seat_set_focus(seat, focused_node); | 423 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); |
482 | } | 424 | struct wlr_touch_up_event *event = data; |
425 | cursor_handle_activity_from_device(cursor, &event->touch->base); | ||
426 | |||
427 | struct sway_seat *seat = cursor->seat; | ||
428 | |||
429 | if (cursor->simulating_pointer_from_touch) { | ||
430 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { | ||
431 | cursor->pointer_touch_up = true; | ||
432 | dispatch_cursor_button(cursor, &event->touch->base, | ||
433 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); | ||
483 | } | 434 | } |
484 | } else if (!cursor->simulating_pointer_from_touch && | 435 | } else { |
485 | (!surface || seat_is_input_allowed(seat, surface))) { | 436 | seatop_touch_up(seat, event); |
486 | // Fallback to cursor simulation. | ||
487 | // The pointer_touch_id state is needed, so drags are not aborted when over | ||
488 | // a surface supporting touch and multi touch events don't interfere. | ||
489 | cursor->simulating_pointer_from_touch = true; | ||
490 | cursor->pointer_touch_id = seat->touch_id; | ||
491 | double dx, dy; | ||
492 | dx = lx - cursor->cursor->x; | ||
493 | dy = ly - cursor->cursor->y; | ||
494 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | ||
495 | dispatch_cursor_button(cursor, event->device, event->time_msec, | ||
496 | BTN_LEFT, WLR_BUTTON_PRESSED); | ||
497 | wlr_seat_pointer_notify_frame(wlr_seat); | ||
498 | transaction_commit_dirty(); | ||
499 | } | 437 | } |
500 | } | 438 | } |
501 | 439 | ||
502 | static void handle_touch_up(struct wl_listener *listener, void *data) { | 440 | static void handle_touch_cancel(struct wl_listener *listener, void *data) { |
503 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); | 441 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel); |
504 | struct wlr_event_touch_up *event = data; | 442 | struct wlr_touch_cancel_event *event = data; |
505 | cursor_handle_activity_from_device(cursor, event->device); | 443 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
506 | 444 | ||
507 | struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; | 445 | struct sway_seat *seat = cursor->seat; |
508 | 446 | ||
509 | if (cursor->simulating_pointer_from_touch) { | 447 | if (cursor->simulating_pointer_from_touch) { |
510 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { | 448 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { |
511 | cursor->simulating_pointer_from_touch = false; | 449 | cursor->pointer_touch_up = true; |
512 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 450 | dispatch_cursor_button(cursor, &event->touch->base, |
513 | BTN_LEFT, WLR_BUTTON_RELEASED); | 451 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); |
514 | wlr_seat_pointer_notify_frame(wlr_seat); | ||
515 | transaction_commit_dirty(); | ||
516 | } | 452 | } |
517 | } else { | 453 | } else { |
518 | wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); | 454 | seatop_touch_cancel(seat, event); |
519 | } | 455 | } |
520 | } | 456 | } |
521 | 457 | ||
522 | static void handle_touch_motion(struct wl_listener *listener, void *data) { | 458 | static void handle_touch_motion(struct wl_listener *listener, void *data) { |
523 | struct sway_cursor *cursor = | 459 | struct sway_cursor *cursor = |
524 | wl_container_of(listener, cursor, touch_motion); | 460 | wl_container_of(listener, cursor, touch_motion); |
525 | struct wlr_event_touch_motion *event = data; | 461 | struct wlr_touch_motion_event *event = data; |
526 | cursor_handle_activity_from_device(cursor, event->device); | 462 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
527 | 463 | ||
528 | struct sway_seat *seat = cursor->seat; | 464 | struct sway_seat *seat = cursor->seat; |
529 | struct wlr_seat *wlr_seat = seat->wlr_seat; | ||
530 | struct wlr_surface *surface = NULL; | ||
531 | 465 | ||
532 | double lx, ly; | 466 | double lx, ly; |
533 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 467 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, |
534 | event->x, event->y, &lx, &ly); | 468 | event->x, event->y, &lx, &ly); |
535 | double sx, sy; | ||
536 | node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); | ||
537 | 469 | ||
538 | if (seat->touch_id == event->touch_id) { | 470 | if (seat->touch_id == event->touch_id) { |
539 | seat->touch_x = lx; | 471 | seat->touch_x = lx; |
540 | seat->touch_y = ly; | 472 | seat->touch_y = ly; |
541 | 473 | ||
542 | struct sway_drag_icon *drag_icon; | 474 | drag_icons_update_position(seat); |
543 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
544 | if (drag_icon->seat == seat) { | ||
545 | drag_icon_update_position(drag_icon); | ||
546 | } | ||
547 | } | ||
548 | } | 475 | } |
549 | 476 | ||
550 | if (cursor->simulating_pointer_from_touch) { | 477 | if (cursor->simulating_pointer_from_touch) { |
@@ -552,12 +479,29 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { | |||
552 | double dx, dy; | 479 | double dx, dy; |
553 | dx = lx - cursor->cursor->x; | 480 | dx = lx - cursor->cursor->x; |
554 | dy = ly - cursor->cursor->y; | 481 | dy = ly - cursor->cursor->y; |
555 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | 482 | pointer_motion(cursor, event->time_msec, &event->touch->base, |
556 | transaction_commit_dirty(); | 483 | dx, dy, dx, dy); |
484 | } | ||
485 | } else { | ||
486 | seatop_touch_motion(seat, event, lx, ly); | ||
487 | } | ||
488 | } | ||
489 | |||
490 | static void handle_touch_frame(struct wl_listener *listener, void *data) { | ||
491 | struct sway_cursor *cursor = | ||
492 | wl_container_of(listener, cursor, touch_frame); | ||
493 | |||
494 | struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; | ||
495 | |||
496 | if (cursor->simulating_pointer_from_touch) { | ||
497 | wlr_seat_pointer_notify_frame(wlr_seat); | ||
498 | |||
499 | if (cursor->pointer_touch_up) { | ||
500 | cursor->pointer_touch_up = false; | ||
501 | cursor->simulating_pointer_from_touch = false; | ||
557 | } | 502 | } |
558 | } else if (surface) { | 503 | } else { |
559 | wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, | 504 | wlr_seat_touch_notify_frame(wlr_seat); |
560 | event->touch_id, sx, sy); | ||
561 | } | 505 | } |
562 | } | 506 | } |
563 | 507 | ||
@@ -574,14 +518,15 @@ static void apply_mapping_from_region(struct wlr_input_device *device, | |||
574 | double x1 = region->x1, x2 = region->x2; | 518 | double x1 = region->x1, x2 = region->x2; |
575 | double y1 = region->y1, y2 = region->y2; | 519 | double y1 = region->y1, y2 = region->y2; |
576 | 520 | ||
577 | if (region->mm) { | 521 | if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) { |
578 | if (device->width_mm == 0 || device->height_mm == 0) { | 522 | struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); |
523 | if (tablet->width_mm == 0 || tablet->height_mm == 0) { | ||
579 | return; | 524 | return; |
580 | } | 525 | } |
581 | x1 /= device->width_mm; | 526 | x1 /= tablet->width_mm; |
582 | x2 /= device->width_mm; | 527 | x2 /= tablet->width_mm; |
583 | y1 /= device->height_mm; | 528 | y1 /= tablet->height_mm; |
584 | y2 /= device->height_mm; | 529 | y2 /= tablet->height_mm; |
585 | } | 530 | } |
586 | 531 | ||
587 | *x = apply_mapping_from_coord(x1, x2, *x); | 532 | *x = apply_mapping_from_coord(x1, x2, *x); |
@@ -639,14 +584,12 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor, | |||
639 | wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); | 584 | wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); |
640 | pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); | 585 | pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); |
641 | } | 586 | } |
642 | |||
643 | transaction_commit_dirty(); | ||
644 | } | 587 | } |
645 | 588 | ||
646 | static void handle_tool_axis(struct wl_listener *listener, void *data) { | 589 | static void handle_tool_axis(struct wl_listener *listener, void *data) { |
647 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); | 590 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); |
648 | struct wlr_event_tablet_tool_axis *event = data; | 591 | struct wlr_tablet_tool_axis_event *event = data; |
649 | cursor_handle_activity_from_device(cursor, event->device); | 592 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
650 | 593 | ||
651 | struct sway_tablet_tool *sway_tool = event->tool->data; | 594 | struct sway_tablet_tool *sway_tool = event->tool->data; |
652 | if (!sway_tool) { | 595 | if (!sway_tool) { |
@@ -701,8 +644,8 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { | |||
701 | 644 | ||
702 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | 645 | static void handle_tool_tip(struct wl_listener *listener, void *data) { |
703 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); | 646 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); |
704 | struct wlr_event_tablet_tool_tip *event = data; | 647 | struct wlr_tablet_tool_tip_event *event = data; |
705 | cursor_handle_activity_from_device(cursor, event->device); | 648 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
706 | 649 | ||
707 | struct sway_tablet_tool *sway_tool = event->tool->data; | 650 | struct sway_tablet_tool *sway_tool = event->tool->data; |
708 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; | 651 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; |
@@ -717,10 +660,9 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { | |||
717 | if (cursor->simulating_pointer_from_tool_tip && | 660 | if (cursor->simulating_pointer_from_tool_tip && |
718 | event->state == WLR_TABLET_TOOL_TIP_UP) { | 661 | event->state == WLR_TABLET_TOOL_TIP_UP) { |
719 | cursor->simulating_pointer_from_tool_tip = false; | 662 | cursor->simulating_pointer_from_tool_tip = false; |
720 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 663 | dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, |
721 | BTN_LEFT, WLR_BUTTON_RELEASED); | 664 | BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); |
722 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 665 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
723 | transaction_commit_dirty(); | ||
724 | } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { | 666 | } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { |
725 | // 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 |
726 | // tablet v2, we should notify that surface if it gets released over a | 668 | // tablet v2, we should notify that surface if it gets released over a |
@@ -730,10 +672,9 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { | |||
730 | WLR_TABLET_TOOL_TIP_UP); | 672 | WLR_TABLET_TOOL_TIP_UP); |
731 | } else { | 673 | } else { |
732 | cursor->simulating_pointer_from_tool_tip = true; | 674 | cursor->simulating_pointer_from_tool_tip = true; |
733 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 675 | dispatch_cursor_button(cursor, &event->tablet->base, |
734 | BTN_LEFT, WLR_BUTTON_PRESSED); | 676 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); |
735 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 677 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
736 | transaction_commit_dirty(); | ||
737 | } | 678 | } |
738 | } else { | 679 | } else { |
739 | seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state); | 680 | seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state); |
@@ -754,12 +695,13 @@ static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor, | |||
754 | static void handle_tool_proximity(struct wl_listener *listener, void *data) { | 695 | static void handle_tool_proximity(struct wl_listener *listener, void *data) { |
755 | struct sway_cursor *cursor = | 696 | struct sway_cursor *cursor = |
756 | wl_container_of(listener, cursor, tool_proximity); | 697 | wl_container_of(listener, cursor, tool_proximity); |
757 | struct wlr_event_tablet_tool_proximity *event = data; | 698 | struct wlr_tablet_tool_proximity_event *event = data; |
758 | cursor_handle_activity_from_device(cursor, event->device); | 699 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
759 | 700 | ||
760 | struct wlr_tablet_tool *tool = event->tool; | 701 | struct wlr_tablet_tool *tool = event->tool; |
761 | if (!tool->data) { | 702 | if (!tool->data) { |
762 | struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); | 703 | struct sway_tablet *tablet = get_tablet_for_device(cursor, |
704 | &event->tablet->base); | ||
763 | if (!tablet) { | 705 | if (!tablet) { |
764 | sway_log(SWAY_ERROR, "no tablet for tablet tool"); | 706 | sway_log(SWAY_ERROR, "no tablet for tablet tool"); |
765 | return; | 707 | return; |
@@ -784,8 +726,8 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) { | |||
784 | 726 | ||
785 | static void handle_tool_button(struct wl_listener *listener, void *data) { | 727 | static void handle_tool_button(struct wl_listener *listener, void *data) { |
786 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); | 728 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); |
787 | struct wlr_event_tablet_tool_button *event = data; | 729 | struct wlr_tablet_tool_button_event *event = data; |
788 | cursor_handle_activity_from_device(cursor, event->device); | 730 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
789 | 731 | ||
790 | struct sway_tablet_tool *sway_tool = event->tool->data; | 732 | struct sway_tablet_tool *sway_tool = event->tool->data; |
791 | if (!sway_tool) { | 733 | if (!sway_tool) { |
@@ -800,32 +742,71 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { | |||
800 | node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, | 742 | node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, |
801 | &surface, &sx, &sy); | 743 | &surface, &sx, &sy); |
802 | 744 | ||
803 | 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 | |||
804 | // 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 |
805 | // which simulated pointer buttons | 774 | // which simulated pointer buttons |
806 | switch (event->state) { | 775 | switch (event->state) { |
807 | case WLR_BUTTON_PRESSED: | 776 | case WLR_BUTTON_PRESSED: |
808 | if (cursor->tool_buttons == 0) { | 777 | if (cursor->tool_buttons == 0) { |
809 | dispatch_cursor_button(cursor, event->device, | 778 | dispatch_cursor_button(cursor, &event->tablet->base, |
810 | event->time_msec, BTN_RIGHT, event->state); | 779 | event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED); |
811 | } | 780 | } |
812 | cursor->tool_buttons++; | ||
813 | break; | 781 | break; |
814 | case WLR_BUTTON_RELEASED: | 782 | case WLR_BUTTON_RELEASED: |
815 | if (cursor->tool_buttons == 1) { | 783 | if (cursor->tool_buttons <= 1) { |
816 | dispatch_cursor_button(cursor, event->device, | 784 | dispatch_cursor_button(cursor, &event->tablet->base, |
817 | event->time_msec, BTN_RIGHT, event->state); | 785 | event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED); |
818 | } | 786 | } |
819 | cursor->tool_buttons--; | ||
820 | break; | 787 | break; |
821 | } | 788 | } |
822 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 789 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
823 | transaction_commit_dirty(); | 790 | } else { |
824 | return; | 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); | ||
825 | } | 795 | } |
826 | 796 | ||
827 | wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, | 797 | // Update tool button count. |
828 | 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 | } | ||
829 | } | 810 | } |
830 | 811 | ||
831 | static void check_constraint_region(struct sway_cursor *cursor) { | 812 | static void check_constraint_region(struct sway_cursor *cursor) { |
@@ -837,8 +818,8 @@ static void check_constraint_region(struct sway_cursor *cursor) { | |||
837 | 818 | ||
838 | struct sway_container *con = view->container; | 819 | struct sway_container *con = view->container; |
839 | 820 | ||
840 | double sx = cursor->cursor->x - con->content_x + view->geometry.x; | 821 | double sx = cursor->cursor->x - con->pending.content_x + view->geometry.x; |
841 | double sy = cursor->cursor->y - con->content_y + view->geometry.y; | 822 | double sy = cursor->cursor->y - con->pending.content_y + view->geometry.y; |
842 | 823 | ||
843 | if (!pixman_region32_contains_point(region, | 824 | if (!pixman_region32_contains_point(region, |
844 | floor(sx), floor(sy), NULL)) { | 825 | floor(sx), floor(sy), NULL)) { |
@@ -849,8 +830,8 @@ static void check_constraint_region(struct sway_cursor *cursor) { | |||
849 | double sy = (boxes[0].y1 + boxes[0].y2) / 2.; | 830 | double sy = (boxes[0].y1 + boxes[0].y2) / 2.; |
850 | 831 | ||
851 | wlr_cursor_warp_closest(cursor->cursor, NULL, | 832 | wlr_cursor_warp_closest(cursor->cursor, NULL, |
852 | sx + con->content_x - view->geometry.x, | 833 | sx + con->pending.content_x - view->geometry.x, |
853 | sy + con->content_y - view->geometry.y); | 834 | sy + con->pending.content_y - view->geometry.y); |
854 | 835 | ||
855 | cursor_rebase(cursor); | 836 | cursor_rebase(cursor); |
856 | } | 837 | } |
@@ -911,59 +892,68 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener, | |||
911 | event->hotspot_y, focused_client); | 892 | event->hotspot_y, focused_client); |
912 | } | 893 | } |
913 | 894 | ||
895 | static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) { | ||
896 | struct sway_cursor *cursor = wl_container_of( | ||
897 | listener, cursor, hold_begin); | ||
898 | struct wlr_pointer_hold_begin_event *event = data; | ||
899 | cursor_handle_activity_from_device(cursor, &event->pointer->base); | ||
900 | seatop_hold_begin(cursor->seat, event); | ||
901 | } | ||
902 | |||
903 | static void handle_pointer_hold_end(struct wl_listener *listener, void *data) { | ||
904 | struct sway_cursor *cursor = wl_container_of( | ||
905 | listener, cursor, hold_end); | ||
906 | struct wlr_pointer_hold_end_event *event = data; | ||
907 | cursor_handle_activity_from_device(cursor, &event->pointer->base); | ||
908 | seatop_hold_end(cursor->seat, event); | ||
909 | } | ||
910 | |||
914 | static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { | 911 | static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { |
915 | struct sway_cursor *cursor = wl_container_of( | 912 | struct sway_cursor *cursor = wl_container_of( |
916 | listener, cursor, pinch_begin); | 913 | listener, cursor, pinch_begin); |
917 | struct wlr_event_pointer_pinch_begin *event = data; | 914 | struct wlr_pointer_pinch_begin_event *event = data; |
918 | wlr_pointer_gestures_v1_send_pinch_begin( | 915 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
919 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 916 | seatop_pinch_begin(cursor->seat, event); |
920 | event->time_msec, event->fingers); | ||
921 | } | 917 | } |
922 | 918 | ||
923 | static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { | 919 | static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { |
924 | struct sway_cursor *cursor = wl_container_of( | 920 | struct sway_cursor *cursor = wl_container_of( |
925 | listener, cursor, pinch_update); | 921 | listener, cursor, pinch_update); |
926 | struct wlr_event_pointer_pinch_update *event = data; | 922 | struct wlr_pointer_pinch_update_event *event = data; |
927 | wlr_pointer_gestures_v1_send_pinch_update( | 923 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
928 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 924 | seatop_pinch_update(cursor->seat, event); |
929 | event->time_msec, event->dx, event->dy, | ||
930 | event->scale, event->rotation); | ||
931 | } | 925 | } |
932 | 926 | ||
933 | static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { | 927 | static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { |
934 | struct sway_cursor *cursor = wl_container_of( | 928 | struct sway_cursor *cursor = wl_container_of( |
935 | listener, cursor, pinch_end); | 929 | listener, cursor, pinch_end); |
936 | struct wlr_event_pointer_pinch_end *event = data; | 930 | struct wlr_pointer_pinch_end_event *event = data; |
937 | wlr_pointer_gestures_v1_send_pinch_end( | 931 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
938 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 932 | seatop_pinch_end(cursor->seat, event); |
939 | event->time_msec, event->cancelled); | ||
940 | } | 933 | } |
941 | 934 | ||
942 | static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { | 935 | static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { |
943 | struct sway_cursor *cursor = wl_container_of( | 936 | struct sway_cursor *cursor = wl_container_of( |
944 | listener, cursor, swipe_begin); | 937 | listener, cursor, swipe_begin); |
945 | struct wlr_event_pointer_swipe_begin *event = data; | 938 | struct wlr_pointer_swipe_begin_event *event = data; |
946 | wlr_pointer_gestures_v1_send_swipe_begin( | 939 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
947 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 940 | seatop_swipe_begin(cursor->seat, event); |
948 | event->time_msec, event->fingers); | ||
949 | } | 941 | } |
950 | 942 | ||
951 | static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { | 943 | static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { |
952 | struct sway_cursor *cursor = wl_container_of( | 944 | struct sway_cursor *cursor = wl_container_of( |
953 | listener, cursor, swipe_update); | 945 | listener, cursor, swipe_update); |
954 | struct wlr_event_pointer_swipe_update *event = data; | 946 | struct wlr_pointer_swipe_update_event *event = data; |
955 | wlr_pointer_gestures_v1_send_swipe_update( | 947 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
956 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 948 | seatop_swipe_update(cursor->seat, event); |
957 | event->time_msec, event->dx, event->dy); | ||
958 | } | 949 | } |
959 | 950 | ||
960 | static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { | 951 | static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { |
961 | struct sway_cursor *cursor = wl_container_of( | 952 | struct sway_cursor *cursor = wl_container_of( |
962 | listener, cursor, swipe_end); | 953 | listener, cursor, swipe_end); |
963 | struct wlr_event_pointer_swipe_end *event = data; | 954 | struct wlr_pointer_swipe_end_event *event = data; |
964 | wlr_pointer_gestures_v1_send_swipe_end( | 955 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
965 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 956 | seatop_swipe_end(cursor->seat, event); |
966 | event->time_msec, event->cancelled); | ||
967 | } | 957 | } |
968 | 958 | ||
969 | static void handle_image_surface_destroy(struct wl_listener *listener, | 959 | static void handle_image_surface_destroy(struct wl_listener *listener, |
@@ -1002,10 +992,9 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image, | |||
1002 | } | 992 | } |
1003 | 993 | ||
1004 | if (!image) { | 994 | if (!image) { |
1005 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | 995 | wlr_cursor_unset_image(cursor->cursor); |
1006 | } else if (!current_image || strcmp(current_image, image) != 0) { | 996 | } else if (!current_image || strcmp(current_image, image) != 0) { |
1007 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, | 997 | wlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image); |
1008 | cursor->cursor); | ||
1009 | } | 998 | } |
1010 | } | 999 | } |
1011 | 1000 | ||
@@ -1037,6 +1026,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { | |||
1037 | wl_event_source_remove(cursor->hide_source); | 1026 | wl_event_source_remove(cursor->hide_source); |
1038 | 1027 | ||
1039 | wl_list_remove(&cursor->image_surface_destroy.link); | 1028 | wl_list_remove(&cursor->image_surface_destroy.link); |
1029 | wl_list_remove(&cursor->hold_begin.link); | ||
1030 | wl_list_remove(&cursor->hold_end.link); | ||
1040 | wl_list_remove(&cursor->pinch_begin.link); | 1031 | wl_list_remove(&cursor->pinch_begin.link); |
1041 | wl_list_remove(&cursor->pinch_update.link); | 1032 | wl_list_remove(&cursor->pinch_update.link); |
1042 | wl_list_remove(&cursor->pinch_end.link); | 1033 | wl_list_remove(&cursor->pinch_end.link); |
@@ -1050,7 +1041,9 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { | |||
1050 | wl_list_remove(&cursor->frame.link); | 1041 | wl_list_remove(&cursor->frame.link); |
1051 | wl_list_remove(&cursor->touch_down.link); | 1042 | wl_list_remove(&cursor->touch_down.link); |
1052 | wl_list_remove(&cursor->touch_up.link); | 1043 | wl_list_remove(&cursor->touch_up.link); |
1044 | wl_list_remove(&cursor->touch_cancel.link); | ||
1053 | wl_list_remove(&cursor->touch_motion.link); | 1045 | wl_list_remove(&cursor->touch_motion.link); |
1046 | wl_list_remove(&cursor->touch_frame.link); | ||
1054 | wl_list_remove(&cursor->tool_axis.link); | 1047 | wl_list_remove(&cursor->tool_axis.link); |
1055 | wl_list_remove(&cursor->tool_tip.link); | 1048 | wl_list_remove(&cursor->tool_tip.link); |
1056 | wl_list_remove(&cursor->tool_button.link); | 1049 | wl_list_remove(&cursor->tool_button.link); |
@@ -1085,19 +1078,24 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
1085 | wl_list_init(&cursor->image_surface_destroy.link); | 1078 | wl_list_init(&cursor->image_surface_destroy.link); |
1086 | cursor->image_surface_destroy.notify = handle_image_surface_destroy; | 1079 | cursor->image_surface_destroy.notify = handle_image_surface_destroy; |
1087 | 1080 | ||
1088 | cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); | 1081 | wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin); |
1089 | cursor->pinch_begin.notify = handle_pointer_pinch_begin; | 1082 | cursor->hold_begin.notify = handle_pointer_hold_begin; |
1083 | wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end); | ||
1084 | cursor->hold_end.notify = handle_pointer_hold_end; | ||
1085 | |||
1090 | wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); | 1086 | wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); |
1091 | cursor->pinch_update.notify = handle_pointer_pinch_update; | 1087 | cursor->pinch_begin.notify = handle_pointer_pinch_begin; |
1092 | wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); | 1088 | wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); |
1093 | cursor->pinch_end.notify = handle_pointer_pinch_end; | 1089 | cursor->pinch_update.notify = handle_pointer_pinch_update; |
1094 | wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); | 1090 | wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); |
1095 | cursor->swipe_begin.notify = handle_pointer_swipe_begin; | 1091 | cursor->pinch_end.notify = handle_pointer_pinch_end; |
1092 | |||
1096 | wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); | 1093 | wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); |
1097 | cursor->swipe_update.notify = handle_pointer_swipe_update; | 1094 | cursor->swipe_begin.notify = handle_pointer_swipe_begin; |
1098 | wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); | 1095 | wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); |
1099 | cursor->swipe_end.notify = handle_pointer_swipe_end; | 1096 | cursor->swipe_update.notify = handle_pointer_swipe_update; |
1100 | wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); | 1097 | wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); |
1098 | cursor->swipe_end.notify = handle_pointer_swipe_end; | ||
1101 | 1099 | ||
1102 | // input events | 1100 | // input events |
1103 | wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); | 1101 | wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); |
@@ -1122,10 +1120,16 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
1122 | wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); | 1120 | wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); |
1123 | cursor->touch_up.notify = handle_touch_up; | 1121 | cursor->touch_up.notify = handle_touch_up; |
1124 | 1122 | ||
1123 | wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel); | ||
1124 | cursor->touch_cancel.notify = handle_touch_cancel; | ||
1125 | |||
1125 | wl_signal_add(&wlr_cursor->events.touch_motion, | 1126 | wl_signal_add(&wlr_cursor->events.touch_motion, |
1126 | &cursor->touch_motion); | 1127 | &cursor->touch_motion); |
1127 | cursor->touch_motion.notify = handle_touch_motion; | 1128 | cursor->touch_motion.notify = handle_touch_motion; |
1128 | 1129 | ||
1130 | wl_signal_add(&wlr_cursor->events.touch_frame, &cursor->touch_frame); | ||
1131 | cursor->touch_frame.notify = handle_touch_frame; | ||
1132 | |||
1129 | wl_signal_add(&wlr_cursor->events.tablet_tool_axis, | 1133 | wl_signal_add(&wlr_cursor->events.tablet_tool_axis, |
1130 | &cursor->tool_axis); | 1134 | &cursor->tool_axis); |
1131 | cursor->tool_axis.notify = handle_tool_axis; | 1135 | cursor->tool_axis.notify = handle_tool_axis; |
@@ -1170,8 +1174,8 @@ void cursor_warp_to_container(struct sway_cursor *cursor, | |||
1170 | return; | 1174 | return; |
1171 | } | 1175 | } |
1172 | 1176 | ||
1173 | double x = container->x + container->width / 2.0; | 1177 | double x = container->pending.x + container->pending.width / 2.0; |
1174 | double y = container->y + container->height / 2.0; | 1178 | double y = container->pending.y + container->pending.height / 2.0; |
1175 | 1179 | ||
1176 | wlr_cursor_warp(cursor->cursor, NULL, x, y); | 1180 | wlr_cursor_warp(cursor->cursor, NULL, x, y); |
1177 | cursor_unhide(cursor); | 1181 | cursor_unhide(cursor); |
@@ -1211,11 +1215,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) { | |||
1211 | // Get event code from name | 1215 | // Get event code from name |
1212 | int code = libevdev_event_code_from_name(EV_KEY, name); | 1216 | int code = libevdev_event_code_from_name(EV_KEY, name); |
1213 | if (code == -1) { | 1217 | if (code == -1) { |
1214 | size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; | 1218 | *error = format_str("Unknown event %s", name); |
1215 | *error = malloc(len); | ||
1216 | if (*error) { | ||
1217 | snprintf(*error, len, "Unknown event %s", name); | ||
1218 | } | ||
1219 | return 0; | 1219 | return 0; |
1220 | } | 1220 | } |
1221 | return code; | 1221 | return code; |
@@ -1237,13 +1237,8 @@ uint32_t get_mouse_bindcode(const char *name, char **error) { | |||
1237 | } | 1237 | } |
1238 | const char *event = libevdev_event_code_get_name(EV_KEY, code); | 1238 | const char *event = libevdev_event_code_get_name(EV_KEY, code); |
1239 | if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { | 1239 | if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { |
1240 | 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", |
1241 | code, event ? event : "(null)") + 1; | 1241 | code, event ? event : "(null)"); |
1242 | *error = malloc(len); | ||
1243 | if (*error) { | ||
1244 | snprintf(*error, len, "Event code %d (%s) is not a button", | ||
1245 | code, event ? event : "(null)"); | ||
1246 | } | ||
1247 | return 0; | 1242 | return 0; |
1248 | } | 1243 | } |
1249 | return code; | 1244 | return code; |
@@ -1276,16 +1271,19 @@ const char *get_mouse_button_name(uint32_t button) { | |||
1276 | static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { | 1271 | static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { |
1277 | struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; | 1272 | struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; |
1278 | 1273 | ||
1279 | if (constraint->current.committed & | 1274 | if (constraint->current.cursor_hint.enabled) { |
1280 | WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { | ||
1281 | double sx = constraint->current.cursor_hint.x; | 1275 | double sx = constraint->current.cursor_hint.x; |
1282 | double sy = constraint->current.cursor_hint.y; | 1276 | double sy = constraint->current.cursor_hint.y; |
1283 | 1277 | ||
1284 | 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 | |||
1285 | struct sway_container *con = view->container; | 1283 | struct sway_container *con = view->container; |
1286 | 1284 | ||
1287 | double lx = sx + con->content_x - view->geometry.x; | 1285 | double lx = sx + con->pending.content_x - view->geometry.x; |
1288 | double ly = sy + con->content_y - view->geometry.y; | 1286 | double ly = sy + con->pending.content_y - view->geometry.y; |
1289 | 1287 | ||
1290 | wlr_cursor_warp(cursor->cursor, NULL, lx, ly); | 1288 | wlr_cursor_warp(cursor->cursor, NULL, lx, ly); |
1291 | 1289 | ||
@@ -1332,12 +1330,9 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) { | |||
1332 | sway_constraint->destroy.notify = handle_constraint_destroy; | 1330 | sway_constraint->destroy.notify = handle_constraint_destroy; |
1333 | wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); | 1331 | wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); |
1334 | 1332 | ||
1335 | struct sway_node *focus = seat_get_focus(seat); | 1333 | struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; |
1336 | if (focus && focus->type == N_CONTAINER && focus->sway_container->view) { | 1334 | if (surface && surface == constraint->surface) { |
1337 | struct wlr_surface *surface = focus->sway_container->view->surface; | 1335 | sway_cursor_constrain(seat->cursor, constraint); |
1338 | if (surface == constraint->surface) { | ||
1339 | sway_cursor_constrain(seat->cursor, constraint); | ||
1340 | } | ||
1341 | } | 1336 | } |
1342 | } | 1337 | } |
1343 | 1338 | ||
@@ -1395,3 +1390,26 @@ void sway_cursor_constrain(struct sway_cursor *cursor, | |||
1395 | wl_signal_add(&constraint->surface->events.commit, | 1390 | wl_signal_add(&constraint->surface->events.commit, |
1396 | &cursor->constraint_commit); | 1391 | &cursor->constraint_commit); |
1397 | } | 1392 | } |
1393 | |||
1394 | void handle_request_set_cursor_shape(struct wl_listener *listener, void *data) { | ||
1395 | const struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; | ||
1396 | struct sway_seat *seat = event->seat_client->seat->data; | ||
1397 | |||
1398 | if (!seatop_allows_set_cursor(seat)) { | ||
1399 | return; | ||
1400 | } | ||
1401 | |||
1402 | struct wl_client *focused_client = NULL; | ||
1403 | struct wlr_surface *focused_surface = seat->wlr_seat->pointer_state.focused_surface; | ||
1404 | if (focused_surface != NULL) { | ||
1405 | focused_client = wl_resource_get_client(focused_surface->resource); | ||
1406 | } | ||
1407 | |||
1408 | // TODO: check cursor mode | ||
1409 | if (focused_client == NULL || event->seat_client->client != focused_client) { | ||
1410 | sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client"); | ||
1411 | return; | ||
1412 | } | ||
1413 | |||
1414 | cursor_set_image(seat->cursor, wlr_cursor_shape_v1_name(event->shape), focused_client); | ||
1415 | } | ||
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index f04a8ce0..248ca34e 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c | |||
@@ -1,12 +1,12 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <math.h> | 4 | #include <math.h> |
5 | #include <assert.h> | ||
6 | #include <wlr/config.h> | ||
6 | #include <wlr/backend/libinput.h> | 7 | #include <wlr/backend/libinput.h> |
7 | #include <wlr/types/wlr_cursor.h> | 8 | #include <wlr/types/wlr_cursor.h> |
8 | #include <wlr/types/wlr_keyboard_group.h> | 9 | #include <wlr/types/wlr_keyboard_group.h> |
9 | #include <wlr/types/wlr_input_inhibitor.h> | ||
10 | #include <wlr/types/wlr_virtual_keyboard_v1.h> | 10 | #include <wlr/types/wlr_virtual_keyboard_v1.h> |
11 | #include <wlr/types/wlr_virtual_pointer_v1.h> | 11 | #include <wlr/types/wlr_virtual_pointer_v1.h> |
12 | #include "sway/config.h" | 12 | #include "sway/config.h" |
@@ -22,6 +22,10 @@ | |||
22 | #include "list.h" | 22 | #include "list.h" |
23 | #include "log.h" | 23 | #include "log.h" |
24 | 24 | ||
25 | #if WLR_HAS_LIBINPUT_BACKEND | ||
26 | #include <wlr/backend/libinput.h> | ||
27 | #endif | ||
28 | |||
25 | #define DEFAULT_SEAT "seat0" | 29 | #define DEFAULT_SEAT "seat0" |
26 | 30 | ||
27 | struct input_config *current_input_config = NULL; | 31 | struct input_config *current_input_config = NULL; |
@@ -63,8 +67,15 @@ struct sway_seat *input_manager_sway_seat_from_wlr_seat(struct wlr_seat *wlr_sea | |||
63 | } | 67 | } |
64 | 68 | ||
65 | char *input_device_get_identifier(struct wlr_input_device *device) { | 69 | char *input_device_get_identifier(struct wlr_input_device *device) { |
66 | int vendor = device->vendor; | 70 | int vendor = 0, product = 0; |
67 | int product = device->product; | 71 | #if WLR_HAS_LIBINPUT_BACKEND |
72 | if (wlr_input_device_is_libinput(device)) { | ||
73 | struct libinput_device *libinput_dev = wlr_libinput_get_device_handle(device); | ||
74 | vendor = libinput_device_get_id_vendor(libinput_dev); | ||
75 | product = libinput_device_get_id_product(libinput_dev); | ||
76 | } | ||
77 | #endif | ||
78 | |||
68 | char *name = strdup(device->name ? device->name : ""); | 79 | char *name = strdup(device->name ? device->name : ""); |
69 | strip_whitespace(name); | 80 | strip_whitespace(name); |
70 | 81 | ||
@@ -76,20 +87,13 @@ char *input_device_get_identifier(struct wlr_input_device *device) { | |||
76 | } | 87 | } |
77 | } | 88 | } |
78 | 89 | ||
79 | const char *fmt = "%d:%d:%s"; | 90 | char *identifier = format_str("%d:%d:%s", vendor, product, name); |
80 | int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; | ||
81 | char *identifier = malloc(len); | ||
82 | if (!identifier) { | ||
83 | sway_log(SWAY_ERROR, "Unable to allocate unique input device name"); | ||
84 | return NULL; | ||
85 | } | ||
86 | |||
87 | snprintf(identifier, len, fmt, vendor, product, name); | ||
88 | free(name); | 91 | free(name); |
89 | return identifier; | 92 | return identifier; |
90 | } | 93 | } |
91 | 94 | ||
92 | static bool device_is_touchpad(struct sway_input_device *device) { | 95 | static bool device_is_touchpad(struct sway_input_device *device) { |
96 | #if WLR_HAS_LIBINPUT_BACKEND | ||
93 | if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || | 97 | if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || |
94 | !wlr_input_device_is_libinput(device->wlr_device)) { | 98 | !wlr_input_device_is_libinput(device->wlr_device)) { |
95 | return false; | 99 | return false; |
@@ -99,6 +103,9 @@ static bool device_is_touchpad(struct sway_input_device *device) { | |||
99 | wlr_libinput_get_device_handle(device->wlr_device); | 103 | wlr_libinput_get_device_handle(device->wlr_device); |
100 | 104 | ||
101 | return libinput_device_config_tap_get_finger_count(libinput_device) > 0; | 105 | return libinput_device_config_tap_get_finger_count(libinput_device) > 0; |
106 | #else | ||
107 | return false; | ||
108 | #endif | ||
102 | } | 109 | } |
103 | 110 | ||
104 | const char *input_device_get_type(struct sway_input_device *device) { | 111 | const char *input_device_get_type(struct sway_input_device *device) { |
@@ -113,7 +120,7 @@ const char *input_device_get_type(struct sway_input_device *device) { | |||
113 | return "keyboard"; | 120 | return "keyboard"; |
114 | case WLR_INPUT_DEVICE_TOUCH: | 121 | case WLR_INPUT_DEVICE_TOUCH: |
115 | return "touch"; | 122 | return "touch"; |
116 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 123 | case WLR_INPUT_DEVICE_TABLET: |
117 | return "tablet_tool"; | 124 | return "tablet_tool"; |
118 | case WLR_INPUT_DEVICE_TABLET_PAD: | 125 | case WLR_INPUT_DEVICE_TABLET_PAD: |
119 | return "tablet_pad"; | 126 | return "tablet_pad"; |
@@ -236,7 +243,11 @@ static void handle_new_input(struct wl_listener *listener, void *data) { | |||
236 | 243 | ||
237 | apply_input_type_config(input_device); | 244 | apply_input_type_config(input_device); |
238 | 245 | ||
239 | sway_input_configure_libinput_device(input_device); | 246 | #if WLR_HAS_LIBINPUT_BACKEND |
247 | bool config_changed = sway_input_configure_libinput_device(input_device); | ||
248 | #else | ||
249 | bool config_changed = false; | ||
250 | #endif | ||
240 | 251 | ||
241 | wl_signal_add(&device->events.destroy, &input_device->device_destroy); | 252 | wl_signal_add(&device->events.destroy, &input_device->device_destroy); |
242 | input_device->device_destroy.notify = handle_device_destroy; | 253 | input_device->device_destroy.notify = handle_device_destroy; |
@@ -274,29 +285,9 @@ static void handle_new_input(struct wl_listener *listener, void *data) { | |||
274 | } | 285 | } |
275 | 286 | ||
276 | ipc_event_input("added", input_device); | 287 | ipc_event_input("added", input_device); |
277 | } | ||
278 | 288 | ||
279 | static void handle_inhibit_activate(struct wl_listener *listener, void *data) { | 289 | if (config_changed) { |
280 | struct sway_input_manager *input_manager = wl_container_of( | 290 | ipc_event_input("libinput_config", input_device); |
281 | listener, input_manager, inhibit_activate); | ||
282 | struct sway_seat *seat; | ||
283 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
284 | seat_set_exclusive_client(seat, input_manager->inhibit->active_client); | ||
285 | } | ||
286 | } | ||
287 | |||
288 | static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) { | ||
289 | struct sway_input_manager *input_manager = wl_container_of( | ||
290 | listener, input_manager, inhibit_deactivate); | ||
291 | struct sway_seat *seat; | ||
292 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
293 | seat_set_exclusive_client(seat, NULL); | ||
294 | struct sway_node *previous = seat_get_focus(seat); | ||
295 | if (previous) { | ||
296 | // Hack to get seat to re-focus the return value of get_focus | ||
297 | seat_set_focus(seat, NULL); | ||
298 | seat_set_focus(seat, previous); | ||
299 | } | ||
300 | } | 291 | } |
301 | } | 292 | } |
302 | 293 | ||
@@ -377,7 +368,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) { | |||
377 | struct sway_input_manager *input_manager = | 368 | struct sway_input_manager *input_manager = |
378 | wl_container_of(listener, input_manager, virtual_keyboard_new); | 369 | wl_container_of(listener, input_manager, virtual_keyboard_new); |
379 | struct wlr_virtual_keyboard_v1 *keyboard = data; | 370 | struct wlr_virtual_keyboard_v1 *keyboard = data; |
380 | struct wlr_input_device *device = &keyboard->input_device; | 371 | struct wlr_input_device *device = &keyboard->keyboard.base; |
381 | 372 | ||
382 | // TODO: Amend protocol to allow NULL seat | 373 | // TODO: Amend protocol to allow NULL seat |
383 | struct sway_seat *seat = keyboard->seat ? | 374 | struct sway_seat *seat = keyboard->seat ? |
@@ -410,7 +401,7 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) { | |||
410 | wl_container_of(listener, input_manager, virtual_pointer_new); | 401 | wl_container_of(listener, input_manager, virtual_pointer_new); |
411 | struct wlr_virtual_pointer_v1_new_pointer_event *event = data; | 402 | struct wlr_virtual_pointer_v1_new_pointer_event *event = data; |
412 | struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; | 403 | struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; |
413 | struct wlr_input_device *device = &pointer->input_device; | 404 | struct wlr_input_device *device = &pointer->pointer.base; |
414 | 405 | ||
415 | struct sway_seat *seat = event->suggested_seat ? | 406 | struct sway_seat *seat = event->suggested_seat ? |
416 | input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : | 407 | input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : |
@@ -442,6 +433,20 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) { | |||
442 | } | 433 | } |
443 | } | 434 | } |
444 | 435 | ||
436 | static void handle_transient_seat_manager_create_seat( | ||
437 | struct wl_listener *listener, void *data) { | ||
438 | struct wlr_transient_seat_v1 *transient_seat = data; | ||
439 | static uint64_t i; | ||
440 | char name[256]; | ||
441 | snprintf(name, sizeof(name), "transient-%"PRIx64, i++); | ||
442 | struct sway_seat *seat = seat_create(name); | ||
443 | if (seat && seat->wlr_seat) { | ||
444 | wlr_transient_seat_v1_ready(transient_seat, seat->wlr_seat); | ||
445 | } else { | ||
446 | wlr_transient_seat_v1_deny(transient_seat); | ||
447 | } | ||
448 | } | ||
449 | |||
445 | struct sway_input_manager *input_manager_create(struct sway_server *server) { | 450 | struct sway_input_manager *input_manager_create(struct sway_server *server) { |
446 | struct sway_input_manager *input = | 451 | struct sway_input_manager *input = |
447 | calloc(1, sizeof(struct sway_input_manager)); | 452 | calloc(1, sizeof(struct sway_input_manager)); |
@@ -468,14 +473,6 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { | |||
468 | &input->virtual_pointer_new); | 473 | &input->virtual_pointer_new); |
469 | input->virtual_pointer_new.notify = handle_virtual_pointer; | 474 | input->virtual_pointer_new.notify = handle_virtual_pointer; |
470 | 475 | ||
471 | input->inhibit = wlr_input_inhibit_manager_create(server->wl_display); | ||
472 | input->inhibit_activate.notify = handle_inhibit_activate; | ||
473 | wl_signal_add(&input->inhibit->events.activate, | ||
474 | &input->inhibit_activate); | ||
475 | input->inhibit_deactivate.notify = handle_inhibit_deactivate; | ||
476 | wl_signal_add(&input->inhibit->events.deactivate, | ||
477 | &input->inhibit_deactivate); | ||
478 | |||
479 | input->keyboard_shortcuts_inhibit = | 476 | input->keyboard_shortcuts_inhibit = |
480 | wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); | 477 | wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); |
481 | input->keyboard_shortcuts_inhibit_new_inhibitor.notify = | 478 | input->keyboard_shortcuts_inhibit_new_inhibitor.notify = |
@@ -483,6 +480,17 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { | |||
483 | wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, | 480 | wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, |
484 | &input->keyboard_shortcuts_inhibit_new_inhibitor); | 481 | &input->keyboard_shortcuts_inhibit_new_inhibitor); |
485 | 482 | ||
483 | input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display); | ||
484 | |||
485 | input->transient_seat_manager = | ||
486 | wlr_transient_seat_manager_v1_create(server->wl_display); | ||
487 | assert(input->transient_seat_manager); | ||
488 | |||
489 | input->transient_seat_create.notify = | ||
490 | handle_transient_seat_manager_create_seat; | ||
491 | wl_signal_add(&input->transient_seat_manager->events.create_seat, | ||
492 | &input->transient_seat_create); | ||
493 | |||
486 | return input; | 494 | return input; |
487 | } | 495 | } |
488 | 496 | ||
@@ -520,21 +528,50 @@ static void retranslate_keysyms(struct input_config *input_config) { | |||
520 | return; | 528 | return; |
521 | } | 529 | } |
522 | } | 530 | } |
531 | |||
532 | for (int i = 0; i < config->input_type_configs->length; ++i) { | ||
533 | struct input_config *ic = config->input_type_configs->items[i]; | ||
534 | if (ic->xkb_layout || ic->xkb_file) { | ||
535 | // this is the first config with xkb_layout or xkb_file | ||
536 | if (ic->identifier == input_config->identifier) { | ||
537 | translate_keysyms(ic); | ||
538 | } | ||
539 | |||
540 | return; | ||
541 | } | ||
542 | } | ||
523 | } | 543 | } |
524 | 544 | ||
525 | static void input_manager_configure_input( | 545 | static void input_manager_configure_input( |
526 | struct sway_input_device *input_device) { | 546 | struct sway_input_device *input_device) { |
527 | sway_input_configure_libinput_device(input_device); | 547 | #if WLR_HAS_LIBINPUT_BACKEND |
548 | bool config_changed = sway_input_configure_libinput_device(input_device); | ||
549 | #else | ||
550 | bool config_changed = false; | ||
551 | #endif | ||
528 | struct sway_seat *seat = NULL; | 552 | struct sway_seat *seat = NULL; |
529 | wl_list_for_each(seat, &server.input->seats, link) { | 553 | wl_list_for_each(seat, &server.input->seats, link) { |
530 | seat_configure_device(seat, input_device); | 554 | seat_configure_device(seat, input_device); |
531 | } | 555 | } |
556 | if (config_changed) { | ||
557 | ipc_event_input("libinput_config", input_device); | ||
558 | } | ||
532 | } | 559 | } |
533 | 560 | ||
534 | void input_manager_configure_all_inputs(void) { | 561 | void input_manager_configure_all_input_mappings(void) { |
535 | struct sway_input_device *input_device = NULL; | 562 | struct sway_input_device *input_device; |
536 | wl_list_for_each(input_device, &server.input->devices, link) { | 563 | wl_list_for_each(input_device, &server.input->devices, link) { |
537 | input_manager_configure_input(input_device); | 564 | struct sway_seat *seat; |
565 | wl_list_for_each(seat, &server.input->seats, link) { | ||
566 | seat_configure_device_mapping(seat, input_device); | ||
567 | } | ||
568 | |||
569 | #if WLR_HAS_LIBINPUT_BACKEND | ||
570 | // Input devices mapped to unavailable outputs get their libinput | ||
571 | // send_events setting switched to false. We need to re-enable this | ||
572 | // when the output appears. | ||
573 | sway_input_configure_libinput_device_send_events(input_device); | ||
574 | #endif | ||
538 | } | 575 | } |
539 | } | 576 | } |
540 | 577 | ||
@@ -556,7 +593,9 @@ void input_manager_apply_input_config(struct input_config *input_config) { | |||
556 | } | 593 | } |
557 | 594 | ||
558 | void input_manager_reset_input(struct sway_input_device *input_device) { | 595 | void input_manager_reset_input(struct sway_input_device *input_device) { |
596 | #if WLR_HAS_LIBINPUT_BACKEND | ||
559 | sway_input_reset_libinput_device(input_device); | 597 | sway_input_reset_libinput_device(input_device); |
598 | #endif | ||
560 | struct sway_seat *seat = NULL; | 599 | struct sway_seat *seat = NULL; |
561 | wl_list_for_each(seat, &server.input->seats, link) { | 600 | wl_list_for_each(seat, &server.input->seats, link) { |
562 | seat_reset_device(seat, input_device); | 601 | seat_reset_device(seat, input_device); |
@@ -564,6 +603,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) { | |||
564 | } | 603 | } |
565 | 604 | ||
566 | void input_manager_reset_all_inputs(void) { | 605 | void input_manager_reset_all_inputs(void) { |
606 | // Set the active keyboard to NULL to avoid spamming configuration updates | ||
607 | // for all keyboard devices. | ||
608 | struct sway_seat *seat; | ||
609 | wl_list_for_each(seat, &server.input->seats, link) { | ||
610 | wlr_seat_set_keyboard(seat->wlr_seat, NULL); | ||
611 | } | ||
612 | |||
567 | struct sway_input_device *input_device = NULL; | 613 | struct sway_input_device *input_device = NULL; |
568 | wl_list_for_each(input_device, &server.input->devices, link) { | 614 | wl_list_for_each(input_device, &server.input->devices, link) { |
569 | input_manager_reset_input(input_device); | 615 | input_manager_reset_input(input_device); |
@@ -572,7 +618,6 @@ void input_manager_reset_all_inputs(void) { | |||
572 | // If there is at least one keyboard using the default keymap, repeat delay, | 618 | // If there is at least one keyboard using the default keymap, repeat delay, |
573 | // and repeat rate, then it is possible that there is a keyboard group that | 619 | // and repeat rate, then it is possible that there is a keyboard group that |
574 | // need their keyboard disarmed. | 620 | // need their keyboard disarmed. |
575 | struct sway_seat *seat; | ||
576 | wl_list_for_each(seat, &server.input->seats, link) { | 621 | wl_list_for_each(seat, &server.input->seats, link) { |
577 | struct sway_keyboard_group *group; | 622 | struct sway_keyboard_group *group; |
578 | wl_list_for_each(group, &seat->keyboard_groups, link) { | 623 | wl_list_for_each(group, &seat->keyboard_groups, link) { |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index ce259eb2..f74d0658 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -1,15 +1,13 @@ | |||
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> |
11 | #include "sway/commands.h" | 10 | #include "sway/commands.h" |
12 | #include "sway/desktop/transaction.h" | ||
13 | #include "sway/input/input-manager.h" | 11 | #include "sway/input/input-manager.h" |
14 | #include "sway/input/keyboard.h" | 12 | #include "sway/input/keyboard.h" |
15 | #include "sway/input/seat.h" | 13 | #include "sway/input/seat.h" |
@@ -17,6 +15,10 @@ | |||
17 | #include "sway/ipc-server.h" | 15 | #include "sway/ipc-server.h" |
18 | #include "log.h" | 16 | #include "log.h" |
19 | 17 | ||
18 | #if WLR_HAS_SESSION | ||
19 | #include <wlr/backend/session.h> | ||
20 | #endif | ||
21 | |||
20 | static struct modifier_key { | 22 | static struct modifier_key { |
21 | char *name; | 23 | char *name; |
22 | uint32_t mod; | 24 | uint32_t mod; |
@@ -30,6 +32,7 @@ static struct modifier_key { | |||
30 | { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, | 32 | { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, |
31 | { "Mod3", WLR_MODIFIER_MOD3 }, | 33 | { "Mod3", WLR_MODIFIER_MOD3 }, |
32 | { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, | 34 | { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, |
35 | { "Super", WLR_MODIFIER_LOGO }, | ||
33 | { "Mod5", WLR_MODIFIER_MOD5 }, | 36 | { "Mod5", WLR_MODIFIER_MOD5 }, |
34 | }; | 37 | }; |
35 | 38 | ||
@@ -265,14 +268,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | |||
265 | xkb_keysym_t keysym = pressed_keysyms[i]; | 268 | xkb_keysym_t keysym = pressed_keysyms[i]; |
266 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && | 269 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && |
267 | keysym <= XKB_KEY_XF86Switch_VT_12) { | 270 | keysym <= XKB_KEY_XF86Switch_VT_12) { |
268 | if (wlr_backend_is_multi(server.backend)) { | 271 | #if WLR_HAS_SESSION |
269 | struct wlr_session *session = | 272 | if (server.session) { |
270 | wlr_backend_get_session(server.backend); | 273 | unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; |
271 | if (session) { | 274 | wlr_session_change_vt(server.session, vt); |
272 | unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; | ||
273 | wlr_session_change_vt(session, vt); | ||
274 | } | ||
275 | } | 275 | } |
276 | #endif | ||
276 | return true; | 277 | return true; |
277 | } | 278 | } |
278 | } | 279 | } |
@@ -292,14 +293,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | |||
292 | static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, | 293 | static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, |
293 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | 294 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, |
294 | uint32_t *modifiers) { | 295 | uint32_t *modifiers) { |
295 | struct wlr_input_device *device = | 296 | *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
296 | keyboard->seat_device->input_device->wlr_device; | ||
297 | *modifiers = wlr_keyboard_get_modifiers(device->keyboard); | ||
298 | xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( | 297 | xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( |
299 | device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); | 298 | keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); |
300 | *modifiers = *modifiers & ~consumed; | 299 | *modifiers = *modifiers & ~consumed; |
301 | 300 | ||
302 | return xkb_state_key_get_syms(device->keyboard->xkb_state, | 301 | return xkb_state_key_get_syms(keyboard->wlr->xkb_state, |
303 | keycode, keysyms); | 302 | keycode, keysyms); |
304 | } | 303 | } |
305 | 304 | ||
@@ -315,13 +314,11 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, | |||
315 | static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, | 314 | static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, |
316 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | 315 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, |
317 | uint32_t *modifiers) { | 316 | uint32_t *modifiers) { |
318 | struct wlr_input_device *device = | 317 | *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
319 | keyboard->seat_device->input_device->wlr_device; | ||
320 | *modifiers = wlr_keyboard_get_modifiers(device->keyboard); | ||
321 | 318 | ||
322 | xkb_layout_index_t layout_index = xkb_state_key_get_layout( | 319 | xkb_layout_index_t layout_index = xkb_state_key_get_layout( |
323 | device->keyboard->xkb_state, keycode); | 320 | keyboard->wlr->xkb_state, keycode); |
324 | return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, | 321 | return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap, |
325 | keycode, layout_index, 0, keysyms); | 322 | keycode, layout_index, 0, keysyms); |
326 | } | 323 | } |
327 | 324 | ||
@@ -361,8 +358,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard, | |||
361 | keyinfo->keycode, &keyinfo->translated_keysyms, | 358 | keyinfo->keycode, &keyinfo->translated_keysyms, |
362 | &keyinfo->translated_modifiers); | 359 | &keyinfo->translated_modifiers); |
363 | 360 | ||
364 | keyinfo->code_modifiers = wlr_keyboard_get_modifiers( | 361 | keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
365 | keyboard->seat_device->input_device->wlr_device->keyboard); | ||
366 | 362 | ||
367 | // Update shortcut model keyinfo | 363 | // Update shortcut model keyinfo |
368 | update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, | 364 | update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, |
@@ -379,16 +375,38 @@ static void update_keyboard_state(struct sway_keyboard *keyboard, | |||
379 | } | 375 | } |
380 | } | 376 | } |
381 | 377 | ||
378 | /** | ||
379 | * Get keyboard grab of the seat from sway_keyboard if we should forward events | ||
380 | * to it. | ||
381 | * | ||
382 | * Returns NULL if the keyboard is not grabbed by an input method, | ||
383 | * or if event is from virtual keyboard of the same client as grab. | ||
384 | * TODO: see swaywm/wlroots#2322 | ||
385 | */ | ||
386 | static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab( | ||
387 | struct sway_keyboard *keyboard) { | ||
388 | struct wlr_input_method_v2 *input_method = keyboard->seat_device-> | ||
389 | sway_seat->im_relay.input_method; | ||
390 | struct wlr_virtual_keyboard_v1 *virtual_keyboard = | ||
391 | wlr_input_device_get_virtual_keyboard(keyboard->seat_device->input_device->wlr_device); | ||
392 | if (!input_method || !input_method->keyboard_grab || (virtual_keyboard && | ||
393 | wl_resource_get_client(virtual_keyboard->resource) == | ||
394 | wl_resource_get_client(input_method->keyboard_grab->resource))) { | ||
395 | return NULL; | ||
396 | } | ||
397 | return input_method->keyboard_grab; | ||
398 | } | ||
399 | |||
382 | static void handle_key_event(struct sway_keyboard *keyboard, | 400 | static void handle_key_event(struct sway_keyboard *keyboard, |
383 | struct wlr_event_keyboard_key *event) { | 401 | struct wlr_keyboard_key_event *event) { |
384 | struct sway_seat *seat = keyboard->seat_device->sway_seat; | 402 | struct sway_seat *seat = keyboard->seat_device->sway_seat; |
385 | struct wlr_seat *wlr_seat = seat->wlr_seat; | 403 | struct wlr_seat *wlr_seat = seat->wlr_seat; |
386 | struct wlr_input_device *wlr_device = | 404 | struct wlr_input_device *wlr_device = |
387 | keyboard->seat_device->input_device->wlr_device; | 405 | keyboard->seat_device->input_device->wlr_device; |
388 | char *device_identifier = input_device_get_identifier(wlr_device); | 406 | char *device_identifier = input_device_get_identifier(wlr_device); |
389 | bool exact_identifier = wlr_device->keyboard->group != NULL; | 407 | bool exact_identifier = keyboard->wlr->group != NULL; |
390 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); | 408 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); |
391 | bool input_inhibited = seat->exclusive_client != NULL; | 409 | bool locked = server.session_lock.lock; |
392 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = | 410 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = |
393 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); | 411 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); |
394 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; | 412 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; |
@@ -406,17 +424,17 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
406 | struct sway_binding *binding_released = NULL; | 424 | struct sway_binding *binding_released = NULL; |
407 | get_active_binding(&keyboard->state_keycodes, | 425 | get_active_binding(&keyboard->state_keycodes, |
408 | config->current_mode->keycode_bindings, &binding_released, | 426 | config->current_mode->keycode_bindings, &binding_released, |
409 | keyinfo.code_modifiers, true, input_inhibited, | 427 | keyinfo.code_modifiers, true, locked, |
410 | shortcuts_inhibited, device_identifier, | 428 | shortcuts_inhibited, device_identifier, |
411 | exact_identifier, keyboard->effective_layout); | 429 | exact_identifier, keyboard->effective_layout); |
412 | get_active_binding(&keyboard->state_keysyms_raw, | 430 | get_active_binding(&keyboard->state_keysyms_raw, |
413 | config->current_mode->keysym_bindings, &binding_released, | 431 | config->current_mode->keysym_bindings, &binding_released, |
414 | keyinfo.raw_modifiers, true, input_inhibited, | 432 | keyinfo.raw_modifiers, true, locked, |
415 | shortcuts_inhibited, device_identifier, | 433 | shortcuts_inhibited, device_identifier, |
416 | exact_identifier, keyboard->effective_layout); | 434 | exact_identifier, keyboard->effective_layout); |
417 | get_active_binding(&keyboard->state_keysyms_translated, | 435 | get_active_binding(&keyboard->state_keysyms_translated, |
418 | config->current_mode->keysym_bindings, &binding_released, | 436 | config->current_mode->keysym_bindings, &binding_released, |
419 | keyinfo.translated_modifiers, true, input_inhibited, | 437 | keyinfo.translated_modifiers, true, locked, |
420 | shortcuts_inhibited, device_identifier, | 438 | shortcuts_inhibited, device_identifier, |
421 | exact_identifier, keyboard->effective_layout); | 439 | exact_identifier, keyboard->effective_layout); |
422 | 440 | ||
@@ -438,17 +456,17 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
438 | if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { | 456 | if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { |
439 | get_active_binding(&keyboard->state_keycodes, | 457 | get_active_binding(&keyboard->state_keycodes, |
440 | config->current_mode->keycode_bindings, &binding, | 458 | config->current_mode->keycode_bindings, &binding, |
441 | keyinfo.code_modifiers, false, input_inhibited, | 459 | keyinfo.code_modifiers, false, locked, |
442 | shortcuts_inhibited, device_identifier, | 460 | shortcuts_inhibited, device_identifier, |
443 | exact_identifier, keyboard->effective_layout); | 461 | exact_identifier, keyboard->effective_layout); |
444 | get_active_binding(&keyboard->state_keysyms_raw, | 462 | get_active_binding(&keyboard->state_keysyms_raw, |
445 | config->current_mode->keysym_bindings, &binding, | 463 | config->current_mode->keysym_bindings, &binding, |
446 | keyinfo.raw_modifiers, false, input_inhibited, | 464 | keyinfo.raw_modifiers, false, locked, |
447 | shortcuts_inhibited, device_identifier, | 465 | shortcuts_inhibited, device_identifier, |
448 | exact_identifier, keyboard->effective_layout); | 466 | exact_identifier, keyboard->effective_layout); |
449 | get_active_binding(&keyboard->state_keysyms_translated, | 467 | get_active_binding(&keyboard->state_keysyms_translated, |
450 | config->current_mode->keysym_bindings, &binding, | 468 | config->current_mode->keysym_bindings, &binding, |
451 | keyinfo.translated_modifiers, false, input_inhibited, | 469 | keyinfo.translated_modifiers, false, locked, |
452 | shortcuts_inhibited, device_identifier, | 470 | shortcuts_inhibited, device_identifier, |
453 | exact_identifier, keyboard->effective_layout); | 471 | exact_identifier, keyboard->effective_layout); |
454 | } | 472 | } |
@@ -456,10 +474,10 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
456 | // Set up (or clear) keyboard repeat for a pressed binding. Since the | 474 | // Set up (or clear) keyboard repeat for a pressed binding. Since the |
457 | // binding may remove the keyboard, the timer needs to be updated first | 475 | // binding may remove the keyboard, the timer needs to be updated first |
458 | if (binding && !(binding->flags & BINDING_NOREPEAT) && | 476 | if (binding && !(binding->flags & BINDING_NOREPEAT) && |
459 | wlr_device->keyboard->repeat_info.delay > 0) { | 477 | keyboard->wlr->repeat_info.delay > 0) { |
460 | keyboard->repeat_binding = binding; | 478 | keyboard->repeat_binding = binding; |
461 | if (wl_event_source_timer_update(keyboard->key_repeat_source, | 479 | if (wl_event_source_timer_update(keyboard->key_repeat_source, |
462 | wlr_device->keyboard->repeat_info.delay) < 0) { | 480 | keyboard->wlr->repeat_info.delay) < 0) { |
463 | sway_log(SWAY_DEBUG, "failed to set key repeat timer"); | 481 | sway_log(SWAY_DEBUG, "failed to set key repeat timer"); |
464 | } | 482 | } |
465 | } else if (keyboard->repeat_binding) { | 483 | } else if (keyboard->repeat_binding) { |
@@ -471,7 +489,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
471 | handled = true; | 489 | handled = true; |
472 | } | 490 | } |
473 | 491 | ||
474 | if (!handled && wlr_device->keyboard->group) { | 492 | if (!handled && keyboard->wlr->group) { |
475 | // Only handle device specific bindings for keyboards in a group | 493 | // Only handle device specific bindings for keyboards in a group |
476 | free(device_identifier); | 494 | free(device_identifier); |
477 | return; | 495 | return; |
@@ -489,18 +507,41 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
489 | keyinfo.raw_keysyms_len); | 507 | keyinfo.raw_keysyms_len); |
490 | } | 508 | } |
491 | 509 | ||
492 | if (!handled || event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { | 510 | if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { |
511 | // If the pressed event was sent to a client, also send the released | ||
512 | // event. In particular, don't send the released event to the IM grab. | ||
493 | bool pressed_sent = update_shortcut_state( | 513 | bool pressed_sent = update_shortcut_state( |
494 | &keyboard->state_pressed_sent, event->keycode, event->state, | 514 | &keyboard->state_pressed_sent, event->keycode, |
495 | keyinfo.keycode, 0); | 515 | event->state, keyinfo.keycode, 0); |
496 | if (pressed_sent || event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { | 516 | if (pressed_sent) { |
497 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | 517 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); |
498 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | 518 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, |
499 | event->keycode, event->state); | 519 | event->keycode, event->state); |
520 | handled = true; | ||
521 | } | ||
522 | } | ||
523 | |||
524 | if (!handled) { | ||
525 | struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); | ||
526 | |||
527 | if (kb_grab) { | ||
528 | wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); | ||
529 | wlr_input_method_keyboard_grab_v2_send_key(kb_grab, | ||
530 | event->time_msec, event->keycode, event->state); | ||
531 | handled = true; | ||
500 | } | 532 | } |
501 | } | 533 | } |
502 | 534 | ||
503 | transaction_commit_dirty(); | 535 | if (!handled && event->state != WL_KEYBOARD_KEY_STATE_RELEASED) { |
536 | // If a released event failed pressed sent test, and not in sent to | ||
537 | // keyboard grab, it is still not handled. Don't handle released here. | ||
538 | update_shortcut_state( | ||
539 | &keyboard->state_pressed_sent, event->keycode, event->state, | ||
540 | keyinfo.keycode, 0); | ||
541 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); | ||
542 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | ||
543 | event->keycode, event->state); | ||
544 | } | ||
504 | 545 | ||
505 | free(device_identifier); | 546 | free(device_identifier); |
506 | } | 547 | } |
@@ -573,21 +614,18 @@ static void handle_keyboard_group_leave(struct wl_listener *listener, | |||
573 | } | 614 | } |
574 | 615 | ||
575 | static int handle_keyboard_repeat(void *data) { | 616 | static int handle_keyboard_repeat(void *data) { |
576 | struct sway_keyboard *keyboard = (struct sway_keyboard *)data; | 617 | struct sway_keyboard *keyboard = data; |
577 | struct wlr_keyboard *wlr_device = | ||
578 | keyboard->seat_device->input_device->wlr_device->keyboard; | ||
579 | if (keyboard->repeat_binding) { | 618 | if (keyboard->repeat_binding) { |
580 | if (wlr_device->repeat_info.rate > 0) { | 619 | if (keyboard->wlr->repeat_info.rate > 0) { |
581 | // We queue the next event first, as the command might cancel it | 620 | // We queue the next event first, as the command might cancel it |
582 | if (wl_event_source_timer_update(keyboard->key_repeat_source, | 621 | if (wl_event_source_timer_update(keyboard->key_repeat_source, |
583 | 1000 / wlr_device->repeat_info.rate) < 0) { | 622 | 1000 / keyboard->wlr->repeat_info.rate) < 0) { |
584 | sway_log(SWAY_DEBUG, "failed to update key repeat timer"); | 623 | sway_log(SWAY_DEBUG, "failed to update key repeat timer"); |
585 | } | 624 | } |
586 | } | 625 | } |
587 | 626 | ||
588 | seat_execute_command(keyboard->seat_device->sway_seat, | 627 | seat_execute_command(keyboard->seat_device->sway_seat, |
589 | keyboard->repeat_binding); | 628 | keyboard->repeat_binding); |
590 | transaction_commit_dirty(); | ||
591 | } | 629 | } |
592 | return 0; | 630 | return 0; |
593 | } | 631 | } |
@@ -614,22 +652,28 @@ static void determine_bar_visibility(uint32_t modifiers) { | |||
614 | } | 652 | } |
615 | 653 | ||
616 | static void handle_modifier_event(struct sway_keyboard *keyboard) { | 654 | static void handle_modifier_event(struct sway_keyboard *keyboard) { |
617 | struct wlr_input_device *wlr_device = | 655 | if (!keyboard->wlr->group) { |
618 | keyboard->seat_device->input_device->wlr_device; | 656 | struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); |
619 | if (!wlr_device->keyboard->group) { | ||
620 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | ||
621 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | ||
622 | wlr_seat_keyboard_notify_modifiers(wlr_seat, | ||
623 | &wlr_device->keyboard->modifiers); | ||
624 | 657 | ||
625 | uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); | 658 | if (kb_grab) { |
659 | wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); | ||
660 | wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, | ||
661 | &keyboard->wlr->modifiers); | ||
662 | } else { | ||
663 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | ||
664 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); | ||
665 | wlr_seat_keyboard_notify_modifiers(wlr_seat, | ||
666 | &keyboard->wlr->modifiers); | ||
667 | } | ||
668 | |||
669 | uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); | ||
626 | determine_bar_visibility(modifiers); | 670 | determine_bar_visibility(modifiers); |
627 | } | 671 | } |
628 | 672 | ||
629 | if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { | 673 | if (keyboard->wlr->modifiers.group != keyboard->effective_layout) { |
630 | keyboard->effective_layout = wlr_device->keyboard->modifiers.group; | 674 | keyboard->effective_layout = keyboard->wlr->modifiers.group; |
631 | 675 | ||
632 | if (!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { | 676 | if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) { |
633 | ipc_event_input("xkb_layout", keyboard->seat_device->input_device); | 677 | ipc_event_input("xkb_layout", keyboard->seat_device->input_device); |
634 | } | 678 | } |
635 | } | 679 | } |
@@ -658,6 +702,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | |||
658 | } | 702 | } |
659 | 703 | ||
660 | keyboard->seat_device = device; | 704 | keyboard->seat_device = device; |
705 | keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device); | ||
661 | device->keyboard = keyboard; | 706 | device->keyboard = keyboard; |
662 | 707 | ||
663 | wl_list_init(&keyboard->keyboard_key.link); | 708 | wl_list_init(&keyboard->keyboard_key.link); |
@@ -671,23 +716,11 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | |||
671 | 716 | ||
672 | static void handle_xkb_context_log(struct xkb_context *context, | 717 | static void handle_xkb_context_log(struct xkb_context *context, |
673 | enum xkb_log_level level, const char *format, va_list args) { | 718 | enum xkb_log_level level, const char *format, va_list args) { |
674 | va_list args_copy; | 719 | char *error = vformat_str(format, args); |
675 | va_copy(args_copy, args); | ||
676 | size_t length = vsnprintf(NULL, 0, format, args_copy) + 1; | ||
677 | va_end(args_copy); | ||
678 | |||
679 | char *error = malloc(length); | ||
680 | if (!error) { | ||
681 | sway_log(SWAY_ERROR, "Failed to allocate libxkbcommon log message"); | ||
682 | return; | ||
683 | } | ||
684 | |||
685 | va_copy(args_copy, args); | ||
686 | vsnprintf(error, length, format, args_copy); | ||
687 | va_end(args_copy); | ||
688 | 720 | ||
689 | if (error[length - 2] == '\n') { | 721 | size_t len = strlen(error); |
690 | error[length - 2] = '\0'; | 722 | if (error[len - 1] == '\n') { |
723 | error[len - 1] = '\0'; | ||
691 | } | 724 | } |
692 | 725 | ||
693 | sway_log_importance_t importance = SWAY_DEBUG; | 726 | sway_log_importance_t importance = SWAY_DEBUG; |
@@ -708,7 +741,7 @@ static void handle_xkb_context_log(struct xkb_context *context, | |||
708 | 741 | ||
709 | struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, | 742 | struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, |
710 | char **error) { | 743 | char **error) { |
711 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | 744 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); |
712 | if (!sway_assert(context, "cannot create XKB context")) { | 745 | if (!sway_assert(context, "cannot create XKB context")) { |
713 | return NULL; | 746 | return NULL; |
714 | } | 747 | } |
@@ -722,13 +755,8 @@ struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, | |||
722 | if (!keymap_file) { | 755 | if (!keymap_file) { |
723 | 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); |
724 | if (error) { | 757 | if (error) { |
725 | size_t len = snprintf(NULL, 0, "cannot read xkb file %s: %s", | 758 | *error = format_str("cannot read xkb file %s: %s", |
726 | ic->xkb_file, strerror(errno)) + 1; | 759 | ic->xkb_file, strerror(errno)); |
727 | *error = malloc(len); | ||
728 | if (*error) { | ||
729 | snprintf(*error, len, "cannot read xkb_file %s: %s", | ||
730 | ic->xkb_file, strerror(errno)); | ||
731 | } | ||
732 | } | 760 | } |
733 | goto cleanup; | 761 | goto cleanup; |
734 | } | 762 | } |
@@ -766,13 +794,12 @@ static void destroy_empty_wlr_keyboard_group(void *data) { | |||
766 | 794 | ||
767 | static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { | 795 | static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { |
768 | struct sway_input_device *device = keyboard->seat_device->input_device; | 796 | struct sway_input_device *device = keyboard->seat_device->input_device; |
769 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | 797 | struct wlr_keyboard_group *wlr_group = keyboard->wlr->group; |
770 | struct wlr_keyboard_group *wlr_group = wlr_keyboard->group; | ||
771 | 798 | ||
772 | sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", | 799 | sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", |
773 | device->identifier, wlr_group); | 800 | device->identifier, wlr_group); |
774 | 801 | ||
775 | wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); | 802 | wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr); |
776 | 803 | ||
777 | if (wl_list_empty(&wlr_group->devices)) { | 804 | if (wl_list_empty(&wlr_group->devices)) { |
778 | sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", | 805 | sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", |
@@ -797,9 +824,7 @@ static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { | |||
797 | } | 824 | } |
798 | 825 | ||
799 | static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | 826 | static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { |
800 | struct sway_input_device *device = keyboard->seat_device->input_device; | 827 | if (!keyboard->wlr->group) { |
801 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | ||
802 | if (!wlr_keyboard->group) { | ||
803 | return; | 828 | return; |
804 | } | 829 | } |
805 | 830 | ||
@@ -815,7 +840,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | |||
815 | break; | 840 | break; |
816 | case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ | 841 | case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ |
817 | case KEYBOARD_GROUP_SMART:; | 842 | case KEYBOARD_GROUP_SMART:; |
818 | struct wlr_keyboard_group *group = wlr_keyboard->group; | 843 | struct wlr_keyboard_group *group = keyboard->wlr->group; |
819 | if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || | 844 | if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || |
820 | !repeat_info_match(keyboard, &group->keyboard)) { | 845 | !repeat_info_match(keyboard, &group->keyboard)) { |
821 | sway_keyboard_group_remove(keyboard); | 846 | sway_keyboard_group_remove(keyboard); |
@@ -826,7 +851,6 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | |||
826 | 851 | ||
827 | static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | 852 | static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { |
828 | struct sway_input_device *device = keyboard->seat_device->input_device; | 853 | struct sway_input_device *device = keyboard->seat_device->input_device; |
829 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | ||
830 | struct sway_seat *seat = keyboard->seat_device->sway_seat; | 854 | struct sway_seat *seat = keyboard->seat_device->sway_seat; |
831 | struct seat_config *sc = seat_get_config(seat); | 855 | struct seat_config *sc = seat_get_config(seat); |
832 | 856 | ||
@@ -858,7 +882,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
858 | repeat_info_match(keyboard, &wlr_group->keyboard)) { | 882 | repeat_info_match(keyboard, &wlr_group->keyboard)) { |
859 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", | 883 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", |
860 | device->identifier, wlr_group); | 884 | device->identifier, wlr_group); |
861 | wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); | 885 | wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr); |
862 | return; | 886 | return; |
863 | } | 887 | } |
864 | break; | 888 | break; |
@@ -897,7 +921,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
897 | goto cleanup; | 921 | goto cleanup; |
898 | } | 922 | } |
899 | sway_group->seat_device->input_device->wlr_device = | 923 | sway_group->seat_device->input_device->wlr_device = |
900 | sway_group->wlr_group->input_device; | 924 | &sway_group->wlr_group->keyboard.base; |
901 | 925 | ||
902 | if (!sway_keyboard_create(seat, sway_group->seat_device)) { | 926 | if (!sway_keyboard_create(seat, sway_group->seat_device)) { |
903 | sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); | 927 | sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); |
@@ -906,7 +930,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
906 | 930 | ||
907 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", | 931 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", |
908 | device->identifier, sway_group->wlr_group); | 932 | device->identifier, sway_group->wlr_group); |
909 | wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); | 933 | wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr); |
910 | 934 | ||
911 | wl_list_insert(&seat->keyboard_groups, &sway_group->link); | 935 | wl_list_insert(&seat->keyboard_groups, &sway_group->link); |
912 | 936 | ||
@@ -938,10 +962,8 @@ cleanup: | |||
938 | void sway_keyboard_configure(struct sway_keyboard *keyboard) { | 962 | void sway_keyboard_configure(struct sway_keyboard *keyboard) { |
939 | struct input_config *input_config = | 963 | struct input_config *input_config = |
940 | input_device_get_config(keyboard->seat_device->input_device); | 964 | input_device_get_config(keyboard->seat_device->input_device); |
941 | struct wlr_input_device *wlr_device = | ||
942 | keyboard->seat_device->input_device->wlr_device; | ||
943 | 965 | ||
944 | if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard), | 966 | if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr), |
945 | "sway_keyboard_configure should not be called with a " | 967 | "sway_keyboard_configure should not be called with a " |
946 | "keyboard group's keyboard")) { | 968 | "keyboard group's keyboard")) { |
947 | return; | 969 | return; |
@@ -983,11 +1005,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
983 | 1005 | ||
984 | sway_keyboard_group_remove_invalid(keyboard); | 1006 | sway_keyboard_group_remove_invalid(keyboard); |
985 | 1007 | ||
986 | wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); | 1008 | wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap); |
987 | wlr_keyboard_set_repeat_info(wlr_device->keyboard, | 1009 | wlr_keyboard_set_repeat_info(keyboard->wlr, |
988 | keyboard->repeat_rate, keyboard->repeat_delay); | 1010 | keyboard->repeat_rate, keyboard->repeat_delay); |
989 | 1011 | ||
990 | if (!wlr_device->keyboard->group) { | 1012 | if (!keyboard->wlr->group) { |
991 | sway_keyboard_group_add(keyboard); | 1013 | sway_keyboard_group_add(keyboard); |
992 | } | 1014 | } |
993 | 1015 | ||
@@ -1007,40 +1029,42 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
1007 | } | 1029 | } |
1008 | } | 1030 | } |
1009 | if (locked_mods) { | 1031 | if (locked_mods) { |
1010 | wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, | 1032 | wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0, |
1011 | locked_mods, 0); | 1033 | locked_mods, 0); |
1012 | uint32_t leds = 0; | 1034 | uint32_t leds = 0; |
1013 | for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { | 1035 | for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { |
1014 | if (xkb_state_led_index_is_active( | 1036 | if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state, |
1015 | wlr_device->keyboard->xkb_state, | 1037 | keyboard->wlr->led_indexes[i])) { |
1016 | wlr_device->keyboard->led_indexes[i])) { | ||
1017 | leds |= (1 << i); | 1038 | leds |= (1 << i); |
1018 | } | 1039 | } |
1019 | } | 1040 | } |
1020 | if (wlr_device->keyboard->group) { | 1041 | if (keyboard->wlr->group) { |
1021 | wlr_keyboard_led_update( | 1042 | wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds); |
1022 | &wlr_device->keyboard->group->keyboard, leds); | ||
1023 | } else { | 1043 | } else { |
1024 | wlr_keyboard_led_update(wlr_device->keyboard, leds); | 1044 | wlr_keyboard_led_update(keyboard->wlr, leds); |
1025 | } | 1045 | } |
1026 | } | 1046 | } |
1027 | } else { | 1047 | } else { |
1028 | xkb_keymap_unref(keymap); | 1048 | xkb_keymap_unref(keymap); |
1029 | sway_keyboard_group_remove_invalid(keyboard); | 1049 | sway_keyboard_group_remove_invalid(keyboard); |
1030 | if (!wlr_device->keyboard->group) { | 1050 | if (!keyboard->wlr->group) { |
1031 | sway_keyboard_group_add(keyboard); | 1051 | sway_keyboard_group_add(keyboard); |
1032 | } | 1052 | } |
1033 | } | 1053 | } |
1034 | 1054 | ||
1055 | // If the seat has no active keyboard, set this one | ||
1035 | struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; | 1056 | struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; |
1036 | wlr_seat_set_keyboard(seat, wlr_device); | 1057 | struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard; |
1058 | if (current_keyboard == NULL) { | ||
1059 | wlr_seat_set_keyboard(seat, keyboard->wlr); | ||
1060 | } | ||
1037 | 1061 | ||
1038 | wl_list_remove(&keyboard->keyboard_key.link); | 1062 | wl_list_remove(&keyboard->keyboard_key.link); |
1039 | wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); | 1063 | wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); |
1040 | keyboard->keyboard_key.notify = handle_keyboard_key; | 1064 | keyboard->keyboard_key.notify = handle_keyboard_key; |
1041 | 1065 | ||
1042 | wl_list_remove(&keyboard->keyboard_modifiers.link); | 1066 | wl_list_remove(&keyboard->keyboard_modifiers.link); |
1043 | wl_signal_add(&wlr_device->keyboard->events.modifiers, | 1067 | wl_signal_add(&keyboard->wlr->events.modifiers, |
1044 | &keyboard->keyboard_modifiers); | 1068 | &keyboard->keyboard_modifiers); |
1045 | keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; | 1069 | keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; |
1046 | 1070 | ||
@@ -1057,12 +1081,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { | |||
1057 | if (!keyboard) { | 1081 | if (!keyboard) { |
1058 | return; | 1082 | return; |
1059 | } | 1083 | } |
1060 | if (keyboard->seat_device->input_device->wlr_device->keyboard->group) { | 1084 | if (keyboard->wlr->group) { |
1061 | sway_keyboard_group_remove(keyboard); | 1085 | sway_keyboard_group_remove(keyboard); |
1062 | } | 1086 | } |
1063 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | 1087 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; |
1064 | struct sway_input_device *device = keyboard->seat_device->input_device; | 1088 | if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) { |
1065 | if (wlr_seat_get_keyboard(wlr_seat) == device->wlr_device->keyboard) { | ||
1066 | wlr_seat_set_keyboard(wlr_seat, NULL); | 1089 | wlr_seat_set_keyboard(wlr_seat, NULL); |
1067 | } | 1090 | } |
1068 | if (keyboard->keymap) { | 1091 | if (keyboard->keymap) { |
diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 54520f9e..0266c7a9 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include <float.h> | 1 | #include <float.h> |
2 | #include <libinput.h> | 2 | #include <libinput.h> |
3 | #include <libudev.h> | ||
3 | #include <limits.h> | 4 | #include <limits.h> |
4 | #include <wlr/backend/libinput.h> | 5 | #include <wlr/backend/libinput.h> |
5 | #include "log.h" | 6 | #include "log.h" |
@@ -78,6 +79,16 @@ static bool set_accel_speed(struct libinput_device *device, double speed) { | |||
78 | return true; | 79 | return true; |
79 | } | 80 | } |
80 | 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 | |||
81 | static bool set_accel_profile(struct libinput_device *device, | 92 | static bool set_accel_profile(struct libinput_device *device, |
82 | enum libinput_config_accel_profile profile) { | 93 | enum libinput_config_accel_profile profile) { |
83 | if (!libinput_device_config_accel_is_available(device) || | 94 | if (!libinput_device_config_accel_is_available(device) || |
@@ -155,6 +166,18 @@ static bool set_scroll_button(struct libinput_device *dev, uint32_t button) { | |||
155 | return true; | 166 | return true; |
156 | } | 167 | } |
157 | 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 | |||
158 | static bool set_dwt(struct libinput_device *device, bool dwt) { | 181 | static bool set_dwt(struct libinput_device *device, bool dwt) { |
159 | if (!libinput_device_config_dwt_is_available(device) || | 182 | if (!libinput_device_config_dwt_is_available(device) || |
160 | libinput_device_config_dwt_get_enabled(device) == dwt) { | 183 | libinput_device_config_dwt_get_enabled(device) == dwt) { |
@@ -165,6 +188,16 @@ static bool set_dwt(struct libinput_device *device, bool dwt) { | |||
165 | return true; | 188 | return true; |
166 | } | 189 | } |
167 | 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 | |||
168 | 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]) { |
169 | if (!libinput_device_config_calibration_has_matrix(dev)) { | 202 | if (!libinput_device_config_calibration_has_matrix(dev)) { |
170 | return false; | 203 | return false; |
@@ -186,35 +219,38 @@ static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { | |||
186 | return changed; | 219 | return changed; |
187 | } | 220 | } |
188 | 221 | ||
189 | void sway_input_configure_libinput_device(struct sway_input_device *input_device) { | 222 | static bool configure_send_events(struct libinput_device *device, |
190 | struct input_config *ic = input_device_get_config(input_device); | 223 | struct input_config *ic) { |
191 | if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | struct libinput_device *device = | ||
196 | wlr_libinput_get_device_handle(input_device->wlr_device); | ||
197 | sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')", | ||
198 | ic->identifier, input_device->identifier); | ||
199 | |||
200 | bool changed = false; | ||
201 | if (ic->mapped_to_output && | 224 | if (ic->mapped_to_output && |
225 | strcmp("*", ic->mapped_to_output) != 0 && | ||
202 | !output_by_name_or_id(ic->mapped_to_output)) { | 226 | !output_by_name_or_id(ic->mapped_to_output)) { |
203 | sway_log(SWAY_DEBUG, | 227 | sway_log(SWAY_DEBUG, |
204 | "%s '%s' is mapped to offline output '%s'; disabling input", | 228 | "%s '%s' is mapped to offline output '%s'; disabling input", |
205 | ic->input_type, ic->identifier, ic->mapped_to_output); | 229 | ic->input_type, ic->identifier, ic->mapped_to_output); |
206 | changed |= set_send_events(device, | 230 | return set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); |
207 | LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); | ||
208 | } else if (ic->send_events != INT_MIN) { | 231 | } else if (ic->send_events != INT_MIN) { |
209 | changed |= set_send_events(device, ic->send_events); | 232 | return set_send_events(device, ic->send_events); |
210 | } else { | 233 | } else { |
211 | // 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 |
212 | // 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, |
213 | // we'd remain stuck sending no events. | 236 | // we'd remain stuck sending no events. |
214 | changed |= set_send_events(device, | 237 | return set_send_events(device, |
215 | libinput_device_config_send_events_get_default_mode(device)); | 238 | libinput_device_config_send_events_get_default_mode(device)); |
216 | } | 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 | } | ||
217 | 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); | ||
218 | if (ic->tap != INT_MIN) { | 254 | if (ic->tap != INT_MIN) { |
219 | changed |= set_tap(device, ic->tap); | 255 | changed |= set_tap(device, ic->tap); |
220 | } | 256 | } |
@@ -230,6 +266,9 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device | |||
230 | if (ic->pointer_accel != FLT_MIN) { | 266 | if (ic->pointer_accel != FLT_MIN) { |
231 | changed |= set_accel_speed(device, ic->pointer_accel); | 267 | changed |= set_accel_speed(device, ic->pointer_accel); |
232 | } | 268 | } |
269 | if (ic->rotation_angle != FLT_MIN) { | ||
270 | changed |= set_rotation_angle(device, ic->rotation_angle); | ||
271 | } | ||
233 | if (ic->accel_profile != INT_MIN) { | 272 | if (ic->accel_profile != INT_MIN) { |
234 | changed |= set_accel_profile(device, ic->accel_profile); | 273 | changed |= set_accel_profile(device, ic->accel_profile); |
235 | } | 274 | } |
@@ -251,13 +290,33 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device | |||
251 | if (ic->scroll_button != INT_MIN) { | 290 | if (ic->scroll_button != INT_MIN) { |
252 | changed |= set_scroll_button(device, ic->scroll_button); | 291 | changed |= set_scroll_button(device, ic->scroll_button); |
253 | } | 292 | } |
293 | if (ic->scroll_button_lock != INT_MIN) { | ||
294 | changed |= set_scroll_button_lock(device, ic->scroll_button_lock); | ||
295 | } | ||
254 | if (ic->dwt != INT_MIN) { | 296 | if (ic->dwt != INT_MIN) { |
255 | changed |= set_dwt(device, ic->dwt); | 297 | changed |= set_dwt(device, ic->dwt); |
256 | } | 298 | } |
299 | if (ic->dwtp != INT_MIN) { | ||
300 | changed |= set_dwtp(device, ic->dwtp); | ||
301 | } | ||
257 | if (ic->calibration_matrix.configured) { | 302 | if (ic->calibration_matrix.configured) { |
258 | changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); | 303 | changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); |
259 | } | 304 | } |
260 | 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 | |||
261 | if (changed) { | 320 | if (changed) { |
262 | ipc_event_input("libinput_config", input_device); | 321 | ipc_event_input("libinput_config", input_device); |
263 | } | 322 | } |
@@ -286,6 +345,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { | |||
286 | libinput_device_config_tap_get_default_drag_lock_enabled(device)); | 345 | libinput_device_config_tap_get_default_drag_lock_enabled(device)); |
287 | changed |= set_accel_speed(device, | 346 | changed |= set_accel_speed(device, |
288 | 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)); | ||
289 | changed |= set_accel_profile(device, | 350 | changed |= set_accel_profile(device, |
290 | libinput_device_config_accel_get_default_profile(device)); | 351 | libinput_device_config_accel_get_default_profile(device)); |
291 | changed |= set_natural_scroll(device, | 352 | changed |= set_natural_scroll(device, |
@@ -303,6 +364,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { | |||
303 | libinput_device_config_scroll_get_default_button(device)); | 364 | libinput_device_config_scroll_get_default_button(device)); |
304 | changed |= set_dwt(device, | 365 | changed |= set_dwt(device, |
305 | 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)); | ||
306 | 369 | ||
307 | float matrix[6]; | 370 | float matrix[6]; |
308 | libinput_device_config_calibration_get_default_matrix(device, matrix); | 371 | libinput_device_config_calibration_get_default_matrix(device, matrix); |
@@ -312,3 +375,32 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { | |||
312 | ipc_event_input("libinput_config", input_device); | 375 | ipc_event_input("libinput_config", input_device); |
313 | } | 376 | } |
314 | } | 377 | } |
378 | |||
379 | bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { | ||
380 | if (!wlr_input_device_is_libinput(sway_device->wlr_device)) { | ||
381 | return false; | ||
382 | } | ||
383 | |||
384 | struct libinput_device *device = | ||
385 | wlr_libinput_get_device_handle(sway_device->wlr_device); | ||
386 | struct udev_device *udev_device = | ||
387 | libinput_device_get_udev_device(device); | ||
388 | if (!udev_device) { | ||
389 | return false; | ||
390 | } | ||
391 | |||
392 | const char *id_path = udev_device_get_property_value(udev_device, "ID_PATH"); | ||
393 | if (!id_path) { | ||
394 | return false; | ||
395 | } | ||
396 | |||
397 | const char prefix_platform[] = "platform-"; | ||
398 | if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) { | ||
399 | return false; | ||
400 | } | ||
401 | |||
402 | const char prefix_pci[] = "pci-"; | ||
403 | const char infix_platform[] = "-platform-"; | ||
404 | return (strncmp(id_path, prefix_pci, strlen(prefix_pci)) == 0) && | ||
405 | strstr(id_path, infix_platform); | ||
406 | } | ||
diff --git a/sway/input/seat.c b/sway/input/seat.c index 1f5865ee..0c5672bc 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -1,25 +1,27 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <strings.h> | 4 | #include <strings.h> |
6 | #include <time.h> | 5 | #include <time.h> |
6 | #include <wlr/config.h> | ||
7 | #include <wlr/types/wlr_cursor.h> | 7 | #include <wlr/types/wlr_cursor.h> |
8 | #include <wlr/types/wlr_data_device.h> | 8 | #include <wlr/types/wlr_data_device.h> |
9 | #include <wlr/types/wlr_idle.h> | 9 | #include <wlr/types/wlr_idle_notify_v1.h> |
10 | #include <wlr/types/wlr_keyboard_group.h> | 10 | #include <wlr/types/wlr_keyboard_group.h> |
11 | #include <wlr/types/wlr_output_layout.h> | 11 | #include <wlr/types/wlr_output_layout.h> |
12 | #include <wlr/types/wlr_primary_selection.h> | 12 | #include <wlr/types/wlr_primary_selection.h> |
13 | #include <wlr/types/wlr_tablet_v2.h> | 13 | #include <wlr/types/wlr_tablet_v2.h> |
14 | #include <wlr/types/wlr_touch.h> | ||
14 | #include <wlr/types/wlr_xcursor_manager.h> | 15 | #include <wlr/types/wlr_xcursor_manager.h> |
15 | #include "config.h" | 16 | #include "config.h" |
16 | #include "list.h" | 17 | #include "list.h" |
17 | #include "log.h" | 18 | #include "log.h" |
18 | #include "sway/config.h" | 19 | #include "sway/config.h" |
19 | #include "sway/desktop.h" | 20 | #include "sway/scene_descriptor.h" |
20 | #include "sway/input/cursor.h" | 21 | #include "sway/input/cursor.h" |
21 | #include "sway/input/input-manager.h" | 22 | #include "sway/input/input-manager.h" |
22 | #include "sway/input/keyboard.h" | 23 | #include "sway/input/keyboard.h" |
24 | #include "sway/input/libinput.h" | ||
23 | #include "sway/input/seat.h" | 25 | #include "sway/input/seat.h" |
24 | #include "sway/input/switch.h" | 26 | #include "sway/input/switch.h" |
25 | #include "sway/input/tablet.h" | 27 | #include "sway/input/tablet.h" |
@@ -41,6 +43,7 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) { | |||
41 | sway_keyboard_destroy(seat_device->keyboard); | 43 | sway_keyboard_destroy(seat_device->keyboard); |
42 | sway_tablet_destroy(seat_device->tablet); | 44 | sway_tablet_destroy(seat_device->tablet); |
43 | sway_tablet_pad_destroy(seat_device->tablet_pad); | 45 | sway_tablet_pad_destroy(seat_device->tablet_pad); |
46 | sway_switch_destroy(seat_device->switch_device); | ||
44 | wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, | 47 | wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, |
45 | seat_device->input_device->wlr_device); | 48 | seat_device->input_device->wlr_device); |
46 | wl_list_remove(&seat_device->link); | 49 | wl_list_remove(&seat_device->link); |
@@ -50,10 +53,26 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) { | |||
50 | static void seat_node_destroy(struct sway_seat_node *seat_node) { | 53 | static void seat_node_destroy(struct sway_seat_node *seat_node) { |
51 | wl_list_remove(&seat_node->destroy.link); | 54 | wl_list_remove(&seat_node->destroy.link); |
52 | wl_list_remove(&seat_node->link); | 55 | wl_list_remove(&seat_node->link); |
56 | |||
57 | /* | ||
58 | * This is the only time we remove items from the focus stack without | ||
59 | * immediately re-adding them. If we just removed the last thing, | ||
60 | * mark that nothing has focus anymore. | ||
61 | */ | ||
62 | if (wl_list_empty(&seat_node->seat->focus_stack)) { | ||
63 | seat_node->seat->has_focus = false; | ||
64 | } | ||
65 | |||
53 | free(seat_node); | 66 | free(seat_node); |
54 | } | 67 | } |
55 | 68 | ||
56 | 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 | |||
57 | if (seat == config->handler_context.seat) { | 76 | if (seat == config->handler_context.seat) { |
58 | config->handler_context.seat = input_manager_get_default_seat(); | 77 | config->handler_context.seat = input_manager_get_default_seat(); |
59 | } | 78 | } |
@@ -74,10 +93,11 @@ void seat_destroy(struct sway_seat *seat) { | |||
74 | wl_list_remove(&seat->request_set_selection.link); | 93 | wl_list_remove(&seat->request_set_selection.link); |
75 | wl_list_remove(&seat->request_set_primary_selection.link); | 94 | wl_list_remove(&seat->request_set_primary_selection.link); |
76 | wl_list_remove(&seat->link); | 95 | wl_list_remove(&seat->link); |
77 | wlr_seat_destroy(seat->wlr_seat); | 96 | wl_list_remove(&seat->destroy.link); |
78 | for (int i = 0; i < seat->deferred_bindings->length; i++) { | 97 | for (int i = 0; i < seat->deferred_bindings->length; i++) { |
79 | free_sway_binding(seat->deferred_bindings->items[i]); | 98 | free_sway_binding(seat->deferred_bindings->items[i]); |
80 | } | 99 | } |
100 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
81 | list_free(seat->deferred_bindings); | 101 | list_free(seat->deferred_bindings); |
82 | free(seat->prev_workspace_name); | 102 | free(seat->prev_workspace_name); |
83 | free(seat); | 103 | free(seat); |
@@ -85,21 +105,10 @@ void seat_destroy(struct sway_seat *seat) { | |||
85 | 105 | ||
86 | void seat_idle_notify_activity(struct sway_seat *seat, | 106 | void seat_idle_notify_activity(struct sway_seat *seat, |
87 | enum sway_input_idle_source source) { | 107 | enum sway_input_idle_source source) { |
88 | uint32_t mask = seat->idle_inhibit_sources; | 108 | if ((source & seat->idle_inhibit_sources) == 0) { |
89 | struct wlr_idle_timeout *timeout; | 109 | return; |
90 | int ntimers = 0, nidle = 0; | ||
91 | wl_list_for_each(timeout, &server.idle->idle_timers, link) { | ||
92 | ++ntimers; | ||
93 | if (timeout->idle_state) { | ||
94 | ++nidle; | ||
95 | } | ||
96 | } | ||
97 | if (nidle == ntimers) { | ||
98 | mask = seat->idle_wake_sources; | ||
99 | } | ||
100 | if ((source & mask) > 0) { | ||
101 | wlr_idle_notify_activity(server.idle, seat->wlr_seat); | ||
102 | } | 110 | } |
111 | wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat); | ||
103 | } | 112 | } |
104 | 113 | ||
105 | /** | 114 | /** |
@@ -129,7 +138,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( | |||
129 | if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { | 138 | if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { |
130 | continue; | 139 | continue; |
131 | } | 140 | } |
132 | if (input_device->wlr_device->keyboard == wlr_keyboard) { | 141 | if (input_device->wlr_device == &wlr_keyboard->base) { |
133 | return seat_device->keyboard; | 142 | return seat_device->keyboard; |
134 | } | 143 | } |
135 | } | 144 | } |
@@ -137,7 +146,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( | |||
137 | wl_list_for_each(group, &seat->keyboard_groups, link) { | 146 | wl_list_for_each(group, &seat->keyboard_groups, link) { |
138 | struct sway_input_device *input_device = | 147 | struct sway_input_device *input_device = |
139 | group->seat_device->input_device; | 148 | group->seat_device->input_device; |
140 | if (input_device->wlr_device->keyboard == wlr_keyboard) { | 149 | if (input_device->wlr_device == &wlr_keyboard->base) { |
141 | return group->seat_device->keyboard; | 150 | return group->seat_device->keyboard; |
142 | } | 151 | } |
143 | } | 152 | } |
@@ -161,11 +170,11 @@ static void seat_keyboard_notify_enter(struct sway_seat *seat, | |||
161 | state->pressed_keycodes, state->npressed, &keyboard->modifiers); | 170 | state->pressed_keycodes, state->npressed, &keyboard->modifiers); |
162 | } | 171 | } |
163 | 172 | ||
164 | static void seat_tablet_pads_notify_enter(struct sway_seat *seat, | 173 | static void seat_tablet_pads_set_focus(struct sway_seat *seat, |
165 | struct wlr_surface *surface) { | 174 | struct wlr_surface *surface) { |
166 | struct sway_seat_device *seat_device; | 175 | struct sway_seat_device *seat_device; |
167 | wl_list_for_each(seat_device, &seat->devices, link) { | 176 | wl_list_for_each(seat_device, &seat->devices, link) { |
168 | sway_tablet_pad_notify_enter(seat_device->tablet_pad, surface); | 177 | sway_tablet_pad_set_focus(seat_device->tablet_pad, surface); |
169 | } | 178 | } |
170 | } | 179 | } |
171 | 180 | ||
@@ -189,7 +198,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { | |||
189 | #endif | 198 | #endif |
190 | 199 | ||
191 | seat_keyboard_notify_enter(seat, view->surface); | 200 | seat_keyboard_notify_enter(seat, view->surface); |
192 | seat_tablet_pads_notify_enter(seat, view->surface); | 201 | seat_tablet_pads_set_focus(seat, view->surface); |
193 | sway_input_method_relay_set_focus(&seat->im_relay, view->surface); | 202 | sway_input_method_relay_set_focus(&seat->im_relay, view->surface); |
194 | 203 | ||
195 | struct wlr_pointer_constraint_v1 *constraint = | 204 | struct wlr_pointer_constraint_v1 *constraint = |
@@ -209,14 +218,13 @@ void seat_for_each_node(struct sway_seat *seat, | |||
209 | 218 | ||
210 | struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, | 219 | struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, |
211 | struct sway_node *ancestor) { | 220 | struct sway_node *ancestor) { |
212 | if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) { | 221 | if (node_is_view(ancestor)) { |
213 | return ancestor->sway_container; | 222 | return ancestor->sway_container; |
214 | } | 223 | } |
215 | struct sway_seat_node *current; | 224 | struct sway_seat_node *current; |
216 | wl_list_for_each(current, &seat->focus_stack, link) { | 225 | wl_list_for_each(current, &seat->focus_stack, link) { |
217 | struct sway_node *node = current->node; | 226 | struct sway_node *node = current->node; |
218 | if (node->type == N_CONTAINER && node->sway_container->view && | 227 | if (node_is_view(node) && node_has_ancestor(node, ancestor)) { |
219 | node_has_ancestor(node, ancestor)) { | ||
220 | return node->sway_container; | 228 | return node->sway_container; |
221 | } | 229 | } |
222 | } | 230 | } |
@@ -235,7 +243,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { | |||
235 | seat_node_destroy(seat_node); | 243 | seat_node_destroy(seat_node); |
236 | // If an unmanaged or layer surface is focused when an output gets | 244 | // If an unmanaged or layer surface is focused when an output gets |
237 | // disabled and an empty workspace on the output was focused by the | 245 | // disabled and an empty workspace on the output was focused by the |
238 | // seat, the seat needs to refocus it's focus inactive to update the | 246 | // seat, the seat needs to refocus its focus inactive to update the |
239 | // value of seat->workspace. | 247 | // value of seat->workspace. |
240 | if (seat->workspace == node->sway_workspace) { | 248 | if (seat->workspace == node->sway_workspace) { |
241 | struct sway_node *node = seat_get_focus_inactive(seat, &root->node); | 249 | struct sway_node *node = seat_get_focus_inactive(seat, &root->node); |
@@ -309,8 +317,8 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { | |||
309 | // Setting focus_inactive | 317 | // Setting focus_inactive |
310 | focus = seat_get_focus_inactive(seat, &root->node); | 318 | focus = seat_get_focus_inactive(seat, &root->node); |
311 | seat_set_raw_focus(seat, next_focus); | 319 | seat_set_raw_focus(seat, next_focus); |
312 | if (focus->type == N_CONTAINER && focus->sway_container->workspace) { | 320 | if (focus->type == N_CONTAINER && focus->sway_container->pending.workspace) { |
313 | seat_set_raw_focus(seat, &focus->sway_container->workspace->node); | 321 | seat_set_raw_focus(seat, &focus->sway_container->pending.workspace->node); |
314 | } | 322 | } |
315 | seat_set_raw_focus(seat, focus); | 323 | seat_set_raw_focus(seat, focus); |
316 | } | 324 | } |
@@ -351,25 +359,15 @@ static void handle_new_node(struct wl_listener *listener, void *data) { | |||
351 | seat_node_from_node(seat, node); | 359 | seat_node_from_node(seat, node); |
352 | } | 360 | } |
353 | 361 | ||
354 | 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) { |
355 | if (!icon->wlr_drag_icon->mapped) { | 363 | struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON); |
356 | return; | ||
357 | } | ||
358 | desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true); | ||
359 | } | ||
360 | |||
361 | void drag_icon_update_position(struct sway_drag_icon *icon) { | ||
362 | drag_icon_damage_whole(icon); | ||
363 | |||
364 | struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; | ||
365 | struct sway_seat *seat = icon->seat; | ||
366 | struct wlr_cursor *cursor = seat->cursor->cursor; | 364 | struct wlr_cursor *cursor = seat->cursor->cursor; |
365 | |||
367 | switch (wlr_icon->drag->grab_type) { | 366 | switch (wlr_icon->drag->grab_type) { |
368 | case WLR_DRAG_GRAB_KEYBOARD: | 367 | case WLR_DRAG_GRAB_KEYBOARD: |
369 | return; | 368 | return; |
370 | case WLR_DRAG_GRAB_KEYBOARD_POINTER: | 369 | case WLR_DRAG_GRAB_KEYBOARD_POINTER: |
371 | icon->x = cursor->x; | 370 | wlr_scene_node_set_position(node, cursor->x, cursor->y); |
372 | icon->y = cursor->y; | ||
373 | break; | 371 | break; |
374 | case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; | 372 | case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; |
375 | struct wlr_touch_point *point = | 373 | struct wlr_touch_point *point = |
@@ -377,39 +375,15 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { | |||
377 | if (point == NULL) { | 375 | if (point == NULL) { |
378 | return; | 376 | return; |
379 | } | 377 | } |
380 | icon->x = seat->touch_x; | 378 | wlr_scene_node_set_position(node, seat->touch_x, seat->touch_y); |
381 | icon->y = seat->touch_y; | ||
382 | } | 379 | } |
383 | |||
384 | drag_icon_damage_whole(icon); | ||
385 | } | 380 | } |
386 | 381 | ||
387 | static void drag_icon_handle_surface_commit(struct wl_listener *listener, | 382 | void drag_icons_update_position(struct sway_seat *seat) { |
388 | void *data) { | 383 | struct wlr_scene_node *node; |
389 | struct sway_drag_icon *icon = | 384 | wl_list_for_each(node, &seat->drag_icons->children, link) { |
390 | wl_container_of(listener, icon, surface_commit); | 385 | drag_icon_update_position(seat, node); |
391 | drag_icon_update_position(icon); | 386 | } |
392 | } | ||
393 | |||
394 | static void drag_icon_handle_map(struct wl_listener *listener, void *data) { | ||
395 | struct sway_drag_icon *icon = wl_container_of(listener, icon, map); | ||
396 | drag_icon_damage_whole(icon); | ||
397 | } | ||
398 | |||
399 | static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) { | ||
400 | struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap); | ||
401 | drag_icon_damage_whole(icon); | ||
402 | } | ||
403 | |||
404 | static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { | ||
405 | struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy); | ||
406 | icon->wlr_drag_icon->data = NULL; | ||
407 | wl_list_remove(&icon->link); | ||
408 | wl_list_remove(&icon->surface_commit.link); | ||
409 | wl_list_remove(&icon->unmap.link); | ||
410 | wl_list_remove(&icon->map.link); | ||
411 | wl_list_remove(&icon->destroy.link); | ||
412 | free(icon); | ||
413 | } | 387 | } |
414 | 388 | ||
415 | static void drag_handle_destroy(struct wl_listener *listener, void *data) { | 389 | static void drag_handle_destroy(struct wl_listener *listener, void *data) { |
@@ -481,27 +455,20 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { | |||
481 | 455 | ||
482 | struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; | 456 | struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; |
483 | if (wlr_drag_icon != NULL) { | 457 | if (wlr_drag_icon != NULL) { |
484 | 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); |
485 | if (icon == NULL) { | 459 | if (!tree) { |
486 | sway_log(SWAY_ERROR, "Allocation failed"); | 460 | sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree"); |
487 | return; | 461 | return; |
488 | } | 462 | } |
489 | icon->seat = seat; | ||
490 | icon->wlr_drag_icon = wlr_drag_icon; | ||
491 | wlr_drag_icon->data = icon; | ||
492 | |||
493 | icon->surface_commit.notify = drag_icon_handle_surface_commit; | ||
494 | wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); | ||
495 | icon->unmap.notify = drag_icon_handle_unmap; | ||
496 | wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); | ||
497 | icon->map.notify = drag_icon_handle_map; | ||
498 | wl_signal_add(&wlr_drag_icon->events.map, &icon->map); | ||
499 | icon->destroy.notify = drag_icon_handle_destroy; | ||
500 | wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); | ||
501 | 463 | ||
502 | wl_list_insert(&root->drag_icons, &icon->link); | 464 | if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON, |
465 | wlr_drag_icon)) { | ||
466 | sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor"); | ||
467 | wlr_scene_node_destroy(&tree->node); | ||
468 | return; | ||
469 | } | ||
503 | 470 | ||
504 | drag_icon_update_position(icon); | 471 | drag_icon_update_position(seat, &tree->node); |
505 | } | 472 | } |
506 | seatop_begin_default(seat); | 473 | seatop_begin_default(seat); |
507 | } | 474 | } |
@@ -548,8 +515,18 @@ struct sway_seat *seat_create(const char *seat_name) { | |||
548 | return NULL; | 515 | return NULL; |
549 | } | 516 | } |
550 | 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 | |||
551 | seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); | 527 | seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); |
552 | 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); | ||
553 | free(seat); | 530 | free(seat); |
554 | return NULL; | 531 | return NULL; |
555 | } | 532 | } |
@@ -557,11 +534,15 @@ struct sway_seat *seat_create(const char *seat_name) { | |||
557 | 534 | ||
558 | seat->cursor = sway_cursor_create(seat); | 535 | seat->cursor = sway_cursor_create(seat); |
559 | if (!seat->cursor) { | 536 | if (!seat->cursor) { |
537 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
560 | wlr_seat_destroy(seat->wlr_seat); | 538 | wlr_seat_destroy(seat->wlr_seat); |
561 | free(seat); | 539 | free(seat); |
562 | return NULL; | 540 | return NULL; |
563 | } | 541 | } |
564 | 542 | ||
543 | seat->destroy.notify = handle_seat_destroy; | ||
544 | wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy); | ||
545 | |||
565 | seat->idle_inhibit_sources = seat->idle_wake_sources = | 546 | seat->idle_inhibit_sources = seat->idle_wake_sources = |
566 | IDLE_SOURCE_KEYBOARD | | 547 | IDLE_SOURCE_KEYBOARD | |
567 | IDLE_SOURCE_POINTER | | 548 | IDLE_SOURCE_POINTER | |
@@ -635,7 +616,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { | |||
635 | case WLR_INPUT_DEVICE_TOUCH: | 616 | case WLR_INPUT_DEVICE_TOUCH: |
636 | caps |= WL_SEAT_CAPABILITY_TOUCH; | 617 | caps |= WL_SEAT_CAPABILITY_TOUCH; |
637 | break; | 618 | break; |
638 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 619 | case WLR_INPUT_DEVICE_TABLET: |
639 | caps |= WL_SEAT_CAPABILITY_POINTER; | 620 | caps |= WL_SEAT_CAPABILITY_POINTER; |
640 | break; | 621 | break; |
641 | case WLR_INPUT_DEVICE_SWITCH: | 622 | case WLR_INPUT_DEVICE_SWITCH: |
@@ -653,7 +634,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { | |||
653 | } else { | 634 | } else { |
654 | wlr_seat_set_capabilities(seat->wlr_seat, caps); | 635 | wlr_seat_set_capabilities(seat->wlr_seat, caps); |
655 | if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { | 636 | if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { |
656 | cursor_set_image(seat->cursor, "left_ptr", NULL); | 637 | cursor_set_image(seat->cursor, "default", NULL); |
657 | } | 638 | } |
658 | } | 639 | } |
659 | } | 640 | } |
@@ -666,12 +647,55 @@ static void seat_reset_input_config(struct sway_seat *seat, | |||
666 | sway_device->input_device->wlr_device, NULL); | 647 | sway_device->input_device->wlr_device, NULL); |
667 | } | 648 | } |
668 | 649 | ||
669 | static void seat_apply_input_config(struct sway_seat *seat, | 650 | static bool has_prefix(const char *str, const char *prefix) { |
651 | return strncmp(str, prefix, strlen(prefix)) == 0; | ||
652 | } | ||
653 | |||
654 | /** | ||
655 | * Get the name of the built-in output, if any. Returns NULL if there isn't | ||
656 | * exactly one built-in output. | ||
657 | */ | ||
658 | static const char *get_builtin_output_name(void) { | ||
659 | const char *match = NULL; | ||
660 | for (int i = 0; i < root->outputs->length; ++i) { | ||
661 | struct sway_output *output = root->outputs->items[i]; | ||
662 | const char *name = output->wlr_output->name; | ||
663 | if (has_prefix(name, "eDP-") || has_prefix(name, "LVDS-") || | ||
664 | has_prefix(name, "DSI-")) { | ||
665 | if (match != NULL) { | ||
666 | return NULL; | ||
667 | } | ||
668 | match = name; | ||
669 | } | ||
670 | } | ||
671 | return match; | ||
672 | } | ||
673 | |||
674 | static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { | ||
675 | switch (seat_device->input_device->wlr_device->type) { | ||
676 | case WLR_INPUT_DEVICE_TOUCH: | ||
677 | case WLR_INPUT_DEVICE_TABLET: | ||
678 | return true; | ||
679 | default: | ||
680 | return false; | ||
681 | } | ||
682 | } | ||
683 | |||
684 | static void seat_apply_input_mapping(struct sway_seat *seat, | ||
670 | struct sway_seat_device *sway_device) { | 685 | struct sway_seat_device *sway_device) { |
671 | struct input_config *ic = | 686 | struct input_config *ic = |
672 | input_device_get_config(sway_device->input_device); | 687 | input_device_get_config(sway_device->input_device); |
673 | 688 | ||
674 | 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", | ||
675 | sway_device->input_device->identifier); | 699 | sway_device->input_device->identifier); |
676 | 700 | ||
677 | 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; |
@@ -680,8 +704,38 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
680 | ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; | 704 | ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; |
681 | 705 | ||
682 | switch (mapped_to) { | 706 | switch (mapped_to) { |
683 | case MAPPED_TO_DEFAULT: | 707 | case MAPPED_TO_DEFAULT:; |
684 | mapped_to_output = sway_device->input_device->wlr_device->output_name; | 708 | /* |
709 | * If the wlroots backend provides an output name, use that. | ||
710 | * | ||
711 | * Otherwise, try to map built-in touch and pointer devices to the | ||
712 | * built-in output. | ||
713 | */ | ||
714 | struct wlr_input_device *dev = sway_device->input_device->wlr_device; | ||
715 | switch (dev->type) { | ||
716 | case WLR_INPUT_DEVICE_POINTER: | ||
717 | mapped_to_output = wlr_pointer_from_input_device(dev)->output_name; | ||
718 | break; | ||
719 | case WLR_INPUT_DEVICE_TOUCH: | ||
720 | mapped_to_output = wlr_touch_from_input_device(dev)->output_name; | ||
721 | break; | ||
722 | default: | ||
723 | mapped_to_output = NULL; | ||
724 | break; | ||
725 | } | ||
726 | #if WLR_HAS_LIBINPUT_BACKEND | ||
727 | if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && | ||
728 | sway_libinput_device_is_builtin(sway_device->input_device)) { | ||
729 | mapped_to_output = get_builtin_output_name(); | ||
730 | if (mapped_to_output) { | ||
731 | sway_log(SWAY_DEBUG, "Auto-detected output '%s' for device '%s'", | ||
732 | mapped_to_output, sway_device->input_device->identifier); | ||
733 | } | ||
734 | } | ||
735 | #else | ||
736 | (void)is_touch_or_tablet_tool; | ||
737 | (void)get_builtin_output_name; | ||
738 | #endif | ||
685 | if (mapped_to_output == NULL) { | 739 | if (mapped_to_output == NULL) { |
686 | return; | 740 | return; |
687 | } | 741 | } |
@@ -725,12 +779,9 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
725 | 779 | ||
726 | static void seat_configure_pointer(struct sway_seat *seat, | 780 | static void seat_configure_pointer(struct sway_seat *seat, |
727 | struct sway_seat_device *sway_device) { | 781 | struct sway_seat_device *sway_device) { |
728 | if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { | 782 | seat_configure_xcursor(seat); |
729 | seat_configure_xcursor(seat); | ||
730 | } | ||
731 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 783 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
732 | sway_device->input_device->wlr_device); | 784 | sway_device->input_device->wlr_device); |
733 | seat_apply_input_config(seat, sway_device); | ||
734 | wl_event_source_timer_update( | 785 | wl_event_source_timer_update( |
735 | seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); | 786 | seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); |
736 | } | 787 | } |
@@ -741,13 +792,22 @@ static void seat_configure_keyboard(struct sway_seat *seat, | |||
741 | sway_keyboard_create(seat, seat_device); | 792 | sway_keyboard_create(seat, seat_device); |
742 | } | 793 | } |
743 | sway_keyboard_configure(seat_device->keyboard); | 794 | sway_keyboard_configure(seat_device->keyboard); |
744 | wlr_seat_set_keyboard(seat->wlr_seat, | 795 | |
745 | seat_device->input_device->wlr_device); | 796 | // We only need to update the current keyboard, as the rest will be updated |
746 | struct sway_node *focus = seat_get_focus(seat); | 797 | // as they are activated. |
747 | if (focus && node_is_view(focus)) { | 798 | struct wlr_keyboard *wlr_keyboard = |
748 | // force notify reenter to pick up the new configuration | 799 | wlr_keyboard_from_input_device(seat_device->input_device->wlr_device); |
800 | struct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard; | ||
801 | if (wlr_keyboard != current_keyboard) { | ||
802 | return; | ||
803 | } | ||
804 | |||
805 | // force notify reenter to pick up the new configuration. This reuses | ||
806 | // the current focused surface to avoid breaking input grabs. | ||
807 | struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; | ||
808 | if (surface) { | ||
749 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); | 809 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); |
750 | seat_keyboard_notify_enter(seat, focus->sway_container->view->surface); | 810 | seat_keyboard_notify_enter(seat, surface); |
751 | } | 811 | } |
752 | } | 812 | } |
753 | 813 | ||
@@ -756,7 +816,6 @@ static void seat_configure_switch(struct sway_seat *seat, | |||
756 | if (!seat_device->switch_device) { | 816 | if (!seat_device->switch_device) { |
757 | sway_switch_create(seat, seat_device); | 817 | sway_switch_create(seat, seat_device); |
758 | } | 818 | } |
759 | seat_apply_input_config(seat, seat_device); | ||
760 | sway_switch_configure(seat_device->switch_device); | 819 | sway_switch_configure(seat_device->switch_device); |
761 | } | 820 | } |
762 | 821 | ||
@@ -764,7 +823,6 @@ static void seat_configure_touch(struct sway_seat *seat, | |||
764 | struct sway_seat_device *sway_device) { | 823 | struct sway_seat_device *sway_device) { |
765 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 824 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
766 | sway_device->input_device->wlr_device); | 825 | sway_device->input_device->wlr_device); |
767 | seat_apply_input_config(seat, sway_device); | ||
768 | } | 826 | } |
769 | 827 | ||
770 | static void seat_configure_tablet_tool(struct sway_seat *seat, | 828 | static void seat_configure_tablet_tool(struct sway_seat *seat, |
@@ -775,7 +833,6 @@ static void seat_configure_tablet_tool(struct sway_seat *seat, | |||
775 | sway_configure_tablet(sway_device->tablet); | 833 | sway_configure_tablet(sway_device->tablet); |
776 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 834 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
777 | sway_device->input_device->wlr_device); | 835 | sway_device->input_device->wlr_device); |
778 | seat_apply_input_config(seat, sway_device); | ||
779 | } | 836 | } |
780 | 837 | ||
781 | static void seat_configure_tablet_pad(struct sway_seat *seat, | 838 | static void seat_configure_tablet_pad(struct sway_seat *seat, |
@@ -825,13 +882,25 @@ void seat_configure_device(struct sway_seat *seat, | |||
825 | case WLR_INPUT_DEVICE_TOUCH: | 882 | case WLR_INPUT_DEVICE_TOUCH: |
826 | seat_configure_touch(seat, seat_device); | 883 | seat_configure_touch(seat, seat_device); |
827 | break; | 884 | break; |
828 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 885 | case WLR_INPUT_DEVICE_TABLET: |
829 | seat_configure_tablet_tool(seat, seat_device); | 886 | seat_configure_tablet_tool(seat, seat_device); |
830 | break; | 887 | break; |
831 | case WLR_INPUT_DEVICE_TABLET_PAD: | 888 | case WLR_INPUT_DEVICE_TABLET_PAD: |
832 | seat_configure_tablet_pad(seat, seat_device); | 889 | seat_configure_tablet_pad(seat, seat_device); |
833 | break; | 890 | break; |
834 | } | 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); | ||
835 | } | 904 | } |
836 | 905 | ||
837 | void seat_reset_device(struct sway_seat *seat, | 906 | void seat_reset_device(struct sway_seat *seat, |
@@ -852,7 +921,7 @@ void seat_reset_device(struct sway_seat *seat, | |||
852 | case WLR_INPUT_DEVICE_TOUCH: | 921 | case WLR_INPUT_DEVICE_TOUCH: |
853 | seat_reset_input_config(seat, seat_device); | 922 | seat_reset_input_config(seat, seat_device); |
854 | break; | 923 | break; |
855 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 924 | case WLR_INPUT_DEVICE_TABLET: |
856 | seat_reset_input_config(seat, seat_device); | 925 | seat_reset_input_config(seat, seat_device); |
857 | break; | 926 | break; |
858 | case WLR_INPUT_DEVICE_TABLET_PAD: | 927 | case WLR_INPUT_DEVICE_TABLET_PAD: |
@@ -948,7 +1017,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
948 | 1017 | ||
949 | wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); | 1018 | wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); |
950 | struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( | 1019 | struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( |
951 | server.xwayland.xcursor_manager, "left_ptr", 1); | 1020 | server.xwayland.xcursor_manager, "default", 1); |
952 | if (xcursor != NULL) { | 1021 | if (xcursor != NULL) { |
953 | struct wlr_xcursor_image *image = xcursor->images[0]; | 1022 | struct wlr_xcursor_image *image = xcursor->images[0]; |
954 | wlr_xwayland_set_cursor( | 1023 | wlr_xwayland_set_cursor( |
@@ -974,32 +1043,35 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
974 | sway_log(SWAY_ERROR, | 1043 | sway_log(SWAY_ERROR, |
975 | "Cannot create XCursor manager for theme '%s'", cursor_theme); | 1044 | "Cannot create XCursor manager for theme '%s'", cursor_theme); |
976 | } | 1045 | } |
977 | } | ||
978 | 1046 | ||
979 | for (int i = 0; i < root->outputs->length; ++i) { | 1047 | |
980 | struct sway_output *sway_output = root->outputs->items[i]; | 1048 | for (int i = 0; i < root->outputs->length; ++i) { |
981 | struct wlr_output *output = sway_output->wlr_output; | 1049 | struct sway_output *sway_output = root->outputs->items[i]; |
982 | bool result = | 1050 | struct wlr_output *output = sway_output->wlr_output; |
983 | wlr_xcursor_manager_load(seat->cursor->xcursor_manager, | 1051 | bool result = |
984 | output->scale); | 1052 | wlr_xcursor_manager_load(seat->cursor->xcursor_manager, |
985 | if (!result) { | 1053 | output->scale); |
986 | sway_log(SWAY_ERROR, | 1054 | if (!result) { |
987 | "Cannot load xcursor theme for output '%s' with scale %f", | 1055 | sway_log(SWAY_ERROR, |
988 | output->name, output->scale); | 1056 | "Cannot load xcursor theme for output '%s' with scale %f", |
1057 | output->name, output->scale); | ||
1058 | } | ||
989 | } | 1059 | } |
990 | } | ||
991 | 1060 | ||
992 | // 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 |
993 | cursor_set_image(seat->cursor, NULL, NULL); | 1062 | cursor_set_image(seat->cursor, NULL, NULL); |
994 | cursor_set_image(seat->cursor, "left_ptr", NULL); | 1063 | cursor_set_image(seat->cursor, "default", NULL); |
995 | wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, | 1064 | wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, |
996 | seat->cursor->cursor->y); | 1065 | seat->cursor->cursor->y); |
1066 | } | ||
997 | } | 1067 | } |
998 | 1068 | ||
999 | bool seat_is_input_allowed(struct sway_seat *seat, | 1069 | bool seat_is_input_allowed(struct sway_seat *seat, |
1000 | struct wlr_surface *surface) { | 1070 | struct wlr_surface *surface) { |
1001 | struct wl_client *client = wl_resource_get_client(surface->resource); | 1071 | if (server.session_lock.lock) { |
1002 | return !seat->exclusive_client || seat->exclusive_client == client; | 1072 | return sway_session_lock_has_surface(server.session_lock.lock, surface); |
1073 | } | ||
1074 | return true; | ||
1003 | } | 1075 | } |
1004 | 1076 | ||
1005 | static void send_unfocus(struct sway_container *con, void *data) { | 1077 | static void send_unfocus(struct sway_container *con, void *data) { |
@@ -1058,15 +1130,7 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1058 | } | 1130 | } |
1059 | } | 1131 | } |
1060 | 1132 | ||
1061 | 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) { |
1062 | if (seat->focused_layer) { | ||
1063 | struct wlr_layer_surface_v1 *layer = seat->focused_layer; | ||
1064 | seat_set_focus_layer(seat, NULL); | ||
1065 | seat_set_focus(seat, node); | ||
1066 | seat_set_focus_layer(seat, layer); | ||
1067 | return; | ||
1068 | } | ||
1069 | |||
1070 | struct sway_node *last_focus = seat_get_focus(seat); | 1134 | struct sway_node *last_focus = seat_get_focus(seat); |
1071 | if (last_focus == node) { | 1135 | if (last_focus == node) { |
1072 | return; | 1136 | return; |
@@ -1086,30 +1150,19 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1086 | } | 1150 | } |
1087 | 1151 | ||
1088 | struct sway_workspace *new_workspace = node->type == N_WORKSPACE ? | 1152 | struct sway_workspace *new_workspace = node->type == N_WORKSPACE ? |
1089 | node->sway_workspace : node->sway_container->workspace; | 1153 | node->sway_workspace : node->sway_container->pending.workspace; |
1090 | struct sway_container *container = node->type == N_CONTAINER ? | 1154 | struct sway_container *container = node->type == N_CONTAINER ? |
1091 | node->sway_container : NULL; | 1155 | node->sway_container : NULL; |
1092 | 1156 | ||
1093 | // Deny setting focus to a view which is hidden by a fullscreen container | 1157 | // Deny setting focus to a view which is hidden by a fullscreen container or global |
1094 | if (new_workspace && new_workspace->fullscreen && container && | 1158 | if (container && container_obstructing_fullscreen_container(container)) { |
1095 | !container_is_fullscreen_or_child(container)) { | 1159 | return; |
1096 | // Unless it's a transient container | ||
1097 | if (!container_is_transient_for(container, new_workspace->fullscreen)) { | ||
1098 | return; | ||
1099 | } | ||
1100 | } | 1160 | } |
1161 | |||
1101 | // Deny setting focus to a workspace node when using fullscreen global | 1162 | // Deny setting focus to a workspace node when using fullscreen global |
1102 | if (root->fullscreen_global && !container && new_workspace) { | 1163 | if (root->fullscreen_global && !container && new_workspace) { |
1103 | return; | 1164 | return; |
1104 | } | 1165 | } |
1105 | // Deny setting focus to a view which is hidden by a fullscreen global | ||
1106 | if (root->fullscreen_global && container != root->fullscreen_global && | ||
1107 | !container_has_ancestor(container, root->fullscreen_global)) { | ||
1108 | // Unless it's a transient container | ||
1109 | if (!container_is_transient_for(container, root->fullscreen_global)) { | ||
1110 | return; | ||
1111 | } | ||
1112 | } | ||
1113 | 1166 | ||
1114 | struct sway_output *new_output = | 1167 | struct sway_output *new_output = |
1115 | new_workspace ? new_workspace->output : NULL; | 1168 | new_workspace ? new_workspace->output : NULL; |
@@ -1135,10 +1188,10 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1135 | // Put the container parents on the focus stack, then the workspace, then | 1188 | // Put the container parents on the focus stack, then the workspace, then |
1136 | // the focused container. | 1189 | // the focused container. |
1137 | if (container) { | 1190 | if (container) { |
1138 | struct sway_container *parent = container->parent; | 1191 | struct sway_container *parent = container->pending.parent; |
1139 | while (parent) { | 1192 | while (parent) { |
1140 | seat_set_raw_focus(seat, &parent->node); | 1193 | seat_set_raw_focus(seat, &parent->node); |
1141 | parent = parent->parent; | 1194 | parent = parent->pending.parent; |
1142 | } | 1195 | } |
1143 | } | 1196 | } |
1144 | if (new_workspace) { | 1197 | if (new_workspace) { |
@@ -1210,6 +1263,24 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1210 | } | 1263 | } |
1211 | } | 1264 | } |
1212 | 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 | |||
1213 | void seat_set_focus_container(struct sway_seat *seat, | 1284 | void seat_set_focus_container(struct sway_seat *seat, |
1214 | struct sway_container *con) { | 1285 | struct sway_container *con) { |
1215 | seat_set_focus(seat, con ? &con->node : NULL); | 1286 | seat_set_focus(seat, con ? &con->node : NULL); |
@@ -1234,7 +1305,8 @@ void seat_set_focus_surface(struct sway_seat *seat, | |||
1234 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); | 1305 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); |
1235 | } | 1306 | } |
1236 | 1307 | ||
1237 | seat_tablet_pads_notify_enter(seat, surface); | 1308 | sway_input_method_relay_set_focus(&seat->im_relay, surface); |
1309 | seat_tablet_pads_set_focus(seat, surface); | ||
1238 | } | 1310 | } |
1239 | 1311 | ||
1240 | void seat_set_focus_layer(struct sway_seat *seat, | 1312 | void seat_set_focus_layer(struct sway_seat *seat, |
@@ -1248,28 +1320,23 @@ void seat_set_focus_layer(struct sway_seat *seat, | |||
1248 | seat_set_focus(seat, previous); | 1320 | seat_set_focus(seat, previous); |
1249 | } | 1321 | } |
1250 | return; | 1322 | return; |
1251 | } else if (!layer || seat->focused_layer == layer) { | 1323 | } else if (!layer) { |
1252 | return; | 1324 | return; |
1253 | } | 1325 | } |
1254 | assert(layer->mapped); | 1326 | assert(layer->surface->mapped); |
1255 | seat_set_focus_surface(seat, layer->surface, true); | 1327 | if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP && |
1256 | if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { | 1328 | layer->current.keyboard_interactive |
1257 | seat->focused_layer = layer; | 1329 | == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { |
1330 | seat->has_exclusive_layer = true; | ||
1258 | } | 1331 | } |
1259 | } | 1332 | if (seat->focused_layer == layer) { |
1260 | |||
1261 | void seat_set_exclusive_client(struct sway_seat *seat, | ||
1262 | struct wl_client *client) { | ||
1263 | if (!client) { | ||
1264 | seat->exclusive_client = client; | ||
1265 | // Triggers a refocus of the topmost surface layer if necessary | ||
1266 | // TODO: Make layer surface focus per-output based on cursor position | ||
1267 | for (int i = 0; i < root->outputs->length; ++i) { | ||
1268 | struct sway_output *output = root->outputs->items[i]; | ||
1269 | arrange_layers(output); | ||
1270 | } | ||
1271 | return; | 1333 | return; |
1272 | } | 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) { | ||
1273 | if (seat->focused_layer) { | 1340 | if (seat->focused_layer) { |
1274 | if (wl_resource_get_client(seat->focused_layer->resource) != client) { | 1341 | if (wl_resource_get_client(seat->focused_layer->resource) != client) { |
1275 | seat_set_focus_layer(seat, NULL); | 1342 | seat_set_focus_layer(seat, NULL); |
@@ -1296,7 +1363,6 @@ void seat_set_exclusive_client(struct sway_seat *seat, | |||
1296 | now.tv_nsec / 1000, point->touch_id); | 1363 | now.tv_nsec / 1000, point->touch_id); |
1297 | } | 1364 | } |
1298 | } | 1365 | } |
1299 | seat->exclusive_client = client; | ||
1300 | } | 1366 | } |
1301 | 1367 | ||
1302 | struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, | 1368 | struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, |
@@ -1326,7 +1392,7 @@ struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, | |||
1326 | struct sway_node *node = current->node; | 1392 | struct sway_node *node = current->node; |
1327 | if (node->type == N_CONTAINER && | 1393 | if (node->type == N_CONTAINER && |
1328 | !container_is_floating_or_child(node->sway_container) && | 1394 | !container_is_floating_or_child(node->sway_container) && |
1329 | node->sway_container->workspace == workspace) { | 1395 | node->sway_container->pending.workspace == workspace) { |
1330 | return node->sway_container; | 1396 | return node->sway_container; |
1331 | } | 1397 | } |
1332 | } | 1398 | } |
@@ -1343,7 +1409,7 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, | |||
1343 | struct sway_node *node = current->node; | 1409 | struct sway_node *node = current->node; |
1344 | if (node->type == N_CONTAINER && | 1410 | if (node->type == N_CONTAINER && |
1345 | container_is_floating_or_child(node->sway_container) && | 1411 | container_is_floating_or_child(node->sway_container) && |
1346 | node->sway_container->workspace == workspace) { | 1412 | node->sway_container->pending.workspace == workspace) { |
1347 | return node->sway_container; | 1413 | return node->sway_container; |
1348 | } | 1414 | } |
1349 | } | 1415 | } |
@@ -1377,9 +1443,8 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) { | |||
1377 | if (!seat->has_focus) { | 1443 | if (!seat->has_focus) { |
1378 | return NULL; | 1444 | return NULL; |
1379 | } | 1445 | } |
1380 | if (wl_list_empty(&seat->focus_stack)) { | 1446 | sway_assert(!wl_list_empty(&seat->focus_stack), |
1381 | return NULL; | 1447 | "focus_stack is empty, but has_focus is true"); |
1382 | } | ||
1383 | struct sway_seat_node *current = | 1448 | struct sway_seat_node *current = |
1384 | wl_container_of(seat->focus_stack.next, current, link); | 1449 | wl_container_of(seat->focus_stack.next, current, link); |
1385 | return current->node; | 1450 | return current->node; |
@@ -1391,7 +1456,7 @@ struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { | |||
1391 | return NULL; | 1456 | return NULL; |
1392 | } | 1457 | } |
1393 | if (focus->type == N_CONTAINER) { | 1458 | if (focus->type == N_CONTAINER) { |
1394 | return focus->sway_container->workspace; | 1459 | return focus->sway_container->pending.workspace; |
1395 | } | 1460 | } |
1396 | if (focus->type == N_WORKSPACE) { | 1461 | if (focus->type == N_WORKSPACE) { |
1397 | return focus->sway_workspace; | 1462 | return focus->sway_workspace; |
@@ -1404,8 +1469,8 @@ struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat) { | |||
1404 | wl_list_for_each(current, &seat->focus_stack, link) { | 1469 | wl_list_for_each(current, &seat->focus_stack, link) { |
1405 | struct sway_node *node = current->node; | 1470 | struct sway_node *node = current->node; |
1406 | if (node->type == N_CONTAINER && | 1471 | if (node->type == N_CONTAINER && |
1407 | node->sway_container->workspace) { | 1472 | node->sway_container->pending.workspace) { |
1408 | return node->sway_container->workspace; | 1473 | return node->sway_container->pending.workspace; |
1409 | } else if (node->type == N_WORKSPACE) { | 1474 | } else if (node->type == N_WORKSPACE) { |
1410 | return node->sway_workspace; | 1475 | return node->sway_workspace; |
1411 | } | 1476 | } |
@@ -1464,7 +1529,7 @@ struct seat_config *seat_get_config_by_name(const char *name) { | |||
1464 | } | 1529 | } |
1465 | 1530 | ||
1466 | 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, |
1467 | uint32_t button, enum wlr_button_state state) { | 1532 | uint32_t button, enum wl_pointer_button_state state) { |
1468 | 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, |
1469 | time_msec, button, state); | 1534 | time_msec, button, state); |
1470 | } | 1535 | } |
@@ -1501,7 +1566,7 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) { | |||
1501 | 1566 | ||
1502 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, | 1567 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, |
1503 | struct wlr_input_device *device, uint32_t button, | 1568 | struct wlr_input_device *device, uint32_t button, |
1504 | enum wlr_button_state state) { | 1569 | enum wl_pointer_button_state state) { |
1505 | if (seat->seatop_impl->button) { | 1570 | if (seat->seatop_impl->button) { |
1506 | seat->seatop_impl->button(seat, time_msec, device, button, state); | 1571 | seat->seatop_impl->button(seat, time_msec, device, button, state); |
1507 | } | 1572 | } |
@@ -1514,12 +1579,38 @@ void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
1514 | } | 1579 | } |
1515 | 1580 | ||
1516 | void seatop_pointer_axis(struct sway_seat *seat, | 1581 | void seatop_pointer_axis(struct sway_seat *seat, |
1517 | struct wlr_event_pointer_axis *event) { | 1582 | struct wlr_pointer_axis_event *event) { |
1518 | if (seat->seatop_impl->pointer_axis) { | 1583 | if (seat->seatop_impl->pointer_axis) { |
1519 | seat->seatop_impl->pointer_axis(seat, event); | 1584 | seat->seatop_impl->pointer_axis(seat, event); |
1520 | } | 1585 | } |
1521 | } | 1586 | } |
1522 | 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 | |||
1523 | void seatop_tablet_tool_tip(struct sway_seat *seat, | 1614 | void seatop_tablet_tool_tip(struct sway_seat *seat, |
1524 | struct sway_tablet_tool *tool, uint32_t time_msec, | 1615 | struct sway_tablet_tool *tool, uint32_t time_msec, |
1525 | enum wlr_tablet_tool_tip_state state) { | 1616 | enum wlr_tablet_tool_tip_state state) { |
@@ -1537,6 +1628,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat, | |||
1537 | } | 1628 | } |
1538 | } | 1629 | } |
1539 | 1630 | ||
1631 | void seatop_hold_begin(struct sway_seat *seat, | ||
1632 | struct wlr_pointer_hold_begin_event *event) { | ||
1633 | if (seat->seatop_impl->hold_begin) { | ||
1634 | seat->seatop_impl->hold_begin(seat, event); | ||
1635 | } | ||
1636 | } | ||
1637 | |||
1638 | void seatop_hold_end(struct sway_seat *seat, | ||
1639 | struct wlr_pointer_hold_end_event *event) { | ||
1640 | if (seat->seatop_impl->hold_end) { | ||
1641 | seat->seatop_impl->hold_end(seat, event); | ||
1642 | } | ||
1643 | } | ||
1644 | |||
1645 | void seatop_pinch_begin(struct sway_seat *seat, | ||
1646 | struct wlr_pointer_pinch_begin_event *event) { | ||
1647 | if (seat->seatop_impl->pinch_begin) { | ||
1648 | seat->seatop_impl->pinch_begin(seat, event); | ||
1649 | } | ||
1650 | } | ||
1651 | |||
1652 | void seatop_pinch_update(struct sway_seat *seat, | ||
1653 | struct wlr_pointer_pinch_update_event *event) { | ||
1654 | if (seat->seatop_impl->pinch_update) { | ||
1655 | seat->seatop_impl->pinch_update(seat, event); | ||
1656 | } | ||
1657 | } | ||
1658 | |||
1659 | void seatop_pinch_end(struct sway_seat *seat, | ||
1660 | struct wlr_pointer_pinch_end_event *event) { | ||
1661 | if (seat->seatop_impl->pinch_end) { | ||
1662 | seat->seatop_impl->pinch_end(seat, event); | ||
1663 | } | ||
1664 | } | ||
1665 | |||
1666 | void seatop_swipe_begin(struct sway_seat *seat, | ||
1667 | struct wlr_pointer_swipe_begin_event *event) { | ||
1668 | if (seat->seatop_impl->swipe_begin) { | ||
1669 | seat->seatop_impl->swipe_begin(seat, event); | ||
1670 | } | ||
1671 | } | ||
1672 | |||
1673 | void seatop_swipe_update(struct sway_seat *seat, | ||
1674 | struct wlr_pointer_swipe_update_event *event) { | ||
1675 | if (seat->seatop_impl->swipe_update) { | ||
1676 | seat->seatop_impl->swipe_update(seat, event); | ||
1677 | } | ||
1678 | } | ||
1679 | |||
1680 | void seatop_swipe_end(struct sway_seat *seat, | ||
1681 | struct wlr_pointer_swipe_end_event *event) { | ||
1682 | if (seat->seatop_impl->swipe_end) { | ||
1683 | seat->seatop_impl->swipe_end(seat, event); | ||
1684 | } | ||
1685 | } | ||
1686 | |||
1540 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { | 1687 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { |
1541 | if (seat->seatop_impl->rebase) { | 1688 | if (seat->seatop_impl->rebase) { |
1542 | seat->seatop_impl->rebase(seat, time_msec); | 1689 | seat->seatop_impl->rebase(seat, time_msec); |
@@ -1552,13 +1699,6 @@ void seatop_end(struct sway_seat *seat) { | |||
1552 | seat->seatop_impl = NULL; | 1699 | seat->seatop_impl = NULL; |
1553 | } | 1700 | } |
1554 | 1701 | ||
1555 | void seatop_render(struct sway_seat *seat, struct sway_output *output, | ||
1556 | pixman_region32_t *damage) { | ||
1557 | if (seat->seatop_impl->render) { | ||
1558 | seat->seatop_impl->render(seat, output, damage); | ||
1559 | } | ||
1560 | } | ||
1561 | |||
1562 | bool seatop_allows_set_cursor(struct sway_seat *seat) { | 1702 | bool seatop_allows_set_cursor(struct sway_seat *seat) { |
1563 | return seat->seatop_impl->allow_set_cursor; | 1703 | return seat->seatop_impl->allow_set_cursor; |
1564 | } | 1704 | } |
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index a583ed62..0c6f7c5e 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c | |||
@@ -1,13 +1,17 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <libevdev/libevdev.h> | 2 | #include <libevdev/libevdev.h> |
4 | #include <wlr/types/wlr_cursor.h> | 3 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/types/wlr_subcompositor.h> | ||
5 | #include <wlr/types/wlr_tablet_v2.h> | 5 | #include <wlr/types/wlr_tablet_v2.h> |
6 | #include <wlr/types/wlr_xcursor_manager.h> | 6 | #include <wlr/types/wlr_xcursor_manager.h> |
7 | #include "gesture.h" | ||
8 | #include "sway/desktop/transaction.h" | ||
7 | #include "sway/input/cursor.h" | 9 | #include "sway/input/cursor.h" |
8 | #include "sway/input/seat.h" | 10 | #include "sway/input/seat.h" |
9 | #include "sway/input/tablet.h" | 11 | #include "sway/input/tablet.h" |
12 | #include "sway/layers.h" | ||
10 | #include "sway/output.h" | 13 | #include "sway/output.h" |
14 | #include "sway/scene_descriptor.h" | ||
11 | #include "sway/tree/view.h" | 15 | #include "sway/tree/view.h" |
12 | #include "sway/tree/workspace.h" | 16 | #include "sway/tree/workspace.h" |
13 | #include "log.h" | 17 | #include "log.h" |
@@ -19,6 +23,7 @@ struct seatop_default_event { | |||
19 | struct sway_node *previous_node; | 23 | struct sway_node *previous_node; |
20 | uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; | 24 | uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; |
21 | size_t pressed_button_count; | 25 | size_t pressed_button_count; |
26 | struct gesture_tracker gestures; | ||
22 | }; | 27 | }; |
23 | 28 | ||
24 | /*-----------------------------------------\ | 29 | /*-----------------------------------------\ |
@@ -50,6 +55,9 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { | |||
50 | while (cont) { | 55 | while (cont) { |
51 | if (container_parent_layout(cont) == layout) { | 56 | if (container_parent_layout(cont) == layout) { |
52 | list_t *siblings = container_get_siblings(cont); | 57 | list_t *siblings = container_get_siblings(cont); |
58 | if (!siblings) { | ||
59 | return false; | ||
60 | } | ||
53 | int index = list_find(siblings, cont); | 61 | int index = list_find(siblings, cont); |
54 | if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { | 62 | if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { |
55 | return false; | 63 | return false; |
@@ -59,7 +67,7 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { | |||
59 | return false; | 67 | return false; |
60 | } | 68 | } |
61 | } | 69 | } |
62 | cont = cont->parent; | 70 | cont = cont->pending.parent; |
63 | } | 71 | } |
64 | return true; | 72 | return true; |
65 | } | 73 | } |
@@ -69,25 +77,25 @@ static enum wlr_edges find_edge(struct sway_container *cont, | |||
69 | if (!cont->view || (surface && cont->view->surface != surface)) { | 77 | if (!cont->view || (surface && cont->view->surface != surface)) { |
70 | return WLR_EDGE_NONE; | 78 | return WLR_EDGE_NONE; |
71 | } | 79 | } |
72 | if (cont->border == B_NONE || !cont->border_thickness || | 80 | if (cont->pending.border == B_NONE || !cont->pending.border_thickness || |
73 | cont->border == B_CSD) { | 81 | cont->pending.border == B_CSD) { |
74 | return WLR_EDGE_NONE; | 82 | return WLR_EDGE_NONE; |
75 | } | 83 | } |
76 | if (cont->fullscreen_mode) { | 84 | if (cont->pending.fullscreen_mode) { |
77 | return WLR_EDGE_NONE; | 85 | return WLR_EDGE_NONE; |
78 | } | 86 | } |
79 | 87 | ||
80 | enum wlr_edges edge = 0; | 88 | enum wlr_edges edge = 0; |
81 | if (cursor->cursor->x < cont->x + cont->border_thickness) { | 89 | if (cursor->cursor->x < cont->pending.x + cont->pending.border_thickness) { |
82 | edge |= WLR_EDGE_LEFT; | 90 | edge |= WLR_EDGE_LEFT; |
83 | } | 91 | } |
84 | if (cursor->cursor->y < cont->y + cont->border_thickness) { | 92 | if (cursor->cursor->y < cont->pending.y + cont->pending.border_thickness) { |
85 | edge |= WLR_EDGE_TOP; | 93 | edge |= WLR_EDGE_TOP; |
86 | } | 94 | } |
87 | if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { | 95 | if (cursor->cursor->x >= cont->pending.x + cont->pending.width - cont->pending.border_thickness) { |
88 | edge |= WLR_EDGE_RIGHT; | 96 | edge |= WLR_EDGE_RIGHT; |
89 | } | 97 | } |
90 | if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { | 98 | if (cursor->cursor->y >= cont->pending.y + cont->pending.height - cont->pending.border_thickness) { |
91 | edge |= WLR_EDGE_BOTTOM; | 99 | edge |= WLR_EDGE_BOTTOM; |
92 | } | 100 | } |
93 | 101 | ||
@@ -225,13 +233,15 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
225 | struct sway_container *cont = node && node->type == N_CONTAINER ? | 233 | struct sway_container *cont = node && node->type == N_CONTAINER ? |
226 | node->sway_container : NULL; | 234 | node->sway_container : NULL; |
227 | 235 | ||
228 | 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) { | ||
229 | // Handle tapping a layer surface | 242 | // Handle tapping a layer surface |
230 | struct wlr_layer_surface_v1 *layer = | 243 | seat_set_focus_layer(seat, layer); |
231 | wlr_layer_surface_v1_from_wlr_surface(surface); | 244 | transaction_commit_dirty(); |
232 | if (layer->current.keyboard_interactive) { | ||
233 | seat_set_focus_layer(seat, layer); | ||
234 | } | ||
235 | } else if (cont) { | 245 | } else if (cont) { |
236 | bool is_floating_or_child = container_is_floating_or_child(cont); | 246 | bool is_floating_or_child = container_is_floating_or_child(cont); |
237 | bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); | 247 | bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); |
@@ -249,26 +259,24 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
249 | 259 | ||
250 | // Handle moving a tiling container | 260 | // Handle moving a tiling container |
251 | if (config->tiling_drag && mod_pressed && !is_floating_or_child && | 261 | if (config->tiling_drag && mod_pressed && !is_floating_or_child && |
252 | cont->fullscreen_mode == FULLSCREEN_NONE) { | 262 | cont->pending.fullscreen_mode == FULLSCREEN_NONE) { |
253 | seatop_begin_move_tiling(seat, cont); | 263 | seatop_begin_move_tiling(seat, cont); |
254 | return; | 264 | return; |
255 | } | 265 | } |
256 | 266 | ||
257 | // Handle tapping on a container surface | 267 | // Handle tapping on a container surface |
258 | seat_set_focus_container(seat, cont); | 268 | seat_set_focus_container(seat, cont); |
259 | seatop_begin_down(seat, node->sway_container, time_msec, sx, sy); | 269 | seatop_begin_down(seat, node->sway_container, sx, sy); |
260 | } | 270 | } |
261 | #if HAVE_XWAYLAND | 271 | #if HAVE_XWAYLAND |
262 | // Handle tapping on an xwayland unmanaged view | 272 | // Handle tapping on an xwayland unmanaged view |
263 | else if (wlr_surface_is_xwayland_surface(surface)) { | 273 | else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && |
264 | struct wlr_xwayland_surface *xsurface = | 274 | xsurface->override_redirect && |
265 | wlr_xwayland_surface_from_wlr_surface(surface); | 275 | wlr_xwayland_or_surface_wants_focus(xsurface)) { |
266 | if (xsurface->override_redirect && | 276 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; |
267 | wlr_xwayland_or_surface_wants_focus(xsurface)) { | 277 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
268 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | 278 | seat_set_focus_surface(seat, xsurface->surface, false); |
269 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 279 | transaction_commit_dirty(); |
270 | seat_set_focus_surface(seat, xsurface->surface, false); | ||
271 | } | ||
272 | } | 280 | } |
273 | #endif | 281 | #endif |
274 | 282 | ||
@@ -282,7 +290,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
282 | 290 | ||
283 | static bool trigger_pointer_button_binding(struct sway_seat *seat, | 291 | static bool trigger_pointer_button_binding(struct sway_seat *seat, |
284 | struct wlr_input_device *device, uint32_t button, | 292 | struct wlr_input_device *device, uint32_t button, |
285 | enum wlr_button_state state, uint32_t modifiers, | 293 | enum wl_pointer_button_state state, uint32_t modifiers, |
286 | 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) { |
287 | // 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 |
288 | // pointer input for one. Emulated input should not trigger bindings. The | 296 | // pointer input for one. Emulated input should not trigger bindings. The |
@@ -296,7 +304,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat, | |||
296 | char *device_identifier = device ? input_device_get_identifier(device) | 304 | char *device_identifier = device ? input_device_get_identifier(device) |
297 | : strdup("*"); | 305 | : strdup("*"); |
298 | struct sway_binding *binding = NULL; | 306 | struct sway_binding *binding = NULL; |
299 | if (state == WLR_BUTTON_PRESSED) { | 307 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { |
300 | state_add_button(e, button); | 308 | state_add_button(e, button); |
301 | binding = get_active_mouse_binding(e, | 309 | binding = get_active_mouse_binding(e, |
302 | config->current_mode->mouse_bindings, modifiers, false, | 310 | config->current_mode->mouse_bindings, modifiers, false, |
@@ -321,7 +329,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat, | |||
321 | 329 | ||
322 | 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, |
323 | struct wlr_input_device *device, uint32_t button, | 331 | struct wlr_input_device *device, uint32_t button, |
324 | enum wlr_button_state state) { | 332 | enum wl_pointer_button_state state) { |
325 | struct sway_cursor *cursor = seat->cursor; | 333 | struct sway_cursor *cursor = seat->cursor; |
326 | 334 | ||
327 | // Determine what's under the cursor | 335 | // Determine what's under the cursor |
@@ -354,19 +362,23 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
354 | 362 | ||
355 | // Handle clicking an empty workspace | 363 | // Handle clicking an empty workspace |
356 | if (node && node->type == N_WORKSPACE) { | 364 | if (node && node->type == N_WORKSPACE) { |
357 | if (state == WLR_BUTTON_PRESSED) { | 365 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { |
358 | seat_set_focus(seat, node); | 366 | seat_set_focus(seat, node); |
367 | transaction_commit_dirty(); | ||
359 | } | 368 | } |
360 | seat_pointer_notify_button(seat, time_msec, button, state); | 369 | seat_pointer_notify_button(seat, time_msec, button, state); |
361 | return; | 370 | return; |
362 | } | 371 | } |
363 | 372 | ||
364 | // Handle clicking a layer surface | 373 | // Handle clicking a layer surface and its popups/subsurfaces |
365 | if (surface && wlr_surface_is_layer_surface(surface)) { | 374 | struct wlr_layer_surface_v1 *layer = NULL; |
366 | struct wlr_layer_surface_v1 *layer = | 375 | if ((layer = toplevel_layer_surface_from_surface(surface))) { |
367 | wlr_layer_surface_v1_from_wlr_surface(surface); | ||
368 | if (layer->current.keyboard_interactive) { | 376 | if (layer->current.keyboard_interactive) { |
369 | seat_set_focus_layer(seat, layer); | 377 | seat_set_focus_layer(seat, layer); |
378 | transaction_commit_dirty(); | ||
379 | } | ||
380 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { | ||
381 | seatop_begin_down_on_surface(seat, surface, sx, sy); | ||
370 | } | 382 | } |
371 | seat_pointer_notify_button(seat, time_msec, button, state); | 383 | seat_pointer_notify_button(seat, time_msec, button, state); |
372 | return; | 384 | return; |
@@ -374,14 +386,14 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
374 | 386 | ||
375 | // Handle tiling resize via border | 387 | // Handle tiling resize via border |
376 | if (cont && resize_edge && button == BTN_LEFT && | 388 | if (cont && resize_edge && button == BTN_LEFT && |
377 | state == WLR_BUTTON_PRESSED && !is_floating) { | 389 | state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) { |
378 | // 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 |
379 | // 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 |
380 | // 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. |
381 | struct sway_container *cont_to_focus = cont; | 393 | struct sway_container *cont_to_focus = cont; |
382 | enum sway_container_layout layout = container_parent_layout(cont); | 394 | enum sway_container_layout layout = container_parent_layout(cont); |
383 | if (layout == L_TABBED || layout == L_STACKED) { | 395 | if (layout == L_TABBED || layout == L_STACKED) { |
384 | cont_to_focus = seat_get_focus_inactive_view(seat, &cont->parent->node); | 396 | cont_to_focus = seat_get_focus_inactive_view(seat, &cont->pending.parent->node); |
385 | } | 397 | } |
386 | 398 | ||
387 | seat_set_focus_container(seat, cont_to_focus); | 399 | seat_set_focus_container(seat, cont_to_focus); |
@@ -392,14 +404,14 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
392 | // Handle tiling resize via mod | 404 | // Handle tiling resize via mod |
393 | bool mod_pressed = modifiers & config->floating_mod; | 405 | bool mod_pressed = modifiers & config->floating_mod; |
394 | if (cont && !is_floating_or_child && mod_pressed && | 406 | if (cont && !is_floating_or_child && mod_pressed && |
395 | state == WLR_BUTTON_PRESSED) { | 407 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
396 | uint32_t btn_resize = config->floating_mod_inverse ? | 408 | uint32_t btn_resize = config->floating_mod_inverse ? |
397 | BTN_LEFT : BTN_RIGHT; | 409 | BTN_LEFT : BTN_RIGHT; |
398 | if (button == btn_resize) { | 410 | if (button == btn_resize) { |
399 | edge = 0; | 411 | edge = 0; |
400 | edge |= cursor->cursor->x > cont->x + cont->width / 2 ? | 412 | edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ? |
401 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; | 413 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; |
402 | edge |= cursor->cursor->y > cont->y + cont->height / 2 ? | 414 | edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ? |
403 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; | 415 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; |
404 | 416 | ||
405 | const char *image = NULL; | 417 | const char *image = NULL; |
@@ -419,13 +431,31 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
419 | } | 431 | } |
420 | } | 432 | } |
421 | 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 | |||
422 | // Handle beginning floating move | 454 | // Handle beginning floating move |
423 | if (cont && is_floating_or_child && !is_fullscreen_or_child && | 455 | if (cont && is_floating_or_child && !is_fullscreen_or_child && |
424 | state == WLR_BUTTON_PRESSED) { | 456 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
425 | 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; |
426 | if (button == btn_move && (mod_pressed || on_titlebar)) { | 458 | if (button == btn_move && (mod_pressed || on_titlebar)) { |
427 | seat_set_focus_container(seat, | ||
428 | seat_get_focus_inactive_view(seat, &cont->node)); | ||
429 | seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); | 459 | seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); |
430 | return; | 460 | return; |
431 | } | 461 | } |
@@ -433,9 +463,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
433 | 463 | ||
434 | // Handle beginning floating resize | 464 | // Handle beginning floating resize |
435 | if (cont && is_floating_or_child && !is_fullscreen_or_child && | 465 | if (cont && is_floating_or_child && !is_fullscreen_or_child && |
436 | state == WLR_BUTTON_PRESSED) { | 466 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
437 | // Via border | 467 | // Via border |
438 | 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); | ||
439 | seatop_begin_resize_floating(seat, cont, resize_edge); | 470 | seatop_begin_resize_floating(seat, cont, resize_edge); |
440 | return; | 471 | return; |
441 | } | 472 | } |
@@ -446,10 +477,11 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
446 | if (mod_pressed && button == btn_resize) { | 477 | if (mod_pressed && button == btn_resize) { |
447 | struct sway_container *floater = container_toplevel_ancestor(cont); | 478 | struct sway_container *floater = container_toplevel_ancestor(cont); |
448 | edge = 0; | 479 | edge = 0; |
449 | edge |= cursor->cursor->x > floater->x + floater->width / 2 ? | 480 | edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ? |
450 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; | 481 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; |
451 | edge |= cursor->cursor->y > floater->y + floater->height / 2 ? | 482 | edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? |
452 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; | 483 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; |
484 | seat_set_focus_container(seat, floater); | ||
453 | seatop_begin_resize_floating(seat, floater, edge); | 485 | seatop_begin_resize_floating(seat, floater, edge); |
454 | return; | 486 | return; |
455 | } | 487 | } |
@@ -457,53 +489,43 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
457 | 489 | ||
458 | // Handle moving a tiling container | 490 | // Handle moving a tiling container |
459 | if (config->tiling_drag && (mod_pressed || on_titlebar) && | 491 | if (config->tiling_drag && (mod_pressed || on_titlebar) && |
460 | state == WLR_BUTTON_PRESSED && !is_floating_or_child && | 492 | state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child && |
461 | cont && cont->fullscreen_mode == FULLSCREEN_NONE) { | 493 | cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { |
462 | struct sway_container *focus = seat_get_focused_container(seat); | 494 | // If moving a container by its title bar, use a threshold for the drag |
463 | bool focused = focus == cont || container_has_ancestor(focus, cont); | ||
464 | if (on_titlebar && !focused) { | ||
465 | node = seat_get_focus_inactive(seat, &cont->node); | ||
466 | seat_set_focus(seat, node); | ||
467 | } | ||
468 | |||
469 | // If moving a container by it's title bar, use a threshold for the drag | ||
470 | if (!mod_pressed && config->tiling_drag_threshold > 0) { | 495 | if (!mod_pressed && config->tiling_drag_threshold > 0) { |
471 | seatop_begin_move_tiling_threshold(seat, cont); | 496 | seatop_begin_move_tiling_threshold(seat, cont); |
472 | } else { | 497 | } else { |
473 | seatop_begin_move_tiling(seat, cont); | 498 | seatop_begin_move_tiling(seat, cont); |
474 | } | 499 | } |
500 | |||
475 | return; | 501 | return; |
476 | } | 502 | } |
477 | 503 | ||
478 | // Handle mousedown on a container surface | 504 | // Handle mousedown on a container surface |
479 | if (surface && cont && state == WLR_BUTTON_PRESSED) { | 505 | if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { |
480 | seat_set_focus_container(seat, cont); | 506 | seatop_begin_down(seat, cont, sx, sy); |
481 | seatop_begin_down(seat, cont, time_msec, sx, sy); | 507 | seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED); |
482 | seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); | ||
483 | return; | 508 | return; |
484 | } | 509 | } |
485 | 510 | ||
486 | // Handle clicking a container surface or decorations | 511 | // Handle clicking a container surface or decorations |
487 | if (cont && state == WLR_BUTTON_PRESSED) { | 512 | if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { |
488 | node = seat_get_focus_inactive(seat, &cont->node); | ||
489 | seat_set_focus(seat, node); | ||
490 | seat_pointer_notify_button(seat, time_msec, button, state); | 513 | seat_pointer_notify_button(seat, time_msec, button, state); |
491 | return; | 514 | return; |
492 | } | 515 | } |
493 | 516 | ||
494 | #if HAVE_XWAYLAND | 517 | #if HAVE_XWAYLAND |
495 | // Handle clicking on xwayland unmanaged view | 518 | // Handle clicking on xwayland unmanaged view |
496 | if (surface && wlr_surface_is_xwayland_surface(surface)) { | 519 | struct wlr_xwayland_surface *xsurface; |
497 | struct wlr_xwayland_surface *xsurface = | 520 | if (surface && |
498 | wlr_xwayland_surface_from_wlr_surface(surface); | 521 | (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && |
499 | if (xsurface->override_redirect && | 522 | xsurface->override_redirect && |
500 | wlr_xwayland_or_surface_wants_focus(xsurface)) { | 523 | wlr_xwayland_or_surface_wants_focus(xsurface)) { |
501 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | 524 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; |
502 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 525 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
503 | seat_set_focus_surface(seat, xsurface->surface, false); | 526 | seat_set_focus_surface(seat, xsurface->surface, false); |
504 | seat_pointer_notify_button(seat, time_msec, button, state); | 527 | transaction_commit_dirty(); |
505 | return; | 528 | seat_pointer_notify_button(seat, time_msec, button, state); |
506 | } | ||
507 | } | 529 | } |
508 | #endif | 530 | #endif |
509 | 531 | ||
@@ -526,10 +548,26 @@ static void check_focus_follows_mouse(struct sway_seat *seat, | |||
526 | if (wlr_output == NULL) { | 548 | if (wlr_output == NULL) { |
527 | return; | 549 | return; |
528 | } | 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 | |||
529 | struct sway_output *hovered_output = wlr_output->data; | 566 | struct sway_output *hovered_output = wlr_output->data; |
530 | if (focus && hovered_output != node_get_output(focus)) { | 567 | if (focus && hovered_output != node_get_output(focus)) { |
531 | struct sway_workspace *ws = output_get_active_workspace(hovered_output); | 568 | struct sway_workspace *ws = output_get_active_workspace(hovered_output); |
532 | seat_set_focus(seat, &ws->node); | 569 | seat_set_focus(seat, &ws->node); |
570 | transaction_commit_dirty(); | ||
533 | } | 571 | } |
534 | return; | 572 | return; |
535 | } | 573 | } |
@@ -541,6 +579,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat, | |||
541 | struct sway_output *hovered_output = node_get_output(hovered_node); | 579 | struct sway_output *hovered_output = node_get_output(hovered_node); |
542 | if (hovered_output != focused_output) { | 580 | if (hovered_output != focused_output) { |
543 | seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); | 581 | seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); |
582 | transaction_commit_dirty(); | ||
544 | } | 583 | } |
545 | return; | 584 | return; |
546 | } | 585 | } |
@@ -556,6 +595,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat, | |||
556 | if (hovered_node != e->previous_node || | 595 | if (hovered_node != e->previous_node || |
557 | config->focus_follows_mouse == FOLLOWS_ALWAYS) { | 596 | config->focus_follows_mouse == FOLLOWS_ALWAYS) { |
558 | seat_set_focus(seat, hovered_node); | 597 | seat_set_focus(seat, hovered_node); |
598 | transaction_commit_dirty(); | ||
559 | } | 599 | } |
560 | } | 600 | } |
561 | } | 601 | } |
@@ -583,12 +623,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
583 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 623 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
584 | } | 624 | } |
585 | 625 | ||
586 | struct sway_drag_icon *drag_icon; | 626 | drag_icons_update_position(seat); |
587 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
588 | if (drag_icon->seat == seat) { | ||
589 | drag_icon_update_position(drag_icon); | ||
590 | } | ||
591 | } | ||
592 | 627 | ||
593 | e->previous_node = node; | 628 | e->previous_node = node; |
594 | } | 629 | } |
@@ -618,25 +653,50 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, | |||
618 | 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); |
619 | } | 654 | } |
620 | 655 | ||
621 | struct sway_drag_icon *drag_icon; | 656 | drag_icons_update_position(seat); |
622 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
623 | if (drag_icon->seat == seat) { | ||
624 | drag_icon_update_position(drag_icon); | ||
625 | } | ||
626 | } | ||
627 | 657 | ||
628 | e->previous_node = node; | 658 | e->previous_node = node; |
629 | } | 659 | } |
630 | 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 | |||
631 | /*----------------------------------------\ | 691 | /*----------------------------------------\ |
632 | * Functions used by handle_pointer_axis / | 692 | * Functions used by handle_pointer_axis / |
633 | *--------------------------------------*/ | 693 | *--------------------------------------*/ |
634 | 694 | ||
635 | static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { | 695 | static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { |
636 | switch (event->orientation) { | 696 | switch (event->orientation) { |
637 | case WLR_AXIS_ORIENTATION_VERTICAL: | 697 | case WL_POINTER_AXIS_VERTICAL_SCROLL: |
638 | return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; | 698 | return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; |
639 | case WLR_AXIS_ORIENTATION_HORIZONTAL: | 699 | case WL_POINTER_AXIS_HORIZONTAL_SCROLL: |
640 | return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; | 700 | return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; |
641 | default: | 701 | default: |
642 | sway_log(SWAY_DEBUG, "Unknown axis orientation"); | 702 | sway_log(SWAY_DEBUG, "Unknown axis orientation"); |
@@ -645,9 +705,9 @@ static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { | |||
645 | } | 705 | } |
646 | 706 | ||
647 | static void handle_pointer_axis(struct sway_seat *seat, | 707 | static void handle_pointer_axis(struct sway_seat *seat, |
648 | struct wlr_event_pointer_axis *event) { | 708 | struct wlr_pointer_axis_event *event) { |
649 | struct sway_input_device *input_device = | 709 | struct sway_input_device *input_device = |
650 | event->device ? event->device->data : NULL; | 710 | event->pointer ? event->pointer->base.data : NULL; |
651 | struct input_config *ic = | 711 | struct input_config *ic = |
652 | input_device ? input_device_get_config(input_device) : NULL; | 712 | input_device ? input_device_get_config(input_device) : NULL; |
653 | struct sway_cursor *cursor = seat->cursor; | 713 | struct sway_cursor *cursor = seat->cursor; |
@@ -664,7 +724,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
664 | bool on_border = edge != WLR_EDGE_NONE; | 724 | bool on_border = edge != WLR_EDGE_NONE; |
665 | bool on_titlebar = cont && !on_border && !surface; | 725 | bool on_titlebar = cont && !on_border && !surface; |
666 | bool on_titlebar_border = cont && on_border && | 726 | bool on_titlebar_border = cont && on_border && |
667 | cursor->cursor->y < cont->content_y; | 727 | cursor->cursor->y < cont->pending.content_y; |
668 | bool on_contents = cont && !on_border && surface; | 728 | bool on_contents = cont && !on_border && surface; |
669 | bool on_workspace = node && node->type == N_WORKSPACE; | 729 | bool on_workspace = node && node->type == N_WORKSPACE; |
670 | float scroll_factor = | 730 | float scroll_factor = |
@@ -693,6 +753,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
693 | 753 | ||
694 | // 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) |
695 | if (!handled && (on_titlebar || on_titlebar_border)) { | 755 | if (!handled && (on_titlebar || on_titlebar_border)) { |
756 | struct sway_node *new_focus; | ||
696 | enum sway_container_layout layout = container_parent_layout(cont); | 757 | enum sway_container_layout layout = container_parent_layout(cont); |
697 | if (layout == L_TABBED || layout == L_STACKED) { | 758 | if (layout == L_TABBED || layout == L_STACKED) { |
698 | struct sway_node *tabcontainer = node_get_parent(node); | 759 | struct sway_node *tabcontainer = node_get_parent(node); |
@@ -700,7 +761,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
700 | seat_get_active_tiling_child(seat, tabcontainer); | 761 | seat_get_active_tiling_child(seat, tabcontainer); |
701 | list_t *siblings = container_get_siblings(cont); | 762 | list_t *siblings = container_get_siblings(cont); |
702 | int desired = list_find(siblings, active->sway_container) + | 763 | int desired = list_find(siblings, active->sway_container) + |
703 | round(scroll_factor * event->delta_discrete); | 764 | roundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); |
704 | if (desired < 0) { | 765 | if (desired < 0) { |
705 | desired = 0; | 766 | desired = 0; |
706 | } else if (desired >= siblings->length) { | 767 | } else if (desired >= siblings->length) { |
@@ -709,13 +770,16 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
709 | 770 | ||
710 | struct sway_container *new_sibling_con = siblings->items[desired]; | 771 | struct sway_container *new_sibling_con = siblings->items[desired]; |
711 | struct sway_node *new_sibling = &new_sibling_con->node; | 772 | struct sway_node *new_sibling = &new_sibling_con->node; |
712 | struct sway_node *new_focus = | ||
713 | seat_get_focus_inactive(seat, new_sibling); | ||
714 | // Use the focused child of the tabbed/stacked container, not the | 773 | // Use the focused child of the tabbed/stacked container, not the |
715 | // container the user scrolled on. | 774 | // container the user scrolled on. |
716 | seat_set_focus(seat, new_focus); | 775 | new_focus = seat_get_focus_inactive(seat, new_sibling); |
717 | handled = true; | 776 | } else { |
777 | new_focus = seat_get_focus_inactive(seat, &cont->node); | ||
718 | } | 778 | } |
779 | |||
780 | seat_set_focus(seat, new_focus); | ||
781 | transaction_commit_dirty(); | ||
782 | handled = true; | ||
719 | } | 783 | } |
720 | 784 | ||
721 | // Handle mouse bindings - x11 mouse buttons 4-7 - release event | 785 | // Handle mouse bindings - x11 mouse buttons 4-7 - release event |
@@ -731,8 +795,307 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
731 | 795 | ||
732 | if (!handled) { | 796 | if (!handled) { |
733 | 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, |
734 | event->orientation, scroll_factor * event->delta, | 798 | event->orientation, scroll_factor * event->delta, |
735 | round(scroll_factor * event->delta_discrete), event->source); | 799 | roundf(scroll_factor * event->delta_discrete), event->source, |
800 | event->relative_direction); | ||
801 | } | ||
802 | } | ||
803 | |||
804 | /*------------------------------------\ | ||
805 | * Functions used by gesture support / | ||
806 | *----------------------------------*/ | ||
807 | |||
808 | /** | ||
809 | * Check gesture binding for a specific gesture type and finger count. | ||
810 | * Returns true if binding is present, false otherwise | ||
811 | */ | ||
812 | static bool gesture_binding_check(list_t *bindings, enum gesture_type type, | ||
813 | uint8_t fingers, struct sway_input_device *device) { | ||
814 | char *input = | ||
815 | device ? input_device_get_identifier(device->wlr_device) : strdup("*"); | ||
816 | |||
817 | for (int i = 0; i < bindings->length; ++i) { | ||
818 | struct sway_gesture_binding *binding = bindings->items[i]; | ||
819 | |||
820 | // Check type and finger count | ||
821 | if (!gesture_check(&binding->gesture, type, fingers)) { | ||
822 | continue; | ||
823 | } | ||
824 | |||
825 | // Check that input matches | ||
826 | if (strcmp(binding->input, "*") != 0 && | ||
827 | strcmp(binding->input, input) != 0) { | ||
828 | continue; | ||
829 | } | ||
830 | |||
831 | free(input); | ||
832 | |||
833 | return true; | ||
834 | } | ||
835 | |||
836 | free(input); | ||
837 | |||
838 | return false; | ||
839 | } | ||
840 | |||
841 | /** | ||
842 | * Return the gesture binding which matches gesture type, finger count | ||
843 | * and direction, otherwise return null. | ||
844 | */ | ||
845 | static struct sway_gesture_binding* gesture_binding_match( | ||
846 | list_t *bindings, struct gesture *gesture, const char *input) { | ||
847 | struct sway_gesture_binding *current = NULL; | ||
848 | |||
849 | // Find best matching binding | ||
850 | for (int i = 0; i < bindings->length; ++i) { | ||
851 | struct sway_gesture_binding *binding = bindings->items[i]; | ||
852 | bool exact = binding->flags & BINDING_EXACT; | ||
853 | |||
854 | // Check gesture matching | ||
855 | if (!gesture_match(&binding->gesture, gesture, exact)) { | ||
856 | continue; | ||
857 | } | ||
858 | |||
859 | // Check input matching | ||
860 | if (strcmp(binding->input, "*") != 0 && | ||
861 | strcmp(binding->input, input) != 0) { | ||
862 | continue; | ||
863 | } | ||
864 | |||
865 | // If we already have a match ... | ||
866 | if (current) { | ||
867 | // ... check if input matching is equivalent | ||
868 | if (strcmp(current->input, binding->input) == 0) { | ||
869 | |||
870 | // ... - do not override an exact binding | ||
871 | if (!exact && current->flags & BINDING_EXACT) { | ||
872 | continue; | ||
873 | } | ||
874 | |||
875 | // ... - and ensure direction matching is better or equal | ||
876 | if (gesture_compare(¤t->gesture, &binding->gesture) > 0) { | ||
877 | continue; | ||
878 | } | ||
879 | } else if (strcmp(binding->input, "*") == 0) { | ||
880 | // ... do not accept worse input match | ||
881 | continue; | ||
882 | } | ||
883 | } | ||
884 | |||
885 | // Accept newer or better match | ||
886 | current = binding; | ||
887 | |||
888 | // If exact binding and input is found, quit search | ||
889 | if (strcmp(current->input, input) == 0 && | ||
890 | gesture_compare(¤t->gesture, gesture) == 0) { | ||
891 | break; | ||
892 | } | ||
893 | } // for all gesture bindings | ||
894 | |||
895 | return current; | ||
896 | } | ||
897 | |||
898 | // Wrapper around gesture_tracker_end to use tracker with sway bindings | ||
899 | static struct sway_gesture_binding* gesture_tracker_end_and_match( | ||
900 | struct gesture_tracker *tracker, struct sway_input_device* device) { | ||
901 | // Determine name of input that received gesture | ||
902 | char *input = device | ||
903 | ? input_device_get_identifier(device->wlr_device) | ||
904 | : strdup("*"); | ||
905 | |||
906 | // Match tracking result to binding | ||
907 | struct gesture *gesture = gesture_tracker_end(tracker); | ||
908 | struct sway_gesture_binding *binding = gesture_binding_match( | ||
909 | config->current_mode->gesture_bindings, gesture, input); | ||
910 | free(gesture); | ||
911 | free(input); | ||
912 | |||
913 | return binding; | ||
914 | } | ||
915 | |||
916 | // Small wrapper around seat_execute_command to work on gesture bindings | ||
917 | static void gesture_binding_execute(struct sway_seat *seat, | ||
918 | struct sway_gesture_binding *binding) { | ||
919 | struct sway_binding *dummy_binding = | ||
920 | calloc(1, sizeof(struct sway_binding)); | ||
921 | dummy_binding->type = BINDING_GESTURE; | ||
922 | dummy_binding->command = binding->command; | ||
923 | |||
924 | char *description = gesture_to_string(&binding->gesture); | ||
925 | sway_log(SWAY_DEBUG, "executing gesture binding: %s", description); | ||
926 | free(description); | ||
927 | |||
928 | seat_execute_command(seat, dummy_binding); | ||
929 | |||
930 | free(dummy_binding); | ||
931 | } | ||
932 | |||
933 | static void handle_hold_begin(struct sway_seat *seat, | ||
934 | struct wlr_pointer_hold_begin_event *event) { | ||
935 | // Start tracking gesture if there is a matching binding ... | ||
936 | struct sway_input_device *device = | ||
937 | event->pointer ? event->pointer->base.data : NULL; | ||
938 | list_t *bindings = config->current_mode->gesture_bindings; | ||
939 | if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) { | ||
940 | struct seatop_default_event *seatop = seat->seatop_data; | ||
941 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers); | ||
942 | } else { | ||
943 | // ... otherwise forward to client | ||
944 | struct sway_cursor *cursor = seat->cursor; | ||
945 | wlr_pointer_gestures_v1_send_hold_begin( | ||
946 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
947 | event->time_msec, event->fingers); | ||
948 | } | ||
949 | } | ||
950 | |||
951 | static void handle_hold_end(struct sway_seat *seat, | ||
952 | struct wlr_pointer_hold_end_event *event) { | ||
953 | // Ensure that gesture is being tracked and was not cancelled | ||
954 | struct seatop_default_event *seatop = seat->seatop_data; | ||
955 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) { | ||
956 | struct sway_cursor *cursor = seat->cursor; | ||
957 | wlr_pointer_gestures_v1_send_hold_end( | ||
958 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
959 | event->time_msec, event->cancelled); | ||
960 | return; | ||
961 | } | ||
962 | if (event->cancelled) { | ||
963 | gesture_tracker_cancel(&seatop->gestures); | ||
964 | return; | ||
965 | } | ||
966 | |||
967 | // End gesture tracking and execute matched binding | ||
968 | struct sway_input_device *device = | ||
969 | event->pointer ? event->pointer->base.data : NULL; | ||
970 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
971 | &seatop->gestures, device); | ||
972 | |||
973 | if (binding) { | ||
974 | gesture_binding_execute(seat, binding); | ||
975 | } | ||
976 | } | ||
977 | |||
978 | static void handle_pinch_begin(struct sway_seat *seat, | ||
979 | struct wlr_pointer_pinch_begin_event *event) { | ||
980 | // Start tracking gesture if there is a matching binding ... | ||
981 | struct sway_input_device *device = | ||
982 | event->pointer ? event->pointer->base.data : NULL; | ||
983 | list_t *bindings = config->current_mode->gesture_bindings; | ||
984 | if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) { | ||
985 | struct seatop_default_event *seatop = seat->seatop_data; | ||
986 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers); | ||
987 | } else { | ||
988 | // ... otherwise forward to client | ||
989 | struct sway_cursor *cursor = seat->cursor; | ||
990 | wlr_pointer_gestures_v1_send_pinch_begin( | ||
991 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
992 | event->time_msec, event->fingers); | ||
993 | } | ||
994 | } | ||
995 | |||
996 | static void handle_pinch_update(struct sway_seat *seat, | ||
997 | struct wlr_pointer_pinch_update_event *event) { | ||
998 | // Update any ongoing tracking ... | ||
999 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1000 | if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { | ||
1001 | gesture_tracker_update(&seatop->gestures, event->dx, event->dy, | ||
1002 | event->scale, event->rotation); | ||
1003 | } else { | ||
1004 | // ... otherwise forward to client | ||
1005 | struct sway_cursor *cursor = seat->cursor; | ||
1006 | wlr_pointer_gestures_v1_send_pinch_update( | ||
1007 | server.input->pointer_gestures, | ||
1008 | cursor->seat->wlr_seat, | ||
1009 | event->time_msec, event->dx, event->dy, | ||
1010 | event->scale, event->rotation); | ||
1011 | } | ||
1012 | } | ||
1013 | |||
1014 | static void handle_pinch_end(struct sway_seat *seat, | ||
1015 | struct wlr_pointer_pinch_end_event *event) { | ||
1016 | // Ensure that gesture is being tracked and was not cancelled | ||
1017 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1018 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { | ||
1019 | struct sway_cursor *cursor = seat->cursor; | ||
1020 | wlr_pointer_gestures_v1_send_pinch_end( | ||
1021 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
1022 | event->time_msec, event->cancelled); | ||
1023 | return; | ||
1024 | } | ||
1025 | if (event->cancelled) { | ||
1026 | gesture_tracker_cancel(&seatop->gestures); | ||
1027 | return; | ||
1028 | } | ||
1029 | |||
1030 | // End gesture tracking and execute matched binding | ||
1031 | struct sway_input_device *device = | ||
1032 | event->pointer ? event->pointer->base.data : NULL; | ||
1033 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
1034 | &seatop->gestures, device); | ||
1035 | |||
1036 | if (binding) { | ||
1037 | gesture_binding_execute(seat, binding); | ||
1038 | } | ||
1039 | } | ||
1040 | |||
1041 | static void handle_swipe_begin(struct sway_seat *seat, | ||
1042 | struct wlr_pointer_swipe_begin_event *event) { | ||
1043 | // Start tracking gesture if there is a matching binding ... | ||
1044 | struct sway_input_device *device = | ||
1045 | event->pointer ? event->pointer->base.data : NULL; | ||
1046 | list_t *bindings = config->current_mode->gesture_bindings; | ||
1047 | if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) { | ||
1048 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1049 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers); | ||
1050 | } else { | ||
1051 | // ... otherwise forward to client | ||
1052 | struct sway_cursor *cursor = seat->cursor; | ||
1053 | wlr_pointer_gestures_v1_send_swipe_begin( | ||
1054 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
1055 | event->time_msec, event->fingers); | ||
1056 | } | ||
1057 | } | ||
1058 | |||
1059 | static void handle_swipe_update(struct sway_seat *seat, | ||
1060 | struct wlr_pointer_swipe_update_event *event) { | ||
1061 | |||
1062 | // Update any ongoing tracking ... | ||
1063 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1064 | if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { | ||
1065 | gesture_tracker_update(&seatop->gestures, | ||
1066 | event->dx, event->dy, NAN, NAN); | ||
1067 | } else { | ||
1068 | // ... otherwise forward to client | ||
1069 | struct sway_cursor *cursor = seat->cursor; | ||
1070 | wlr_pointer_gestures_v1_send_swipe_update( | ||
1071 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
1072 | event->time_msec, event->dx, event->dy); | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | static void handle_swipe_end(struct sway_seat *seat, | ||
1077 | struct wlr_pointer_swipe_end_event *event) { | ||
1078 | // Ensure gesture is being tracked and was not cancelled | ||
1079 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1080 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { | ||
1081 | struct sway_cursor *cursor = seat->cursor; | ||
1082 | wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures, | ||
1083 | cursor->seat->wlr_seat, event->time_msec, event->cancelled); | ||
1084 | return; | ||
1085 | } | ||
1086 | if (event->cancelled) { | ||
1087 | gesture_tracker_cancel(&seatop->gestures); | ||
1088 | return; | ||
1089 | } | ||
1090 | |||
1091 | // End gesture tracking and execute matched binding | ||
1092 | struct sway_input_device *device = | ||
1093 | event->pointer ? event->pointer->base.data : NULL; | ||
1094 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
1095 | &seatop->gestures, device); | ||
1096 | |||
1097 | if (binding) { | ||
1098 | gesture_binding_execute(seat, binding); | ||
736 | } | 1099 | } |
737 | } | 1100 | } |
738 | 1101 | ||
@@ -765,6 +1128,15 @@ static const struct sway_seatop_impl seatop_impl = { | |||
765 | .pointer_axis = handle_pointer_axis, | 1128 | .pointer_axis = handle_pointer_axis, |
766 | .tablet_tool_tip = handle_tablet_tool_tip, | 1129 | .tablet_tool_tip = handle_tablet_tool_tip, |
767 | .tablet_tool_motion = handle_tablet_tool_motion, | 1130 | .tablet_tool_motion = handle_tablet_tool_motion, |
1131 | .hold_begin = handle_hold_begin, | ||
1132 | .hold_end = handle_hold_end, | ||
1133 | .pinch_begin = handle_pinch_begin, | ||
1134 | .pinch_update = handle_pinch_update, | ||
1135 | .pinch_end = handle_pinch_end, | ||
1136 | .swipe_begin = handle_swipe_begin, | ||
1137 | .swipe_update = handle_swipe_update, | ||
1138 | .swipe_end = handle_swipe_end, | ||
1139 | .touch_down = handle_touch_down, | ||
768 | .rebase = handle_rebase, | 1140 | .rebase = handle_rebase, |
769 | .allow_set_cursor = true, | 1141 | .allow_set_cursor = true, |
770 | }; | 1142 | }; |
@@ -775,8 +1147,8 @@ void seatop_begin_default(struct sway_seat *seat) { | |||
775 | struct seatop_default_event *e = | 1147 | struct seatop_default_event *e = |
776 | calloc(1, sizeof(struct seatop_default_event)); | 1148 | calloc(1, sizeof(struct seatop_default_event)); |
777 | sway_assert(e, "Unable to allocate seatop_default_event"); | 1149 | sway_assert(e, "Unable to allocate seatop_default_event"); |
1150 | |||
778 | seat->seatop_impl = &seatop_impl; | 1151 | seat->seatop_impl = &seatop_impl; |
779 | seat->seatop_data = e; | 1152 | seat->seatop_data = e; |
780 | |||
781 | seatop_rebase(seat, 0); | 1153 | seatop_rebase(seat, 0); |
782 | } | 1154 | } |
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 17f619e3..340e334b 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c | |||
@@ -1,22 +1,138 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <wlr/types/wlr_cursor.h> | 2 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/types/wlr_tablet_v2.h> | 3 | #include <wlr/types/wlr_tablet_v2.h> |
4 | #include <wlr/types/wlr_touch.h> | ||
5 | #include "sway/input/cursor.h" | 5 | #include "sway/input/cursor.h" |
6 | #include "sway/input/seat.h" | 6 | #include "sway/input/seat.h" |
7 | #include "sway/tree/view.h" | 7 | #include "sway/tree/view.h" |
8 | #include "sway/desktop/transaction.h" | ||
8 | #include "log.h" | 9 | #include "log.h" |
9 | 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 | |||
10 | struct seatop_down_event { | 18 | struct seatop_down_event { |
11 | struct sway_container *con; | 19 | struct sway_container *con; |
20 | struct sway_seat *seat; | ||
21 | struct wl_listener surface_destroy; | ||
22 | struct wlr_surface *surface; | ||
12 | 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 |
13 | 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 | ||
14 | }; | 26 | }; |
15 | 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 | |||
16 | static void handle_pointer_axis(struct sway_seat *seat, | 132 | static void handle_pointer_axis(struct sway_seat *seat, |
17 | struct wlr_event_pointer_axis *event) { | 133 | struct wlr_pointer_axis_event *event) { |
18 | struct sway_input_device *input_device = | 134 | struct sway_input_device *input_device = |
19 | event->device ? event->device->data : NULL; | 135 | event->pointer ? event->pointer->base.data : NULL; |
20 | struct input_config *ic = | 136 | struct input_config *ic = |
21 | input_device ? input_device_get_config(input_device) : NULL; | 137 | input_device ? input_device_get_config(input_device) : NULL; |
22 | float scroll_factor = | 138 | float scroll_factor = |
@@ -24,12 +140,13 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
24 | 140 | ||
25 | wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, | 141 | wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, |
26 | event->orientation, scroll_factor * event->delta, | 142 | event->orientation, scroll_factor * event->delta, |
27 | round(scroll_factor * event->delta_discrete), event->source); | 143 | roundf(scroll_factor * event->delta_discrete), event->source, |
144 | event->relative_direction); | ||
28 | } | 145 | } |
29 | 146 | ||
30 | 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, |
31 | struct wlr_input_device *device, uint32_t button, | 148 | struct wlr_input_device *device, uint32_t button, |
32 | enum wlr_button_state state) { | 149 | enum wl_pointer_button_state state) { |
33 | seat_pointer_notify_button(seat, time_msec, button, state); | 150 | seat_pointer_notify_button(seat, time_msec, button, state); |
34 | 151 | ||
35 | if (seat->cursor->pressed_button_count == 0) { | 152 | if (seat->cursor->pressed_button_count == 0) { |
@@ -39,8 +156,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
39 | 156 | ||
40 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | 157 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { |
41 | struct seatop_down_event *e = seat->seatop_data; | 158 | struct seatop_down_event *e = seat->seatop_data; |
42 | struct sway_container *con = e->con; | 159 | if (seat_is_input_allowed(seat, e->surface)) { |
43 | if (seat_is_input_allowed(seat, con->view->surface)) { | ||
44 | double moved_x = seat->cursor->cursor->x - e->ref_lx; | 160 | double moved_x = seat->cursor->cursor->x - e->ref_lx; |
45 | double moved_y = seat->cursor->cursor->y - e->ref_ly; | 161 | double moved_y = seat->cursor->cursor->y - e->ref_ly; |
46 | double sx = e->ref_con_lx + moved_x; | 162 | double sx = e->ref_con_lx + moved_x; |
@@ -61,8 +177,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
61 | static void handle_tablet_tool_motion(struct sway_seat *seat, | 177 | static void handle_tablet_tool_motion(struct sway_seat *seat, |
62 | struct sway_tablet_tool *tool, uint32_t time_msec) { | 178 | struct sway_tablet_tool *tool, uint32_t time_msec) { |
63 | struct seatop_down_event *e = seat->seatop_data; | 179 | struct seatop_down_event *e = seat->seatop_data; |
64 | struct sway_container *con = e->con; | 180 | if (seat_is_input_allowed(seat, e->surface)) { |
65 | if (seat_is_input_allowed(seat, con->view->surface)) { | ||
66 | double moved_x = seat->cursor->cursor->x - e->ref_lx; | 181 | double moved_x = seat->cursor->cursor->x - e->ref_lx; |
67 | double moved_y = seat->cursor->cursor->y - e->ref_ly; | 182 | double moved_y = seat->cursor->cursor->y - e->ref_ly; |
68 | double sx = e->ref_con_lx + moved_x; | 183 | double sx = e->ref_con_lx + moved_x; |
@@ -71,6 +186,14 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, | |||
71 | } | 186 | } |
72 | } | 187 | } |
73 | 188 | ||
189 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
190 | struct seatop_down_event *e = | ||
191 | wl_container_of(listener, e, surface_destroy); | ||
192 | if (e) { | ||
193 | seatop_begin_default(e->seat); | ||
194 | } | ||
195 | } | ||
196 | |||
74 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 197 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
75 | struct seatop_down_event *e = seat->seatop_data; | 198 | struct seatop_down_event *e = seat->seatop_data; |
76 | if (e->con == con) { | 199 | if (e->con == con) { |
@@ -78,33 +201,63 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | |||
78 | } | 201 | } |
79 | } | 202 | } |
80 | 203 | ||
204 | static void handle_end(struct sway_seat *seat) { | ||
205 | struct seatop_down_event *e = seat->seatop_data; | ||
206 | wl_list_remove(&e->surface_destroy.link); | ||
207 | } | ||
208 | |||
81 | static const struct sway_seatop_impl seatop_impl = { | 209 | static const struct sway_seatop_impl seatop_impl = { |
82 | .button = handle_button, | 210 | .button = handle_button, |
83 | .pointer_motion = handle_pointer_motion, | 211 | .pointer_motion = handle_pointer_motion, |
84 | .pointer_axis = handle_pointer_axis, | 212 | .pointer_axis = handle_pointer_axis, |
85 | .tablet_tool_tip = handle_tablet_tool_tip, | 213 | .tablet_tool_tip = handle_tablet_tool_tip, |
86 | .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, | ||
87 | .unref = handle_unref, | 219 | .unref = handle_unref, |
220 | .end = handle_end, | ||
88 | .allow_set_cursor = true, | 221 | .allow_set_cursor = true, |
89 | }; | 222 | }; |
90 | 223 | ||
91 | 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, |
92 | uint32_t time_msec, int sx, int sy) { | 225 | double sx, double sy) { |
226 | seatop_begin_down_on_surface(seat, con->view->surface, sx, sy); | ||
227 | struct seatop_down_event *e = seat->seatop_data; | ||
228 | e->con = con; | ||
229 | |||
230 | container_raise_floating(con); | ||
231 | transaction_commit_dirty(); | ||
232 | } | ||
233 | |||
234 | void seatop_begin_touch_down(struct sway_seat *seat, | ||
235 | struct wlr_surface *surface, struct wlr_touch_down_event *event, | ||
236 | double sx, double sy, double lx, double ly) { | ||
237 | seatop_begin_down_on_surface(seat, surface, sx, sy); | ||
238 | handle_touch_down(seat, event, lx, ly); | ||
239 | } | ||
240 | |||
241 | void seatop_begin_down_on_surface(struct sway_seat *seat, | ||
242 | struct wlr_surface *surface, double sx, double sy) { | ||
93 | seatop_end(seat); | 243 | seatop_end(seat); |
94 | 244 | ||
95 | struct seatop_down_event *e = | 245 | struct seatop_down_event *e = |
96 | calloc(1, sizeof(struct seatop_down_event)); | 246 | calloc(1, sizeof(struct seatop_down_event)); |
97 | if (!e) { | 247 | if (!sway_assert(e, "Unable to allocate e")) { |
98 | return; | 248 | return; |
99 | } | 249 | } |
100 | e->con = con; | 250 | e->con = NULL; |
251 | e->seat = seat; | ||
252 | e->surface = surface; | ||
253 | wl_signal_add(&e->surface->events.destroy, &e->surface_destroy); | ||
254 | e->surface_destroy.notify = handle_destroy; | ||
101 | e->ref_lx = seat->cursor->cursor->x; | 255 | e->ref_lx = seat->cursor->cursor->x; |
102 | e->ref_ly = seat->cursor->cursor->y; | 256 | e->ref_ly = seat->cursor->cursor->y; |
103 | e->ref_con_lx = sx; | 257 | e->ref_con_lx = sx; |
104 | e->ref_con_ly = sy; | 258 | e->ref_con_ly = sy; |
259 | wl_list_init(&e->point_events); | ||
105 | 260 | ||
106 | seat->seatop_impl = &seatop_impl; | 261 | seat->seatop_impl = &seatop_impl; |
107 | seat->seatop_data = e; | 262 | seat->seatop_data = e; |
108 | |||
109 | container_raise_floating(con); | ||
110 | } | 263 | } |
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index 7f501fc9..83668d88 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c | |||
@@ -1,6 +1,5 @@ | |||
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" | 2 | #include "sway/desktop/transaction.h" |
4 | #include "sway/input/cursor.h" | 3 | #include "sway/input/cursor.h" |
5 | #include "sway/input/seat.h" | 4 | #include "sway/input/seat.h" |
6 | 5 | ||
@@ -14,14 +13,15 @@ static void finalize_move(struct sway_seat *seat) { | |||
14 | 13 | ||
15 | // We "move" the container to its own location | 14 | // We "move" the container to its own location |
16 | // so it discovers its output again. | 15 | // so it discovers its output again. |
17 | container_floating_move_to(e->con, e->con->x, e->con->y); | 16 | container_floating_move_to(e->con, e->con->pending.x, e->con->pending.y); |
17 | transaction_commit_dirty(); | ||
18 | 18 | ||
19 | seatop_begin_default(seat); | 19 | seatop_begin_default(seat); |
20 | } | 20 | } |
21 | 21 | ||
22 | 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, |
23 | struct wlr_input_device *device, uint32_t button, | 23 | struct wlr_input_device *device, uint32_t button, |
24 | enum wlr_button_state state) { | 24 | enum wl_pointer_button_state state) { |
25 | if (seat->cursor->pressed_button_count == 0) { | 25 | if (seat->cursor->pressed_button_count == 0) { |
26 | finalize_move(seat); | 26 | finalize_move(seat); |
27 | } | 27 | } |
@@ -37,9 +37,8 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
37 | 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) { |
38 | struct seatop_move_floating_event *e = seat->seatop_data; | 38 | struct seatop_move_floating_event *e = seat->seatop_data; |
39 | struct wlr_cursor *cursor = seat->cursor->cursor; | 39 | struct wlr_cursor *cursor = seat->cursor->cursor; |
40 | desktop_damage_whole_container(e->con); | ||
41 | 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); |
42 | desktop_damage_whole_container(e->con); | 41 | transaction_commit_dirty(); |
43 | } | 42 | } |
44 | 43 | ||
45 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 44 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
@@ -67,13 +66,14 @@ void seatop_begin_move_floating(struct sway_seat *seat, | |||
67 | return; | 66 | return; |
68 | } | 67 | } |
69 | e->con = con; | 68 | e->con = con; |
70 | e->dx = cursor->cursor->x - con->x; | 69 | e->dx = cursor->cursor->x - con->pending.x; |
71 | e->dy = cursor->cursor->y - con->y; | 70 | e->dy = cursor->cursor->y - con->pending.y; |
72 | 71 | ||
73 | seat->seatop_impl = &seatop_impl; | 72 | seat->seatop_impl = &seatop_impl; |
74 | seat->seatop_data = e; | 73 | seat->seatop_data = e; |
75 | 74 | ||
76 | container_raise_floating(con); | 75 | container_raise_floating(con); |
76 | transaction_commit_dirty(); | ||
77 | 77 | ||
78 | cursor_set_image(cursor, "grab", NULL); | 78 | cursor_set_image(cursor, "grab", NULL); |
79 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 79 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 704e7270..c525b77a 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c | |||
@@ -1,8 +1,7 @@ | |||
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" | 4 | #include "sway/desktop/transaction.h" |
6 | #include "sway/input/cursor.h" | 5 | #include "sway/input/cursor.h" |
7 | #include "sway/input/seat.h" | 6 | #include "sway/input/seat.h" |
8 | #include "sway/ipc-server.h" | 7 | #include "sway/ipc-server.h" |
@@ -15,31 +14,25 @@ | |||
15 | // Thickness of the dropzone when dragging to the edge of a layout container | 14 | // Thickness of the dropzone when dragging to the edge of a layout container |
16 | #define DROP_LAYOUT_BORDER 30 | 15 | #define DROP_LAYOUT_BORDER 30 |
17 | 16 | ||
17 | // Thickness of indicator when dropping onto a titlebar. This should be a | ||
18 | // multiple of 2. | ||
19 | #define DROP_SPLIT_INDICATOR 10 | ||
20 | |||
18 | struct seatop_move_tiling_event { | 21 | struct seatop_move_tiling_event { |
19 | struct sway_container *con; | 22 | struct sway_container *con; |
20 | struct sway_node *target_node; | 23 | struct sway_node *target_node; |
21 | enum wlr_edges target_edge; | 24 | enum wlr_edges target_edge; |
22 | struct wlr_box drop_box; | ||
23 | 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 |
24 | bool threshold_reached; | 26 | bool threshold_reached; |
27 | bool split_target; | ||
28 | bool insert_after_target; | ||
29 | struct wlr_scene_rect *indicator_rect; | ||
25 | }; | 30 | }; |
26 | 31 | ||
27 | static void handle_render(struct sway_seat *seat, | 32 | static void handle_end(struct sway_seat *seat) { |
28 | struct sway_output *output, pixman_region32_t *damage) { | ||
29 | struct seatop_move_tiling_event *e = seat->seatop_data; | 33 | struct seatop_move_tiling_event *e = seat->seatop_data; |
30 | if (!e->threshold_reached) { | 34 | wlr_scene_node_destroy(&e->indicator_rect->node); |
31 | return; | 35 | e->indicator_rect = NULL; |
32 | } | ||
33 | if (e->target_node && node_get_output(e->target_node) == output) { | ||
34 | float color[4]; | ||
35 | memcpy(&color, config->border_colors.focused.indicator, | ||
36 | sizeof(float) * 4); | ||
37 | premultiply_alpha(color, 0.5); | ||
38 | struct wlr_box box; | ||
39 | memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); | ||
40 | scale_box(&box, output->wlr_output->scale); | ||
41 | render_rect(output, damage, &box, color); | ||
42 | } | ||
43 | } | 36 | } |
44 | 37 | ||
45 | static void handle_motion_prethreshold(struct sway_seat *seat) { | 38 | static void handle_motion_prethreshold(struct sway_seat *seat) { |
@@ -60,6 +53,7 @@ static void handle_motion_prethreshold(struct sway_seat *seat) { | |||
60 | 53 | ||
61 | // If the threshold has been exceeded, start the actual drag | 54 | // If the threshold has been exceeded, start the actual drag |
62 | 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); | ||
63 | e->threshold_reached = true; | 57 | e->threshold_reached = true; |
64 | cursor_set_image(seat->cursor, "grab", NULL); | 58 | cursor_set_image(seat->cursor, "grab", NULL); |
65 | } | 59 | } |
@@ -91,15 +85,86 @@ static void resize_box(struct wlr_box *box, enum wlr_edges edge, | |||
91 | } | 85 | } |
92 | } | 86 | } |
93 | 87 | ||
88 | static void split_border(double pos, int offset, int len, int n_children, | ||
89 | int avoid, int *out_pos, bool *out_after) { | ||
90 | int region = 2 * n_children * (pos - offset) / len; | ||
91 | // If the cursor is over the right side of a left-adjacent titlebar, or the | ||
92 | // left side of a right-adjacent titlebar, it's position when dropped will | ||
93 | // be the same. To avoid this, shift the region for adjacent containers. | ||
94 | if (avoid >= 0) { | ||
95 | if (region == 2 * avoid - 1 || region == 2 * avoid) { | ||
96 | region--; | ||
97 | } else if (region == 2 * avoid + 1 || region == 2 * avoid + 2) { | ||
98 | region++; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | int child_index = (region + 1) / 2; | ||
103 | *out_after = region % 2; | ||
104 | // When dropping at the beginning or end of a container, show the drop | ||
105 | // region within the container boundary, otherwise show it on top of the | ||
106 | // border between two titlebars. | ||
107 | if (child_index == 0) { | ||
108 | *out_pos = offset; | ||
109 | } else if (child_index == n_children) { | ||
110 | *out_pos = offset + len - DROP_SPLIT_INDICATOR; | ||
111 | } else { | ||
112 | *out_pos = offset + child_index * len / n_children - | ||
113 | DROP_SPLIT_INDICATOR / 2; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | static bool split_titlebar(struct sway_node *node, struct sway_container *avoid, | ||
118 | struct wlr_cursor *cursor, struct wlr_box *title_box, bool *after) { | ||
119 | struct sway_container *con = node->sway_container; | ||
120 | struct sway_node *parent = &con->pending.parent->node; | ||
121 | int title_height = container_titlebar_height(); | ||
122 | struct wlr_box box; | ||
123 | int n_children, avoid_index; | ||
124 | enum sway_container_layout layout = | ||
125 | parent ? node_get_layout(parent) : L_NONE; | ||
126 | if (layout == L_TABBED || layout == L_STACKED) { | ||
127 | node_get_box(parent, &box); | ||
128 | n_children = node_get_children(parent)->length; | ||
129 | avoid_index = list_find(node_get_children(parent), avoid); | ||
130 | } else { | ||
131 | node_get_box(node, &box); | ||
132 | n_children = 1; | ||
133 | avoid_index = -1; | ||
134 | } | ||
135 | if (layout == L_STACKED && cursor->y < box.y + title_height * n_children) { | ||
136 | // Drop into stacked titlebars. | ||
137 | title_box->width = box.width; | ||
138 | title_box->height = DROP_SPLIT_INDICATOR; | ||
139 | title_box->x = box.x; | ||
140 | split_border(cursor->y, box.y, title_height * n_children, | ||
141 | n_children, avoid_index, &title_box->y, after); | ||
142 | return true; | ||
143 | } else if (layout != L_STACKED && cursor->y < box.y + title_height) { | ||
144 | // Drop into side-by-side titlebars. | ||
145 | title_box->width = DROP_SPLIT_INDICATOR; | ||
146 | title_box->height = title_height; | ||
147 | title_box->y = box.y; | ||
148 | split_border(cursor->x, box.x, box.width, n_children, | ||
149 | avoid_index, &title_box->x, after); | ||
150 | return true; | ||
151 | } | ||
152 | return false; | ||
153 | } | ||
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 | |||
94 | static void handle_motion_postthreshold(struct sway_seat *seat) { | 160 | static void handle_motion_postthreshold(struct sway_seat *seat) { |
95 | struct seatop_move_tiling_event *e = seat->seatop_data; | 161 | struct seatop_move_tiling_event *e = seat->seatop_data; |
162 | e->split_target = false; | ||
96 | struct wlr_surface *surface = NULL; | 163 | struct wlr_surface *surface = NULL; |
97 | double sx, sy; | 164 | double sx, sy; |
98 | struct sway_cursor *cursor = seat->cursor; | 165 | struct sway_cursor *cursor = seat->cursor; |
99 | struct sway_node *node = node_at_coords(seat, | 166 | struct sway_node *node = node_at_coords(seat, |
100 | cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); | 167 | cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); |
101 | // Damage the old location | ||
102 | desktop_damage_box(&e->drop_box); | ||
103 | 168 | ||
104 | if (!node) { | 169 | if (!node) { |
105 | // Eg. hovered over a layer surface such as swaybar | 170 | // Eg. hovered over a layer surface such as swaybar |
@@ -112,41 +177,77 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
112 | // Empty workspace | 177 | // Empty workspace |
113 | e->target_node = node; | 178 | e->target_node = node; |
114 | e->target_edge = WLR_EDGE_NONE; | 179 | e->target_edge = WLR_EDGE_NONE; |
115 | workspace_get_box(node->sway_workspace, &e->drop_box); | 180 | |
116 | 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); | ||
117 | return; | 184 | return; |
118 | } | 185 | } |
119 | 186 | ||
120 | // Deny moving within own workspace if this is the only child | 187 | // Deny moving within own workspace if this is the only child |
121 | struct sway_container *con = node->sway_container; | 188 | struct sway_container *con = node->sway_container; |
122 | if (workspace_num_tiling_views(e->con->workspace) == 1 && | 189 | if (workspace_num_tiling_views(e->con->pending.workspace) == 1 && |
123 | con->workspace == e->con->workspace) { | 190 | con->pending.workspace == e->con->pending.workspace) { |
124 | e->target_node = NULL; | 191 | e->target_node = NULL; |
125 | e->target_edge = WLR_EDGE_NONE; | 192 | e->target_edge = WLR_EDGE_NONE; |
126 | return; | 193 | return; |
127 | } | 194 | } |
128 | 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 | |||
203 | // Check if the cursor is over a tilebar only if the destination | ||
204 | // container is not a descendant of the source container. | ||
205 | if (!surface && !container_has_ancestor(con, e->con) && | ||
206 | split_titlebar(node, e->con, cursor->cursor, | ||
207 | &drop_box, &e->insert_after_target)) { | ||
208 | // Don't allow dropping over the source container's titlebar | ||
209 | // to give users a chance to cancel a drag operation. | ||
210 | if (con == e->con) { | ||
211 | e->target_node = NULL; | ||
212 | } else { | ||
213 | e->target_node = node; | ||
214 | e->split_target = true; | ||
215 | } | ||
216 | e->target_edge = WLR_EDGE_NONE; | ||
217 | update_indicator(e, &drop_box); | ||
218 | return; | ||
219 | } | ||
220 | |||
129 | // Traverse the ancestors, trying to find a layout container perpendicular | 221 | // Traverse the ancestors, trying to find a layout container perpendicular |
130 | // to the edge. Eg. close to the top or bottom of a horiz layout. | 222 | // to the edge. Eg. close to the top or bottom of a horiz layout. |
223 | int thresh_top = con->pending.content_y + DROP_LAYOUT_BORDER; | ||
224 | int thresh_bottom = con->pending.content_y + | ||
225 | con->pending.content_height - DROP_LAYOUT_BORDER; | ||
226 | int thresh_left = con->pending.content_x + DROP_LAYOUT_BORDER; | ||
227 | int thresh_right = con->pending.content_x + | ||
228 | con->pending.content_width - DROP_LAYOUT_BORDER; | ||
131 | while (con) { | 229 | while (con) { |
132 | enum wlr_edges edge = WLR_EDGE_NONE; | 230 | enum wlr_edges edge = WLR_EDGE_NONE; |
133 | enum sway_container_layout layout = container_parent_layout(con); | 231 | enum sway_container_layout layout = container_parent_layout(con); |
134 | struct wlr_box parent; | 232 | struct wlr_box box; |
135 | con->parent ? container_get_box(con->parent, &parent) : | 233 | node_get_box(node_get_parent(&con->node), &box); |
136 | workspace_get_box(con->workspace, &parent); | ||
137 | if (layout == L_HORIZ || layout == L_TABBED) { | 234 | if (layout == L_HORIZ || layout == L_TABBED) { |
138 | if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) { | 235 | if (cursor->cursor->y < thresh_top) { |
139 | edge = WLR_EDGE_TOP; | 236 | edge = WLR_EDGE_TOP; |
140 | } else if (cursor->cursor->y > parent.y + parent.height | 237 | box.height = thresh_top - box.y; |
141 | - DROP_LAYOUT_BORDER) { | 238 | } else if (cursor->cursor->y > thresh_bottom) { |
142 | edge = WLR_EDGE_BOTTOM; | 239 | edge = WLR_EDGE_BOTTOM; |
240 | box.height = box.y + box.height - thresh_bottom; | ||
241 | box.y = thresh_bottom; | ||
143 | } | 242 | } |
144 | } else if (layout == L_VERT || layout == L_STACKED) { | 243 | } else if (layout == L_VERT || layout == L_STACKED) { |
145 | if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) { | 244 | if (cursor->cursor->x < thresh_left) { |
146 | edge = WLR_EDGE_LEFT; | 245 | edge = WLR_EDGE_LEFT; |
147 | } else if (cursor->cursor->x > parent.x + parent.width | 246 | box.width = thresh_left - box.x; |
148 | - DROP_LAYOUT_BORDER) { | 247 | } else if (cursor->cursor->x > thresh_right) { |
149 | edge = WLR_EDGE_RIGHT; | 248 | edge = WLR_EDGE_RIGHT; |
249 | box.width = box.x + box.width - thresh_right; | ||
250 | box.x = thresh_right; | ||
150 | } | 251 | } |
151 | } | 252 | } |
152 | if (edge) { | 253 | if (edge) { |
@@ -155,12 +256,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
155 | e->target_node = node_get_parent(e->target_node); | 256 | e->target_node = node_get_parent(e->target_node); |
156 | } | 257 | } |
157 | e->target_edge = edge; | 258 | e->target_edge = edge; |
158 | node_get_box(e->target_node, &e->drop_box); | 259 | update_indicator(e, &box); |
159 | resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER); | ||
160 | desktop_damage_box(&e->drop_box); | ||
161 | return; | 260 | return; |
162 | } | 261 | } |
163 | con = con->parent; | 262 | con = con->pending.parent; |
164 | } | 263 | } |
165 | 264 | ||
166 | // Use the hovered view - but we must be over the actual surface | 265 | // Use the hovered view - but we must be over the actual surface |
@@ -173,23 +272,23 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
173 | } | 272 | } |
174 | 273 | ||
175 | // Find the closest edge | 274 | // Find the closest edge |
176 | size_t thickness = fmin(con->content_width, con->content_height) * 0.3; | 275 | size_t thickness = fmin(con->pending.content_width, con->pending.content_height) * 0.3; |
177 | size_t closest_dist = INT_MAX; | 276 | size_t closest_dist = INT_MAX; |
178 | size_t dist; | 277 | size_t dist; |
179 | e->target_edge = WLR_EDGE_NONE; | 278 | e->target_edge = WLR_EDGE_NONE; |
180 | if ((dist = cursor->cursor->y - con->y) < closest_dist) { | 279 | if ((dist = cursor->cursor->y - con->pending.y) < closest_dist) { |
181 | closest_dist = dist; | 280 | closest_dist = dist; |
182 | e->target_edge = WLR_EDGE_TOP; | 281 | e->target_edge = WLR_EDGE_TOP; |
183 | } | 282 | } |
184 | if ((dist = cursor->cursor->x - con->x) < closest_dist) { | 283 | if ((dist = cursor->cursor->x - con->pending.x) < closest_dist) { |
185 | closest_dist = dist; | 284 | closest_dist = dist; |
186 | e->target_edge = WLR_EDGE_LEFT; | 285 | e->target_edge = WLR_EDGE_LEFT; |
187 | } | 286 | } |
188 | if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) { | 287 | if ((dist = con->pending.x + con->pending.width - cursor->cursor->x) < closest_dist) { |
189 | closest_dist = dist; | 288 | closest_dist = dist; |
190 | e->target_edge = WLR_EDGE_RIGHT; | 289 | e->target_edge = WLR_EDGE_RIGHT; |
191 | } | 290 | } |
192 | if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) { | 291 | if ((dist = con->pending.y + con->pending.height - cursor->cursor->y) < closest_dist) { |
193 | closest_dist = dist; | 292 | closest_dist = dist; |
194 | e->target_edge = WLR_EDGE_BOTTOM; | 293 | e->target_edge = WLR_EDGE_BOTTOM; |
195 | } | 294 | } |
@@ -199,12 +298,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
199 | } | 298 | } |
200 | 299 | ||
201 | e->target_node = node; | 300 | e->target_node = node; |
202 | e->drop_box.x = con->content_x; | 301 | resize_box(&drop_box, e->target_edge, thickness); |
203 | e->drop_box.y = con->content_y; | 302 | update_indicator(e, &drop_box); |
204 | e->drop_box.width = con->content_width; | ||
205 | e->drop_box.height = con->content_height; | ||
206 | resize_box(&e->drop_box, e->target_edge, thickness); | ||
207 | desktop_damage_box(&e->drop_box); | ||
208 | } | 303 | } |
209 | 304 | ||
210 | 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) { |
@@ -214,6 +309,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
214 | } else { | 309 | } else { |
215 | handle_motion_prethreshold(seat); | 310 | handle_motion_prethreshold(seat); |
216 | } | 311 | } |
312 | transaction_commit_dirty(); | ||
217 | } | 313 | } |
218 | 314 | ||
219 | static bool is_parallel(enum sway_container_layout layout, | 315 | static bool is_parallel(enum sway_container_layout layout, |
@@ -232,14 +328,15 @@ static void finalize_move(struct sway_seat *seat) { | |||
232 | } | 328 | } |
233 | 329 | ||
234 | struct sway_container *con = e->con; | 330 | struct sway_container *con = e->con; |
235 | struct sway_container *old_parent = con->parent; | 331 | struct sway_container *old_parent = con->pending.parent; |
236 | struct sway_workspace *old_ws = con->workspace; | 332 | struct sway_workspace *old_ws = con->pending.workspace; |
237 | struct sway_node *target_node = e->target_node; | 333 | struct sway_node *target_node = e->target_node; |
238 | struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? | 334 | struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? |
239 | target_node->sway_workspace : target_node->sway_container->workspace; | 335 | target_node->sway_workspace : target_node->sway_container->pending.workspace; |
240 | enum wlr_edges edge = e->target_edge; | 336 | enum wlr_edges edge = e->target_edge; |
241 | int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; | 337 | int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; |
242 | bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER; | 338 | bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER && |
339 | !e->split_target; | ||
243 | 340 | ||
244 | if (!swap) { | 341 | if (!swap) { |
245 | container_detach(con); | 342 | container_detach(con); |
@@ -248,6 +345,14 @@ static void finalize_move(struct sway_seat *seat) { | |||
248 | // Moving container into empty workspace | 345 | // Moving container into empty workspace |
249 | if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { | 346 | if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { |
250 | con = workspace_add_tiling(new_ws, con); | 347 | con = workspace_add_tiling(new_ws, con); |
348 | } else if (e->split_target) { | ||
349 | struct sway_container *target = target_node->sway_container; | ||
350 | enum sway_container_layout layout = container_parent_layout(target); | ||
351 | if (layout != L_TABBED && layout != L_STACKED) { | ||
352 | container_split(target, L_TABBED); | ||
353 | } | ||
354 | container_add_sibling(target, con, e->insert_after_target); | ||
355 | ipc_event_window(con, "move"); | ||
251 | } else if (target_node->type == N_CONTAINER) { | 356 | } else if (target_node->type == N_CONTAINER) { |
252 | // Moving container before/after another | 357 | // Moving container before/after another |
253 | struct sway_container *target = target_node->sway_container; | 358 | struct sway_container *target = target_node->sway_container; |
@@ -283,8 +388,8 @@ static void finalize_move(struct sway_seat *seat) { | |||
283 | int index = list_find(siblings, con); | 388 | int index = list_find(siblings, con); |
284 | struct sway_container *sibling = index == 0 ? | 389 | struct sway_container *sibling = index == 0 ? |
285 | siblings->items[1] : siblings->items[index - 1]; | 390 | siblings->items[1] : siblings->items[index - 1]; |
286 | con->width = sibling->width; | 391 | con->pending.width = sibling->pending.width; |
287 | con->height = sibling->height; | 392 | con->pending.height = sibling->pending.height; |
288 | con->width_fraction = sibling->width_fraction; | 393 | con->width_fraction = sibling->width_fraction; |
289 | con->height_fraction = sibling->height_fraction; | 394 | con->height_fraction = sibling->height_fraction; |
290 | } | 395 | } |
@@ -294,12 +399,13 @@ static void finalize_move(struct sway_seat *seat) { | |||
294 | arrange_workspace(new_ws); | 399 | arrange_workspace(new_ws); |
295 | } | 400 | } |
296 | 401 | ||
402 | transaction_commit_dirty(); | ||
297 | seatop_begin_default(seat); | 403 | seatop_begin_default(seat); |
298 | } | 404 | } |
299 | 405 | ||
300 | 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, |
301 | struct wlr_input_device *device, uint32_t button, | 407 | struct wlr_input_device *device, uint32_t button, |
302 | enum wlr_button_state state) { | 408 | enum wl_pointer_button_state state) { |
303 | if (seat->cursor->pressed_button_count == 0) { | 409 | if (seat->cursor->pressed_button_count == 0) { |
304 | finalize_move(seat); | 410 | finalize_move(seat); |
305 | } | 411 | } |
@@ -328,7 +434,7 @@ static const struct sway_seatop_impl seatop_impl = { | |||
328 | .pointer_motion = handle_pointer_motion, | 434 | .pointer_motion = handle_pointer_motion, |
329 | .tablet_tool_tip = handle_tablet_tool_tip, | 435 | .tablet_tool_tip = handle_tablet_tool_tip, |
330 | .unref = handle_unref, | 436 | .unref = handle_unref, |
331 | .render = handle_render, | 437 | .end = handle_end, |
332 | }; | 438 | }; |
333 | 439 | ||
334 | void seatop_begin_move_tiling_threshold(struct sway_seat *seat, | 440 | void seatop_begin_move_tiling_threshold(struct sway_seat *seat, |
@@ -340,6 +446,20 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, | |||
340 | if (!e) { | 446 | if (!e) { |
341 | return; | 447 | return; |
342 | } | 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 | |||
343 | e->con = con; | 463 | e->con = con; |
344 | e->ref_lx = seat->cursor->cursor->x; | 464 | e->ref_lx = seat->cursor->cursor->x; |
345 | e->ref_ly = seat->cursor->cursor->y; | 465 | e->ref_ly = seat->cursor->cursor->y; |
@@ -348,6 +468,7 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, | |||
348 | seat->seatop_data = e; | 468 | seat->seatop_data = e; |
349 | 469 | ||
350 | container_raise_floating(con); | 470 | container_raise_floating(con); |
471 | transaction_commit_dirty(); | ||
351 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 472 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
352 | } | 473 | } |
353 | 474 | ||
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index 5da22e47..bec86e33 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c | |||
@@ -1,7 +1,7 @@ | |||
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> |
4 | #include "sway/desktop/transaction.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/arrange.h" | 7 | #include "sway/tree/arrange.h" |
@@ -20,13 +20,14 @@ struct seatop_resize_floating_event { | |||
20 | 20 | ||
21 | 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, |
22 | struct wlr_input_device *device, uint32_t button, | 22 | struct wlr_input_device *device, uint32_t button, |
23 | enum wlr_button_state state) { | 23 | enum wl_pointer_button_state state) { |
24 | struct seatop_resize_floating_event *e = seat->seatop_data; | 24 | struct seatop_resize_floating_event *e = seat->seatop_data; |
25 | struct sway_container *con = e->con; | 25 | struct sway_container *con = e->con; |
26 | 26 | ||
27 | if (seat->cursor->pressed_button_count == 0) { | 27 | if (seat->cursor->pressed_button_count == 0) { |
28 | container_set_resizing(con, false); | 28 | container_set_resizing(con, false); |
29 | arrange_container(con); // Send configure w/o resizing hint | 29 | arrange_container(con); // Send configure w/o resizing hint |
30 | transaction_commit_dirty(); | ||
30 | seatop_begin_default(seat); | 31 | seatop_begin_default(seat); |
31 | } | 32 | } |
32 | } | 33 | } |
@@ -78,17 +79,25 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
78 | double height = e->ref_height + grow_height; | 79 | double height = e->ref_height + grow_height; |
79 | int min_width, max_width, min_height, max_height; | 80 | int min_width, max_width, min_height, max_height; |
80 | floating_calculate_constraints(&min_width, &max_width, | 81 | floating_calculate_constraints(&min_width, &max_width, |
81 | &min_height, &max_height); | 82 | &min_height, &max_height); |
82 | width = fmax(min_width + border_width, fmin(width, max_width)); | 83 | width = fmin(width, max_width - border_width); |
83 | height = fmax(min_height + border_height, fmin(height, max_height)); | 84 | width = fmax(width, min_width + border_width); |
85 | width = fmax(width, 1); | ||
86 | height = fmin(height, max_height - border_height); | ||
87 | height = fmax(height, min_height + border_height); | ||
88 | height = fmax(height, 1); | ||
84 | 89 | ||
85 | // Apply the view's min/max size | 90 | // Apply the view's min/max size |
86 | if (con->view) { | 91 | if (con->view) { |
87 | double view_min_width, view_max_width, view_min_height, view_max_height; | 92 | double view_min_width, view_max_width, view_min_height, view_max_height; |
88 | view_get_constraints(con->view, &view_min_width, &view_max_width, | 93 | view_get_constraints(con->view, &view_min_width, &view_max_width, |
89 | &view_min_height, &view_max_height); | 94 | &view_min_height, &view_max_height); |
90 | width = fmax(view_min_width + border_width, fmin(width, view_max_width)); | 95 | width = fmin(width, view_max_width - border_width); |
91 | height = fmax(view_min_height + border_height, fmin(height, view_max_height)); | 96 | width = fmax(width, view_min_width + border_width); |
97 | width = fmax(width, 1); | ||
98 | height = fmin(height, view_max_height - border_height); | ||
99 | height = fmax(height, view_min_height + border_height); | ||
100 | height = fmax(height, 1); | ||
92 | 101 | ||
93 | } | 102 | } |
94 | 103 | ||
@@ -116,23 +125,24 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
116 | 125 | ||
117 | // Determine the amounts we need to bump everything relative to the current | 126 | // Determine the amounts we need to bump everything relative to the current |
118 | // size. | 127 | // size. |
119 | int relative_grow_width = width - con->width; | 128 | int relative_grow_width = width - con->pending.width; |
120 | int relative_grow_height = height - con->height; | 129 | int relative_grow_height = height - con->pending.height; |
121 | int relative_grow_x = (e->ref_con_lx + grow_x) - con->x; | 130 | int relative_grow_x = (e->ref_con_lx + grow_x) - con->pending.x; |
122 | int relative_grow_y = (e->ref_con_ly + grow_y) - con->y; | 131 | int relative_grow_y = (e->ref_con_ly + grow_y) - con->pending.y; |
123 | 132 | ||
124 | // Actually resize stuff | 133 | // Actually resize stuff |
125 | con->x += relative_grow_x; | 134 | con->pending.x += relative_grow_x; |
126 | con->y += relative_grow_y; | 135 | con->pending.y += relative_grow_y; |
127 | con->width += relative_grow_width; | 136 | con->pending.width += relative_grow_width; |
128 | con->height += relative_grow_height; | 137 | con->pending.height += relative_grow_height; |
129 | 138 | ||
130 | con->content_x += relative_grow_x; | 139 | con->pending.content_x += relative_grow_x; |
131 | con->content_y += relative_grow_y; | 140 | con->pending.content_y += relative_grow_y; |
132 | con->content_width += relative_grow_width; | 141 | con->pending.content_width += relative_grow_width; |
133 | con->content_height += relative_grow_height; | 142 | con->pending.content_height += relative_grow_height; |
134 | 143 | ||
135 | arrange_container(con); | 144 | arrange_container(con); |
145 | transaction_commit_dirty(); | ||
136 | } | 146 | } |
137 | 147 | ||
138 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 148 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
@@ -166,16 +176,17 @@ void seatop_begin_resize_floating(struct sway_seat *seat, | |||
166 | e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; | 176 | e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; |
167 | e->ref_lx = seat->cursor->cursor->x; | 177 | e->ref_lx = seat->cursor->cursor->x; |
168 | e->ref_ly = seat->cursor->cursor->y; | 178 | e->ref_ly = seat->cursor->cursor->y; |
169 | e->ref_con_lx = con->x; | 179 | e->ref_con_lx = con->pending.x; |
170 | e->ref_con_ly = con->y; | 180 | e->ref_con_ly = con->pending.y; |
171 | e->ref_width = con->width; | 181 | e->ref_width = con->pending.width; |
172 | e->ref_height = con->height; | 182 | e->ref_height = con->pending.height; |
173 | 183 | ||
174 | seat->seatop_impl = &seatop_impl; | 184 | seat->seatop_impl = &seatop_impl; |
175 | seat->seatop_data = e; | 185 | seat->seatop_data = e; |
176 | 186 | ||
177 | container_set_resizing(con, true); | 187 | container_set_resizing(con, true); |
178 | container_raise_floating(con); | 188 | container_raise_floating(con); |
189 | transaction_commit_dirty(); | ||
179 | 190 | ||
180 | const char *image = edge == WLR_EDGE_NONE ? | 191 | const char *image = edge == WLR_EDGE_NONE ? |
181 | "se-resize" : wlr_xcursor_get_resize_name(edge); | 192 | "se-resize" : wlr_xcursor_get_resize_name(edge); |
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index 2cca805d..15fd333b 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c | |||
@@ -1,7 +1,7 @@ | |||
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" |
4 | #include "sway/desktop/transaction.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/arrange.h" | 7 | #include "sway/tree/arrange.h" |
@@ -45,28 +45,29 @@ static struct sway_container *container_get_resize_sibling( | |||
45 | 45 | ||
46 | 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, |
47 | struct wlr_input_device *device, uint32_t button, | 47 | struct wlr_input_device *device, uint32_t button, |
48 | enum wlr_button_state state) { | 48 | enum wl_pointer_button_state state) { |
49 | struct seatop_resize_tiling_event *e = seat->seatop_data; | 49 | struct seatop_resize_tiling_event *e = seat->seatop_data; |
50 | 50 | ||
51 | if (seat->cursor->pressed_button_count == 0) { | 51 | if (seat->cursor->pressed_button_count == 0) { |
52 | if (e->h_con) { | 52 | if (e->h_con) { |
53 | container_set_resizing(e->h_con, false); | 53 | container_set_resizing(e->h_con, false); |
54 | container_set_resizing(e->h_sib, false); | 54 | container_set_resizing(e->h_sib, false); |
55 | if (e->h_con->parent) { | 55 | if (e->h_con->pending.parent) { |
56 | arrange_container(e->h_con->parent); | 56 | arrange_container(e->h_con->pending.parent); |
57 | } else { | 57 | } else { |
58 | arrange_workspace(e->h_con->workspace); | 58 | arrange_workspace(e->h_con->pending.workspace); |
59 | } | 59 | } |
60 | } | 60 | } |
61 | if (e->v_con) { | 61 | if (e->v_con) { |
62 | container_set_resizing(e->v_con, false); | 62 | container_set_resizing(e->v_con, false); |
63 | container_set_resizing(e->v_sib, false); | 63 | container_set_resizing(e->v_sib, false); |
64 | if (e->v_con->parent) { | 64 | if (e->v_con->pending.parent) { |
65 | arrange_container(e->v_con->parent); | 65 | arrange_container(e->v_con->pending.parent); |
66 | } else { | 66 | } else { |
67 | arrange_workspace(e->v_con->workspace); | 67 | arrange_workspace(e->v_con->pending.workspace); |
68 | } | 68 | } |
69 | } | 69 | } |
70 | transaction_commit_dirty(); | ||
70 | seatop_begin_default(seat); | 71 | seatop_begin_default(seat); |
71 | } | 72 | } |
72 | } | 73 | } |
@@ -80,16 +81,16 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
80 | 81 | ||
81 | if (e->h_con) { | 82 | if (e->h_con) { |
82 | if (e->edge & WLR_EDGE_LEFT) { | 83 | if (e->edge & WLR_EDGE_LEFT) { |
83 | amount_x = (e->h_con_orig_width - moved_x) - e->h_con->width; | 84 | amount_x = (e->h_con_orig_width - moved_x) - e->h_con->pending.width; |
84 | } else if (e->edge & WLR_EDGE_RIGHT) { | 85 | } else if (e->edge & WLR_EDGE_RIGHT) { |
85 | amount_x = (e->h_con_orig_width + moved_x) - e->h_con->width; | 86 | amount_x = (e->h_con_orig_width + moved_x) - e->h_con->pending.width; |
86 | } | 87 | } |
87 | } | 88 | } |
88 | if (e->v_con) { | 89 | if (e->v_con) { |
89 | if (e->edge & WLR_EDGE_TOP) { | 90 | if (e->edge & WLR_EDGE_TOP) { |
90 | amount_y = (e->v_con_orig_height - moved_y) - e->v_con->height; | 91 | amount_y = (e->v_con_orig_height - moved_y) - e->v_con->pending.height; |
91 | } else if (e->edge & WLR_EDGE_BOTTOM) { | 92 | } else if (e->edge & WLR_EDGE_BOTTOM) { |
92 | amount_y = (e->v_con_orig_height + moved_y) - e->v_con->height; | 93 | amount_y = (e->v_con_orig_height + moved_y) - e->v_con->pending.height; |
93 | } | 94 | } |
94 | } | 95 | } |
95 | 96 | ||
@@ -99,6 +100,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
99 | if (amount_y != 0) { | 100 | if (amount_y != 0) { |
100 | container_resize_tiled(e->v_con, e->edge_y, amount_y); | 101 | container_resize_tiled(e->v_con, e->edge_y, amount_y); |
101 | } | 102 | } |
103 | transaction_commit_dirty(); | ||
102 | } | 104 | } |
103 | 105 | ||
104 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 106 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
@@ -140,7 +142,7 @@ void seatop_begin_resize_tiling(struct sway_seat *seat, | |||
140 | if (e->h_con) { | 142 | if (e->h_con) { |
141 | container_set_resizing(e->h_con, true); | 143 | container_set_resizing(e->h_con, true); |
142 | container_set_resizing(e->h_sib, true); | 144 | container_set_resizing(e->h_sib, true); |
143 | e->h_con_orig_width = e->h_con->width; | 145 | e->h_con_orig_width = e->h_con->pending.width; |
144 | } | 146 | } |
145 | } | 147 | } |
146 | if (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { | 148 | if (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { |
@@ -151,12 +153,13 @@ void seatop_begin_resize_tiling(struct sway_seat *seat, | |||
151 | if (e->v_con) { | 153 | if (e->v_con) { |
152 | container_set_resizing(e->v_con, true); | 154 | container_set_resizing(e->v_con, true); |
153 | container_set_resizing(e->v_sib, true); | 155 | container_set_resizing(e->v_sib, true); |
154 | e->v_con_orig_height = e->v_con->height; | 156 | e->v_con_orig_height = e->v_con->pending.height; |
155 | } | 157 | } |
156 | } | 158 | } |
157 | 159 | ||
158 | seat->seatop_impl = &seatop_impl; | 160 | seat->seatop_impl = &seatop_impl; |
159 | seat->seatop_data = e; | 161 | seat->seatop_data = e; |
160 | 162 | ||
163 | transaction_commit_dirty(); | ||
161 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 164 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
162 | } | 165 | } |
diff --git a/sway/input/switch.c b/sway/input/switch.c index b7c28df1..831f4dbf 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c | |||
@@ -1,7 +1,5 @@ | |||
1 | #include "sway/config.h" | 1 | #include "sway/config.h" |
2 | #include "sway/desktop/transaction.h" | ||
3 | #include "sway/input/switch.h" | 2 | #include "sway/input/switch.h" |
4 | #include <wlr/types/wlr_idle.h> | ||
5 | #include "log.h" | 3 | #include "log.h" |
6 | 4 | ||
7 | struct sway_switch *sway_switch_create(struct sway_seat *seat, | 5 | struct sway_switch *sway_switch_create(struct sway_seat *seat, |
@@ -12,6 +10,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat, | |||
12 | return NULL; | 10 | return NULL; |
13 | } | 11 | } |
14 | device->switch_device = switch_device; | 12 | device->switch_device = switch_device; |
13 | switch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device); | ||
15 | switch_device->seat_device = device; | 14 | switch_device->seat_device = device; |
16 | switch_device->state = WLR_SWITCH_STATE_OFF; | 15 | switch_device->state = WLR_SWITCH_STATE_OFF; |
17 | wl_list_init(&switch_device->switch_toggle.link); | 16 | wl_list_init(&switch_device->switch_toggle.link); |
@@ -20,9 +19,22 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat, | |||
20 | return switch_device; | 19 | return switch_device; |
21 | } | 20 | } |
22 | 21 | ||
22 | static bool sway_switch_trigger_test(enum sway_switch_trigger trigger, | ||
23 | enum wlr_switch_state state) { | ||
24 | switch (trigger) { | ||
25 | case SWAY_SWITCH_TRIGGER_ON: | ||
26 | return state == WLR_SWITCH_STATE_ON; | ||
27 | case SWAY_SWITCH_TRIGGER_OFF: | ||
28 | return state == WLR_SWITCH_STATE_OFF; | ||
29 | case SWAY_SWITCH_TRIGGER_TOGGLE: | ||
30 | return true; | ||
31 | } | ||
32 | abort(); // unreachable | ||
33 | } | ||
34 | |||
23 | static void execute_binding(struct sway_switch *sway_switch) { | 35 | static void execute_binding(struct sway_switch *sway_switch) { |
24 | struct sway_seat* seat = sway_switch->seat_device->sway_seat; | 36 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; |
25 | bool input_inhibited = seat->exclusive_client != NULL; | 37 | bool locked = server.session_lock.lock; |
26 | 38 | ||
27 | list_t *bindings = config->current_mode->switch_bindings; | 39 | list_t *bindings = config->current_mode->switch_bindings; |
28 | struct sway_switch_binding *matched_binding = NULL; | 40 | struct sway_switch_binding *matched_binding = NULL; |
@@ -31,22 +43,21 @@ static void execute_binding(struct sway_switch *sway_switch) { | |||
31 | if (binding->type != sway_switch->type) { | 43 | if (binding->type != sway_switch->type) { |
32 | continue; | 44 | continue; |
33 | } | 45 | } |
34 | if (binding->state != WLR_SWITCH_STATE_TOGGLE && | 46 | if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) { |
35 | binding->state != sway_switch->state) { | ||
36 | continue; | 47 | continue; |
37 | } | 48 | } |
38 | if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE | 49 | if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE |
39 | || (binding->flags & BINDING_RELOAD) == 0)) { | 50 | || (binding->flags & BINDING_RELOAD) == 0)) { |
40 | continue; | 51 | continue; |
41 | } | 52 | } |
42 | bool binding_locked = binding->flags & BINDING_LOCKED; | 53 | bool binding_locked = binding->flags & BINDING_LOCKED; |
43 | if (!binding_locked && input_inhibited) { | 54 | if (!binding_locked && locked) { |
44 | continue; | 55 | continue; |
45 | } | 56 | } |
46 | 57 | ||
47 | matched_binding = binding; | 58 | matched_binding = binding; |
48 | 59 | ||
49 | if (binding_locked == input_inhibited) { | 60 | if (binding_locked == locked) { |
50 | break; | 61 | break; |
51 | } | 62 | } |
52 | } | 63 | } |
@@ -61,15 +72,12 @@ static void execute_binding(struct sway_switch *sway_switch) { | |||
61 | seat_execute_command(seat, dummy_binding); | 72 | seat_execute_command(seat, dummy_binding); |
62 | free(dummy_binding); | 73 | free(dummy_binding); |
63 | } | 74 | } |
64 | |||
65 | transaction_commit_dirty(); | ||
66 | |||
67 | } | 75 | } |
68 | 76 | ||
69 | static void handle_switch_toggle(struct wl_listener *listener, void *data) { | 77 | static void handle_switch_toggle(struct wl_listener *listener, void *data) { |
70 | struct sway_switch *sway_switch = | 78 | struct sway_switch *sway_switch = |
71 | wl_container_of(listener, sway_switch, switch_toggle); | 79 | wl_container_of(listener, sway_switch, switch_toggle); |
72 | struct wlr_event_switch_toggle *event = data; | 80 | struct wlr_switch_toggle_event *event = data; |
73 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; | 81 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; |
74 | seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); | 82 | seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); |
75 | 83 | ||
@@ -86,10 +94,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) { | |||
86 | } | 94 | } |
87 | 95 | ||
88 | void sway_switch_configure(struct sway_switch *sway_switch) { | 96 | void sway_switch_configure(struct sway_switch *sway_switch) { |
89 | struct wlr_input_device *wlr_device = | ||
90 | sway_switch->seat_device->input_device->wlr_device; | ||
91 | wl_list_remove(&sway_switch->switch_toggle.link); | 97 | wl_list_remove(&sway_switch->switch_toggle.link); |
92 | wl_signal_add(&wlr_device->switch_device->events.toggle, | 98 | wl_signal_add(&sway_switch->wlr->events.toggle, |
93 | &sway_switch->switch_toggle); | 99 | &sway_switch->switch_toggle); |
94 | sway_switch->switch_toggle.notify = handle_switch_toggle; | 100 | sway_switch->switch_toggle.notify = handle_switch_toggle; |
95 | sway_log(SWAY_DEBUG, "Configured switch for device"); | 101 | sway_log(SWAY_DEBUG, "Configured switch for device"); |
diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 26e86e36..2863642a 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <wlr/backend/libinput.h> | 2 | #include <wlr/config.h> |
4 | #include <wlr/types/wlr_tablet_v2.h> | 3 | #include <wlr/types/wlr_tablet_v2.h> |
5 | #include <wlr/types/wlr_tablet_tool.h> | 4 | #include <wlr/types/wlr_tablet_tool.h> |
6 | #include <wlr/types/wlr_tablet_pad.h> | 5 | #include <wlr/types/wlr_tablet_pad.h> |
@@ -9,6 +8,10 @@ | |||
9 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
10 | #include "sway/input/tablet.h" | 9 | #include "sway/input/tablet.h" |
11 | 10 | ||
11 | #if WLR_HAS_LIBINPUT_BACKEND | ||
12 | #include <wlr/backend/libinput.h> | ||
13 | #endif | ||
14 | |||
12 | static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { | 15 | static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { |
13 | struct sway_tablet_pad *pad = | 16 | struct sway_tablet_pad *pad = |
14 | wl_container_of(listener, pad, tablet_destroy); | 17 | wl_container_of(listener, pad, tablet_destroy); |
@@ -54,15 +57,14 @@ void sway_configure_tablet(struct sway_tablet *tablet) { | |||
54 | tablet->seat_device->input_device->wlr_device; | 57 | tablet->seat_device->input_device->wlr_device; |
55 | struct sway_seat *seat = tablet->seat_device->sway_seat; | 58 | struct sway_seat *seat = tablet->seat_device->sway_seat; |
56 | 59 | ||
57 | if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { | 60 | seat_configure_xcursor(seat); |
58 | seat_configure_xcursor(seat); | ||
59 | } | ||
60 | 61 | ||
61 | if (!tablet->tablet_v2) { | 62 | if (!tablet->tablet_v2) { |
62 | tablet->tablet_v2 = | 63 | tablet->tablet_v2 = |
63 | wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); | 64 | wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); |
64 | } | 65 | } |
65 | 66 | ||
67 | #if WLR_HAS_LIBINPUT_BACKEND | ||
66 | /* Search for a sibling tablet pad */ | 68 | /* Search for a sibling tablet pad */ |
67 | if (!wlr_input_device_is_libinput(device)) { | 69 | if (!wlr_input_device_is_libinput(device)) { |
68 | /* We can only do this on libinput devices */ | 70 | /* We can only do this on libinput devices */ |
@@ -87,6 +89,7 @@ void sway_configure_tablet(struct sway_tablet *tablet) { | |||
87 | break; | 89 | break; |
88 | } | 90 | } |
89 | } | 91 | } |
92 | #endif | ||
90 | } | 93 | } |
91 | 94 | ||
92 | void sway_tablet_destroy(struct sway_tablet *tablet) { | 95 | void sway_tablet_destroy(struct sway_tablet *tablet) { |
@@ -196,7 +199,7 @@ static void handle_tablet_pad_attach(struct wl_listener *listener, | |||
196 | 199 | ||
197 | static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { | 200 | static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { |
198 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); | 201 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); |
199 | struct wlr_event_tablet_pad_ring *event = data; | 202 | struct wlr_tablet_pad_ring_event *event = data; |
200 | 203 | ||
201 | if (!pad->current_surface) { | 204 | if (!pad->current_surface) { |
202 | return; | 205 | return; |
@@ -210,7 +213,7 @@ static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { | |||
210 | 213 | ||
211 | static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { | 214 | static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { |
212 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); | 215 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); |
213 | struct wlr_event_tablet_pad_strip *event = data; | 216 | struct wlr_tablet_pad_strip_event *event = data; |
214 | 217 | ||
215 | if (!pad->current_surface) { | 218 | if (!pad->current_surface) { |
216 | return; | 219 | return; |
@@ -224,7 +227,7 @@ static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { | |||
224 | 227 | ||
225 | static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { | 228 | static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { |
226 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); | 229 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); |
227 | struct wlr_event_tablet_pad_button *event = data; | 230 | struct wlr_tablet_pad_button_event *event = data; |
228 | 231 | ||
229 | if (!pad->current_surface) { | 232 | if (!pad->current_surface) { |
230 | return; | 233 | return; |
@@ -246,6 +249,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, | |||
246 | return NULL; | 249 | return NULL; |
247 | } | 250 | } |
248 | 251 | ||
252 | tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device); | ||
249 | tablet_pad->seat_device = device; | 253 | tablet_pad->seat_device = device; |
250 | wl_list_init(&tablet_pad->attach.link); | 254 | wl_list_init(&tablet_pad->attach.link); |
251 | wl_list_init(&tablet_pad->button.link); | 255 | wl_list_init(&tablet_pad->button.link); |
@@ -260,40 +264,41 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, | |||
260 | } | 264 | } |
261 | 265 | ||
262 | void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { | 266 | void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { |
263 | struct wlr_input_device *device = | 267 | struct wlr_input_device *wlr_device = |
264 | tablet_pad->seat_device->input_device->wlr_device; | 268 | tablet_pad->seat_device->input_device->wlr_device; |
265 | struct sway_seat *seat = tablet_pad->seat_device->sway_seat; | 269 | struct sway_seat *seat = tablet_pad->seat_device->sway_seat; |
266 | 270 | ||
267 | if (!tablet_pad->tablet_v2_pad) { | 271 | if (!tablet_pad->tablet_v2_pad) { |
268 | tablet_pad->tablet_v2_pad = | 272 | tablet_pad->tablet_v2_pad = |
269 | wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); | 273 | wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device); |
270 | } | 274 | } |
271 | 275 | ||
272 | wl_list_remove(&tablet_pad->attach.link); | 276 | wl_list_remove(&tablet_pad->attach.link); |
273 | tablet_pad->attach.notify = handle_tablet_pad_attach; | 277 | tablet_pad->attach.notify = handle_tablet_pad_attach; |
274 | wl_signal_add(&device->tablet_pad->events.attach_tablet, | 278 | wl_signal_add(&tablet_pad->wlr->events.attach_tablet, |
275 | &tablet_pad->attach); | 279 | &tablet_pad->attach); |
276 | 280 | ||
277 | wl_list_remove(&tablet_pad->button.link); | 281 | wl_list_remove(&tablet_pad->button.link); |
278 | tablet_pad->button.notify = handle_tablet_pad_button; | 282 | tablet_pad->button.notify = handle_tablet_pad_button; |
279 | wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); | 283 | wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button); |
280 | 284 | ||
281 | wl_list_remove(&tablet_pad->strip.link); | 285 | wl_list_remove(&tablet_pad->strip.link); |
282 | tablet_pad->strip.notify = handle_tablet_pad_strip; | 286 | tablet_pad->strip.notify = handle_tablet_pad_strip; |
283 | wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); | 287 | wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip); |
284 | 288 | ||
285 | wl_list_remove(&tablet_pad->ring.link); | 289 | wl_list_remove(&tablet_pad->ring.link); |
286 | tablet_pad->ring.notify = handle_tablet_pad_ring; | 290 | tablet_pad->ring.notify = handle_tablet_pad_ring; |
287 | wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); | 291 | wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring); |
288 | 292 | ||
293 | #if WLR_HAS_LIBINPUT_BACKEND | ||
289 | /* Search for a sibling tablet */ | 294 | /* Search for a sibling tablet */ |
290 | if (!wlr_input_device_is_libinput(device)) { | 295 | if (!wlr_input_device_is_libinput(wlr_device)) { |
291 | /* We can only do this on libinput devices */ | 296 | /* We can only do this on libinput devices */ |
292 | return; | 297 | return; |
293 | } | 298 | } |
294 | 299 | ||
295 | struct libinput_device_group *group = | 300 | struct libinput_device_group *group = |
296 | libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); | 301 | libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device)); |
297 | struct sway_tablet *tool; | 302 | struct sway_tablet *tool; |
298 | wl_list_for_each(tool, &seat->cursor->tablets, link) { | 303 | wl_list_for_each(tool, &seat->cursor->tablets, link) { |
299 | struct wlr_input_device *tablet = | 304 | struct wlr_input_device *tablet = |
@@ -310,6 +315,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { | |||
310 | break; | 315 | break; |
311 | } | 316 | } |
312 | } | 317 | } |
318 | #endif | ||
313 | } | 319 | } |
314 | 320 | ||
315 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { | 321 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { |
@@ -333,14 +339,10 @@ static void handle_pad_tablet_surface_destroy(struct wl_listener *listener, | |||
333 | struct sway_tablet_pad *tablet_pad = | 339 | struct sway_tablet_pad *tablet_pad = |
334 | wl_container_of(listener, tablet_pad, surface_destroy); | 340 | wl_container_of(listener, tablet_pad, surface_destroy); |
335 | 341 | ||
336 | wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, | 342 | sway_tablet_pad_set_focus(tablet_pad, NULL); |
337 | tablet_pad->current_surface); | ||
338 | wl_list_remove(&tablet_pad->surface_destroy.link); | ||
339 | wl_list_init(&tablet_pad->surface_destroy.link); | ||
340 | tablet_pad->current_surface = NULL; | ||
341 | } | 343 | } |
342 | 344 | ||
343 | void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | 345 | void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad, |
344 | struct wlr_surface *surface) { | 346 | struct wlr_surface *surface) { |
345 | if (!tablet_pad || !tablet_pad->tablet) { | 347 | if (!tablet_pad || !tablet_pad->tablet) { |
346 | return; | 348 | return; |
@@ -359,7 +361,8 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | |||
359 | tablet_pad->current_surface = NULL; | 361 | tablet_pad->current_surface = NULL; |
360 | } | 362 | } |
361 | 363 | ||
362 | if (!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { | 364 | if (surface == NULL || |
365 | !wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { | ||
363 | return; | 366 | return; |
364 | } | 367 | } |
365 | 368 | ||
@@ -367,7 +370,6 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | |||
367 | tablet_pad->tablet->tablet_v2, surface); | 370 | tablet_pad->tablet->tablet_v2, surface); |
368 | 371 | ||
369 | tablet_pad->current_surface = surface; | 372 | tablet_pad->current_surface = surface; |
370 | wl_list_remove(&tablet_pad->surface_destroy.link); | ||
371 | tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; | 373 | tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; |
372 | wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); | 374 | wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); |
373 | } | 375 | } |
diff --git a/sway/input/text_input.c b/sway/input/text_input.c index f83726ee..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) { |
@@ -55,6 +62,35 @@ static void handle_im_commit(struct wl_listener *listener, void *data) { | |||
55 | wlr_text_input_v3_send_done(text_input->input); | 62 | wlr_text_input_v3_send_done(text_input->input); |
56 | } | 63 | } |
57 | 64 | ||
65 | static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *data) { | ||
66 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
67 | input_method_keyboard_grab_destroy); | ||
68 | struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; | ||
69 | wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); | ||
70 | |||
71 | if (keyboard_grab->keyboard) { | ||
72 | // send modifier state to original client | ||
73 | wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat, | ||
74 | &keyboard_grab->keyboard->modifiers); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) { | ||
79 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
80 | input_method_grab_keyboard); | ||
81 | struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; | ||
82 | |||
83 | // send modifier state to grab | ||
84 | struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat); | ||
85 | wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, | ||
86 | active_keyboard); | ||
87 | |||
88 | wl_signal_add(&keyboard_grab->events.destroy, | ||
89 | &relay->input_method_keyboard_grab_destroy); | ||
90 | relay->input_method_keyboard_grab_destroy.notify = | ||
91 | handle_im_keyboard_grab_destroy; | ||
92 | } | ||
93 | |||
58 | static void text_input_set_pending_focused_surface( | 94 | static void text_input_set_pending_focused_surface( |
59 | struct sway_text_input *text_input, struct wlr_surface *surface) { | 95 | struct sway_text_input *text_input, struct wlr_surface *surface) { |
60 | wl_list_remove(&text_input->pending_focused_surface_destroy.link); | 96 | wl_list_remove(&text_input->pending_focused_surface_destroy.link); |
@@ -73,6 +109,10 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) { | |||
73 | input_method_destroy); | 109 | input_method_destroy); |
74 | struct wlr_input_method_v2 *context = data; | 110 | struct wlr_input_method_v2 *context = data; |
75 | 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); | ||
76 | relay->input_method = NULL; | 116 | relay->input_method = NULL; |
77 | 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); |
78 | if (text_input) { | 118 | if (text_input) { |
@@ -92,13 +132,23 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, | |||
92 | return; | 132 | return; |
93 | } | 133 | } |
94 | // TODO: only send each of those if they were modified | 134 | // TODO: only send each of those if they were modified |
95 | wlr_input_method_v2_send_surrounding_text(input_method, | 135 | if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) { |
96 | input->current.surrounding.text, input->current.surrounding.cursor, | 136 | wlr_input_method_v2_send_surrounding_text(input_method, |
97 | input->current.surrounding.anchor); | 137 | input->current.surrounding.text, input->current.surrounding.cursor, |
138 | input->current.surrounding.anchor); | ||
139 | } | ||
98 | wlr_input_method_v2_send_text_change_cause(input_method, | 140 | wlr_input_method_v2_send_text_change_cause(input_method, |
99 | input->current.text_change_cause); | 141 | input->current.text_change_cause); |
100 | wlr_input_method_v2_send_content_type(input_method, | 142 | if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) { |
101 | input->current.content_type.hint, input->current.content_type.purpose); | 143 | wlr_input_method_v2_send_content_type(input_method, |
144 | input->current.content_type.hint, | ||
145 | input->current.content_type.purpose); | ||
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 | } | ||
102 | wlr_input_method_v2_send_done(input_method); | 152 | wlr_input_method_v2_send_done(input_method); |
103 | // TODO: pass intent, display popup size | 153 | // TODO: pass intent, display popup size |
104 | } | 154 | } |
@@ -144,6 +194,10 @@ static void handle_text_input_disable(struct wl_listener *listener, | |||
144 | void *data) { | 194 | void *data) { |
145 | struct sway_text_input *text_input = wl_container_of(listener, text_input, | 195 | struct sway_text_input *text_input = wl_container_of(listener, text_input, |
146 | text_input_disable); | 196 | text_input_disable); |
197 | if (text_input->input->focused_surface == NULL) { | ||
198 | sway_log(SWAY_DEBUG, "Disabling text input, but no longer focused"); | ||
199 | return; | ||
200 | } | ||
147 | relay_disable_text_input(text_input->relay, text_input); | 201 | relay_disable_text_input(text_input->relay, text_input); |
148 | } | 202 | } |
149 | 203 | ||
@@ -217,6 +271,211 @@ static void relay_handle_text_input(struct wl_listener *listener, | |||
217 | sway_text_input_create(relay, wlr_text_input); | 271 | sway_text_input_create(relay, wlr_text_input); |
218 | } | 272 | } |
219 | 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 | |||
220 | static void relay_handle_input_method(struct wl_listener *listener, | 479 | static void relay_handle_input_method(struct wl_listener *listener, |
221 | void *data) { | 480 | void *data) { |
222 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | 481 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, |
@@ -236,13 +495,19 @@ static void relay_handle_input_method(struct wl_listener *listener, | |||
236 | wl_signal_add(&relay->input_method->events.commit, | 495 | wl_signal_add(&relay->input_method->events.commit, |
237 | &relay->input_method_commit); | 496 | &relay->input_method_commit); |
238 | relay->input_method_commit.notify = handle_im_commit; | 497 | relay->input_method_commit.notify = handle_im_commit; |
498 | wl_signal_add(&relay->input_method->events.grab_keyboard, | ||
499 | &relay->input_method_grab_keyboard); | ||
500 | relay->input_method_grab_keyboard.notify = handle_im_grab_keyboard; | ||
239 | wl_signal_add(&relay->input_method->events.destroy, | 501 | wl_signal_add(&relay->input_method->events.destroy, |
240 | &relay->input_method_destroy); | 502 | &relay->input_method_destroy); |
241 | 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; | ||
242 | 507 | ||
243 | 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); |
244 | if (text_input) { | 509 | if (text_input) { |
245 | wlr_text_input_v3_send_enter(text_input->input, | 510 | text_input_send_enter(text_input, |
246 | text_input->pending_focused_surface); | 511 | text_input->pending_focused_surface); |
247 | text_input_set_pending_focused_surface(text_input, NULL); | 512 | text_input_set_pending_focused_surface(text_input, NULL); |
248 | } | 513 | } |
@@ -252,6 +517,7 @@ void sway_input_method_relay_init(struct sway_seat *seat, | |||
252 | struct sway_input_method_relay *relay) { | 517 | struct sway_input_method_relay *relay) { |
253 | relay->seat = seat; | 518 | relay->seat = seat; |
254 | wl_list_init(&relay->text_inputs); | 519 | wl_list_init(&relay->text_inputs); |
520 | wl_list_init(&relay->input_popups); | ||
255 | 521 | ||
256 | relay->text_input_new.notify = relay_handle_text_input; | 522 | relay->text_input_new.notify = relay_handle_text_input; |
257 | 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 fceee84d..81ca3483 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c | |||
@@ -1,7 +1,12 @@ | |||
1 | #include <ctype.h> | ||
2 | #include <float.h> | ||
1 | #include <json.h> | 3 | #include <json.h> |
2 | #include <libevdev/libevdev.h> | 4 | #include <libevdev/libevdev.h> |
3 | #include <stdio.h> | 5 | #include <stdio.h> |
4 | #include <ctype.h> | 6 | #include <wlr/config.h> |
7 | #include <wlr/types/wlr_content_type_v1.h> | ||
8 | #include <wlr/types/wlr_output.h> | ||
9 | #include <xkbcommon/xkbcommon.h> | ||
5 | #include "config.h" | 10 | #include "config.h" |
6 | #include "log.h" | 11 | #include "log.h" |
7 | #include "sway/config.h" | 12 | #include "sway/config.h" |
@@ -13,16 +18,30 @@ | |||
13 | #include "sway/input/input-manager.h" | 18 | #include "sway/input/input-manager.h" |
14 | #include "sway/input/cursor.h" | 19 | #include "sway/input/cursor.h" |
15 | #include "sway/input/seat.h" | 20 | #include "sway/input/seat.h" |
16 | #include <wlr/backend/libinput.h> | ||
17 | #include <wlr/types/wlr_box.h> | ||
18 | #include <wlr/types/wlr_output.h> | ||
19 | #include <xkbcommon/xkbcommon.h> | ||
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 | ||
31 | static const char *ipc_json_node_type_description(enum sway_node_type node_type) { | ||
32 | switch (node_type) { | ||
33 | case N_ROOT: | ||
34 | return "root"; | ||
35 | case N_OUTPUT: | ||
36 | return "output"; | ||
37 | case N_WORKSPACE: | ||
38 | return "workspace"; | ||
39 | case N_CONTAINER: | ||
40 | return "con"; | ||
41 | } | ||
42 | return "none"; | ||
43 | } | ||
44 | |||
26 | static const char *ipc_json_layout_description(enum sway_container_layout l) { | 45 | static const char *ipc_json_layout_description(enum sway_container_layout l) { |
27 | switch (l) { | 46 | switch (l) { |
28 | case L_VERT: | 47 | case L_VERT: |
@@ -98,12 +117,43 @@ static const char *ipc_json_output_adaptive_sync_status_description( | |||
98 | return "disabled"; | 117 | return "disabled"; |
99 | case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: | 118 | case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: |
100 | return "enabled"; | 119 | return "enabled"; |
101 | case WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN: | ||
102 | return "unknown"; | ||
103 | } | 120 | } |
104 | return NULL; | 121 | return NULL; |
105 | } | 122 | } |
106 | 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 | |||
107 | #if HAVE_XWAYLAND | 157 | #if HAVE_XWAYLAND |
108 | 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) { |
109 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; | 159 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; |
@@ -156,6 +206,20 @@ static const char *ipc_json_user_idle_inhibitor_description(enum sway_idle_inhib | |||
156 | return NULL; | 206 | return NULL; |
157 | } | 207 | } |
158 | 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 | |||
159 | json_object *ipc_json_get_version(void) { | 223 | json_object *ipc_json_get_version(void) { |
160 | int major = 0, minor = 0, patch = 0; | 224 | int major = 0, minor = 0, patch = 0; |
161 | json_object *version = json_object_new_object(); | 225 | json_object *version = json_object_new_object(); |
@@ -189,16 +253,22 @@ static json_object *ipc_json_create_empty_rect(void) { | |||
189 | return ipc_json_create_rect(&empty); | 253 | return ipc_json_create_rect(&empty); |
190 | } | 254 | } |
191 | 255 | ||
192 | static json_object *ipc_json_create_node(int id, char *name, | 256 | static json_object *ipc_json_create_node(int id, const char* type, char *name, |
193 | bool focused, json_object *focus, struct wlr_box *box) { | 257 | bool focused, json_object *focus, struct wlr_box *box) { |
194 | json_object *object = json_object_new_object(); | 258 | json_object *object = json_object_new_object(); |
195 | 259 | ||
196 | json_object_object_add(object, "id", json_object_new_int(id)); | 260 | json_object_object_add(object, "id", json_object_new_int(id)); |
197 | json_object_object_add(object, "name", | 261 | json_object_object_add(object, "type", json_object_new_string(type)); |
198 | name ? json_object_new_string(name) : NULL); | 262 | json_object_object_add(object, "orientation", |
199 | json_object_object_add(object, "rect", ipc_json_create_rect(box)); | 263 | json_object_new_string( |
264 | ipc_json_orientation_description(L_HORIZ))); | ||
265 | json_object_object_add(object, "percent", NULL); | ||
266 | json_object_object_add(object, "urgent", json_object_new_boolean(false)); | ||
267 | json_object_object_add(object, "marks", json_object_new_array()); | ||
200 | json_object_object_add(object, "focused", json_object_new_boolean(focused)); | 268 | json_object_object_add(object, "focused", json_object_new_boolean(focused)); |
201 | json_object_object_add(object, "focus", focus); | 269 | json_object_object_add(object, "layout", |
270 | json_object_new_string( | ||
271 | ipc_json_layout_description(L_HORIZ))); | ||
202 | 272 | ||
203 | // set default values to be compatible with i3 | 273 | // set default values to be compatible with i3 |
204 | json_object_object_add(object, "border", | 274 | json_object_object_add(object, "border", |
@@ -206,49 +276,68 @@ static json_object *ipc_json_create_node(int id, char *name, | |||
206 | ipc_json_border_description(B_NONE))); | 276 | ipc_json_border_description(B_NONE))); |
207 | json_object_object_add(object, "current_border_width", | 277 | json_object_object_add(object, "current_border_width", |
208 | json_object_new_int(0)); | 278 | json_object_new_int(0)); |
209 | json_object_object_add(object, "layout", | 279 | json_object_object_add(object, "rect", ipc_json_create_rect(box)); |
210 | json_object_new_string( | ||
211 | ipc_json_layout_description(L_HORIZ))); | ||
212 | json_object_object_add(object, "orientation", | ||
213 | json_object_new_string( | ||
214 | ipc_json_orientation_description(L_HORIZ))); | ||
215 | json_object_object_add(object, "percent", NULL); | ||
216 | json_object_object_add(object, "window_rect", ipc_json_create_empty_rect()); | ||
217 | json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect()); | 280 | json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect()); |
281 | json_object_object_add(object, "window_rect", ipc_json_create_empty_rect()); | ||
218 | json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); | 282 | json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); |
283 | json_object_object_add(object, "name", | ||
284 | name ? json_object_new_string(name) : NULL); | ||
219 | json_object_object_add(object, "window", NULL); | 285 | json_object_object_add(object, "window", NULL); |
220 | json_object_object_add(object, "urgent", json_object_new_boolean(false)); | ||
221 | json_object_object_add(object, "marks", json_object_new_array()); | ||
222 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); | ||
223 | json_object_object_add(object, "nodes", json_object_new_array()); | 286 | json_object_object_add(object, "nodes", json_object_new_array()); |
224 | json_object_object_add(object, "floating_nodes", json_object_new_array()); | 287 | json_object_object_add(object, "floating_nodes", json_object_new_array()); |
288 | json_object_object_add(object, "focus", focus); | ||
289 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); | ||
225 | 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); | ||
226 | 293 | ||
227 | return object; | 294 | return object; |
228 | } | 295 | } |
229 | 296 | ||
230 | static void ipc_json_describe_root(struct sway_root *root, json_object *object) { | 297 | static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) { |
231 | json_object_object_add(object, "type", json_object_new_string("root")); | 298 | json_object_object_add(object, "primary", json_object_new_boolean(false)); |
299 | json_object_object_add(object, "make", | ||
300 | json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); | ||
301 | json_object_object_add(object, "model", | ||
302 | json_object_new_string(wlr_output->model ? wlr_output->model : "Unknown")); | ||
303 | json_object_object_add(object, "serial", | ||
304 | json_object_new_string(wlr_output->serial ? wlr_output->serial : "Unknown")); | ||
305 | |||
306 | json_object *modes_array = json_object_new_array(); | ||
307 | struct wlr_output_mode *mode; | ||
308 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
309 | json_object *mode_object = json_object_new_object(); | ||
310 | json_object_object_add(mode_object, "width", | ||
311 | json_object_new_int(mode->width)); | ||
312 | json_object_object_add(mode_object, "height", | ||
313 | json_object_new_int(mode->height)); | ||
314 | json_object_object_add(mode_object, "refresh", | ||
315 | json_object_new_int(mode->refresh)); | ||
316 | json_object_array_add(modes_array, mode_object); | ||
317 | } | ||
318 | json_object_object_add(object, "modes", modes_array); | ||
232 | } | 319 | } |
233 | 320 | ||
234 | static void ipc_json_describe_output(struct sway_output *output, | 321 | static void ipc_json_describe_output(struct sway_output *output, |
235 | json_object *object) { | 322 | json_object *object) { |
323 | ipc_json_describe_wlr_output(output->wlr_output, object); | ||
324 | } | ||
325 | |||
326 | static void ipc_json_describe_enabled_output(struct sway_output *output, | ||
327 | json_object *object) { | ||
328 | ipc_json_describe_output(output, object); | ||
329 | |||
236 | struct wlr_output *wlr_output = output->wlr_output; | 330 | struct wlr_output *wlr_output = output->wlr_output; |
237 | json_object_object_add(object, "type", json_object_new_string("output")); | 331 | json_object_object_add(object, "non_desktop", json_object_new_boolean(false)); |
238 | json_object_object_add(object, "active", json_object_new_boolean(true)); | 332 | json_object_object_add(object, "active", json_object_new_boolean(true)); |
239 | json_object_object_add(object, "dpms", | 333 | json_object_object_add(object, "dpms", |
240 | json_object_new_boolean(wlr_output->enabled)); | 334 | json_object_new_boolean(wlr_output->enabled)); |
241 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | 335 | json_object_object_add(object, "power", |
336 | json_object_new_boolean(wlr_output->enabled)); | ||
242 | json_object_object_add(object, "layout", json_object_new_string("output")); | 337 | json_object_object_add(object, "layout", json_object_new_string("output")); |
243 | json_object_object_add(object, "orientation", | 338 | json_object_object_add(object, "orientation", |
244 | json_object_new_string( | 339 | json_object_new_string( |
245 | ipc_json_orientation_description(L_NONE))); | 340 | ipc_json_orientation_description(L_NONE))); |
246 | json_object_object_add(object, "make", | ||
247 | json_object_new_string(wlr_output->make)); | ||
248 | json_object_object_add(object, "model", | ||
249 | json_object_new_string(wlr_output->model)); | ||
250 | json_object_object_add(object, "serial", | ||
251 | json_object_new_string(wlr_output->serial)); | ||
252 | json_object_object_add(object, "scale", | 341 | json_object_object_add(object, "scale", |
253 | json_object_new_double(wlr_output->scale)); | 342 | json_object_new_double(wlr_output->scale)); |
254 | json_object_object_add(object, "scale_filter", | 343 | json_object_object_add(object, "scale_filter", |
@@ -273,25 +362,26 @@ static void ipc_json_describe_output(struct sway_output *output, | |||
273 | json_object *modes_array = json_object_new_array(); | 362 | json_object *modes_array = json_object_new_array(); |
274 | struct wlr_output_mode *mode; | 363 | struct wlr_output_mode *mode; |
275 | wl_list_for_each(mode, &wlr_output->modes, link) { | 364 | wl_list_for_each(mode, &wlr_output->modes, link) { |
276 | json_object *mode_object = json_object_new_object(); | 365 | json_object *mode_object = |
277 | json_object_object_add(mode_object, "width", | 366 | ipc_json_output_mode_description(mode); |
278 | json_object_new_int(mode->width)); | ||
279 | json_object_object_add(mode_object, "height", | ||
280 | json_object_new_int(mode->height)); | ||
281 | json_object_object_add(mode_object, "refresh", | ||
282 | json_object_new_int(mode->refresh)); | ||
283 | json_object_array_add(modes_array, mode_object); | 367 | json_object_array_add(modes_array, mode_object); |
284 | } | 368 | } |
285 | 369 | ||
286 | json_object_object_add(object, "modes", modes_array); | 370 | json_object_object_add(object, "modes", modes_array); |
287 | 371 | ||
288 | json_object *current_mode_object = json_object_new_object(); | 372 | json_object *current_mode_object; |
289 | json_object_object_add(current_mode_object, "width", | 373 | if (wlr_output->current_mode != NULL) { |
290 | json_object_new_int(wlr_output->width)); | 374 | current_mode_object = |
291 | json_object_object_add(current_mode_object, "height", | 375 | ipc_json_output_mode_description(wlr_output->current_mode); |
292 | json_object_new_int(wlr_output->height)); | 376 | } else { |
293 | json_object_object_add(current_mode_object, "refresh", | 377 | current_mode_object = json_object_new_object(); |
294 | 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 | } | ||
295 | json_object_object_add(object, "current_mode", current_mode_object); | 385 | json_object_object_add(object, "current_mode", current_mode_object); |
296 | 386 | ||
297 | struct sway_node *parent = node_get_parent(&output->node); | 387 | struct sway_node *parent = node_get_parent(&output->node); |
@@ -315,33 +405,15 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { | |||
315 | 405 | ||
316 | json_object *object = json_object_new_object(); | 406 | json_object *object = json_object_new_object(); |
317 | 407 | ||
408 | ipc_json_describe_output(output, object); | ||
409 | |||
410 | json_object_object_add(object, "non_desktop", json_object_new_boolean(false)); | ||
318 | json_object_object_add(object, "type", json_object_new_string("output")); | 411 | json_object_object_add(object, "type", json_object_new_string("output")); |
319 | json_object_object_add(object, "name", | 412 | json_object_object_add(object, "name", |
320 | json_object_new_string(wlr_output->name)); | 413 | json_object_new_string(wlr_output->name)); |
321 | json_object_object_add(object, "active", json_object_new_boolean(false)); | 414 | json_object_object_add(object, "active", json_object_new_boolean(false)); |
322 | json_object_object_add(object, "dpms", json_object_new_boolean(false)); | 415 | json_object_object_add(object, "dpms", json_object_new_boolean(false)); |
323 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | 416 | json_object_object_add(object, "power", json_object_new_boolean(false)); |
324 | json_object_object_add(object, "make", | ||
325 | json_object_new_string(wlr_output->make)); | ||
326 | json_object_object_add(object, "model", | ||
327 | json_object_new_string(wlr_output->model)); | ||
328 | json_object_object_add(object, "serial", | ||
329 | json_object_new_string(wlr_output->serial)); | ||
330 | |||
331 | json_object *modes_array = json_object_new_array(); | ||
332 | struct wlr_output_mode *mode; | ||
333 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
334 | json_object *mode_object = json_object_new_object(); | ||
335 | json_object_object_add(mode_object, "width", | ||
336 | json_object_new_int(mode->width)); | ||
337 | json_object_object_add(mode_object, "height", | ||
338 | json_object_new_int(mode->height)); | ||
339 | json_object_object_add(mode_object, "refresh", | ||
340 | json_object_new_int(mode->refresh)); | ||
341 | json_object_array_add(modes_array, mode_object); | ||
342 | } | ||
343 | |||
344 | json_object_object_add(object, "modes", modes_array); | ||
345 | 417 | ||
346 | json_object_object_add(object, "current_workspace", NULL); | 418 | json_object_object_add(object, "current_workspace", NULL); |
347 | 419 | ||
@@ -357,6 +429,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { | |||
357 | return object; | 429 | return object; |
358 | } | 430 | } |
359 | 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 | |||
360 | static json_object *ipc_json_describe_scratchpad_output(void) { | 447 | static json_object *ipc_json_describe_scratchpad_output(void) { |
361 | struct wlr_box box; | 448 | struct wlr_box box; |
362 | root_get_box(root, &box); | 449 | root_get_box(root, &box); |
@@ -369,11 +456,9 @@ static json_object *ipc_json_describe_scratchpad_output(void) { | |||
369 | json_object_new_int(container->node.id)); | 456 | json_object_new_int(container->node.id)); |
370 | } | 457 | } |
371 | 458 | ||
372 | json_object *workspace = ipc_json_create_node(i3_scratch_id, | 459 | json_object *workspace = ipc_json_create_node(i3_scratch_id, "workspace", |
373 | "__i3_scratch", false, workspace_focus, &box); | 460 | "__i3_scratch", false, workspace_focus, &box); |
374 | json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1)); | 461 | json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1)); |
375 | json_object_object_add(workspace, "type", | ||
376 | json_object_new_string("workspace")); | ||
377 | 462 | ||
378 | // List all hidden scratchpad containers as floating nodes | 463 | // List all hidden scratchpad containers as floating nodes |
379 | json_object *floating_array = json_object_new_array(); | 464 | json_object *floating_array = json_object_new_array(); |
@@ -390,10 +475,8 @@ static json_object *ipc_json_describe_scratchpad_output(void) { | |||
390 | json_object *output_focus = json_object_new_array(); | 475 | json_object *output_focus = json_object_new_array(); |
391 | json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); | 476 | json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); |
392 | 477 | ||
393 | json_object *output = ipc_json_create_node(i3_output_id, | 478 | json_object *output = ipc_json_create_node(i3_output_id, "output", |
394 | "__i3", false, output_focus, &box); | 479 | "__i3", false, output_focus, &box); |
395 | json_object_object_add(output, "type", | ||
396 | json_object_new_string("output")); | ||
397 | json_object_object_add(output, "layout", | 480 | json_object_object_add(output, "layout", |
398 | json_object_new_string("output")); | 481 | json_object_new_string("output")); |
399 | 482 | ||
@@ -423,7 +506,6 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, | |||
423 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); | 506 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); |
424 | json_object_object_add(object, "output", workspace->output ? | 507 | json_object_object_add(object, "output", workspace->output ? |
425 | json_object_new_string(workspace->output->wlr_output->name) : NULL); | 508 | json_object_new_string(workspace->output->wlr_output->name) : NULL); |
426 | json_object_object_add(object, "type", json_object_new_string("workspace")); | ||
427 | json_object_object_add(object, "urgent", | 509 | json_object_object_add(object, "urgent", |
428 | json_object_new_boolean(workspace->urgent)); | 510 | json_object_new_boolean(workspace->urgent)); |
429 | json_object_object_add(object, "representation", workspace->representation ? | 511 | json_object_object_add(object, "representation", workspace->representation ? |
@@ -448,30 +530,32 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, | |||
448 | 530 | ||
449 | static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { | 531 | static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { |
450 | enum sway_container_layout parent_layout = container_parent_layout(c); | 532 | enum sway_container_layout parent_layout = container_parent_layout(c); |
451 | bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; | 533 | list_t *siblings = container_get_siblings(c); |
534 | bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED) | ||
535 | && ((siblings && siblings->length > 1) || !config->hide_lone_tab); | ||
452 | if (((!tab_or_stack || container_is_floating(c)) && | 536 | if (((!tab_or_stack || container_is_floating(c)) && |
453 | c->current.border != B_NORMAL) || | 537 | c->current.border != B_NORMAL) || |
454 | c->fullscreen_mode != FULLSCREEN_NONE || | 538 | c->pending.fullscreen_mode != FULLSCREEN_NONE || |
455 | c->workspace == NULL) { | 539 | c->pending.workspace == NULL) { |
456 | deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; | 540 | deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; |
457 | return; | 541 | return; |
458 | } | 542 | } |
459 | 543 | ||
460 | if (c->parent) { | 544 | if (c->pending.parent) { |
461 | deco_rect->x = c->x - c->parent->x; | 545 | deco_rect->x = c->pending.x - c->pending.parent->pending.x; |
462 | deco_rect->y = c->y - c->parent->y; | 546 | deco_rect->y = c->pending.y - c->pending.parent->pending.y; |
463 | } else { | 547 | } else { |
464 | deco_rect->x = c->x - c->workspace->x; | 548 | deco_rect->x = c->pending.x - c->pending.workspace->x; |
465 | deco_rect->y = c->y - c->workspace->y; | 549 | deco_rect->y = c->pending.y - c->pending.workspace->y; |
466 | } | 550 | } |
467 | deco_rect->width = c->width; | 551 | deco_rect->width = c->pending.width; |
468 | deco_rect->height = container_titlebar_height(); | 552 | deco_rect->height = container_titlebar_height(); |
469 | 553 | ||
470 | if (!container_is_floating(c)) { | 554 | if (!container_is_floating(c)) { |
471 | if (parent_layout == L_TABBED) { | 555 | if (parent_layout == L_TABBED) { |
472 | deco_rect->width = c->parent | 556 | deco_rect->width = c->pending.parent |
473 | ? c->parent->width / c->parent->children->length | 557 | ? c->pending.parent->pending.width / c->pending.parent->pending.children->length |
474 | : c->workspace->width / c->workspace->tiling->length; | 558 | : c->pending.workspace->width / c->pending.workspace->tiling->length; |
475 | deco_rect->x += deco_rect->width * container_sibling_index(c); | 559 | deco_rect->x += deco_rect->width * container_sibling_index(c); |
476 | } else if (parent_layout == L_STACKED) { | 560 | } else if (parent_layout == L_STACKED) { |
477 | if (!c->view) { | 561 | if (!c->view) { |
@@ -494,10 +578,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
494 | json_object_object_add(object, "visible", json_object_new_boolean(visible)); | 578 | json_object_object_add(object, "visible", json_object_new_boolean(visible)); |
495 | 579 | ||
496 | struct wlr_box window_box = { | 580 | struct wlr_box window_box = { |
497 | c->content_x - c->x, | 581 | c->pending.content_x - c->pending.x, |
498 | (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, | 582 | (c->current.border == B_PIXEL) ? c->pending.content_y - c->pending.y : 0, |
499 | c->content_width, | 583 | c->pending.content_width, |
500 | c->content_height | 584 | c->pending.content_height |
501 | }; | 585 | }; |
502 | 586 | ||
503 | json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); | 587 | json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); |
@@ -539,6 +623,16 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
539 | 623 | ||
540 | json_object_object_add(object, "idle_inhibitors", idle_inhibitors); | 624 | json_object_object_add(object, "idle_inhibitors", idle_inhibitors); |
541 | 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 | |||
542 | #if HAVE_XWAYLAND | 636 | #if HAVE_XWAYLAND |
543 | if (c->view->type == SWAY_VIEW_XWAYLAND) { | 637 | if (c->view->type == SWAY_VIEW_XWAYLAND) { |
544 | json_object_object_add(object, "window", | 638 | json_object_object_add(object, "window", |
@@ -583,24 +677,35 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
583 | 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) { |
584 | json_object_object_add(object, "name", | 678 | json_object_object_add(object, "name", |
585 | c->title ? json_object_new_string(c->title) : NULL); | 679 | c->title ? json_object_new_string(c->title) : NULL); |
586 | json_object_object_add(object, "type", | 680 | bool floating = container_is_floating(c); |
587 | json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); | 681 | if (floating) { |
682 | json_object_object_add(object, "type", | ||
683 | json_object_new_string("floating_con")); | ||
684 | } | ||
588 | 685 | ||
589 | json_object_object_add(object, "layout", | 686 | json_object_object_add(object, "layout", |
590 | json_object_new_string( | 687 | json_object_new_string( |
591 | ipc_json_layout_description(c->layout))); | 688 | ipc_json_layout_description(c->pending.layout))); |
592 | 689 | ||
593 | json_object_object_add(object, "orientation", | 690 | json_object_object_add(object, "orientation", |
594 | json_object_new_string( | 691 | json_object_new_string( |
595 | ipc_json_orientation_description(c->layout))); | 692 | ipc_json_orientation_description(c->pending.layout))); |
596 | 693 | ||
597 | bool urgent = c->view ? | 694 | bool urgent = c->view ? |
598 | view_is_urgent(c->view) : container_has_urgent_child(c); | 695 | view_is_urgent(c->view) : container_has_urgent_child(c); |
599 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); | 696 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); |
600 | 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)); |
601 | 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 | |||
602 | json_object_object_add(object, "fullscreen_mode", | 703 | json_object_object_add(object, "fullscreen_mode", |
603 | json_object_new_int(c->fullscreen_mode)); | 704 | json_object_new_int(c->pending.fullscreen_mode)); |
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")); | ||
604 | 709 | ||
605 | struct sway_node *parent = node_get_parent(&c->node); | 710 | struct sway_node *parent = node_get_parent(&c->node); |
606 | struct wlr_box parent_box = {0, 0, 0, 0}; | 711 | struct wlr_box parent_box = {0, 0, 0, 0}; |
@@ -610,8 +715,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o | |||
610 | } | 715 | } |
611 | 716 | ||
612 | if (parent_box.width != 0 && parent_box.height != 0) { | 717 | if (parent_box.width != 0 && parent_box.height != 0) { |
613 | double percent = ((double)c->width / parent_box.width) | 718 | double percent = ((double)c->pending.width / parent_box.width) |
614 | * ((double)c->height / parent_box.height); | 719 | * ((double)c->pending.height / parent_box.height); |
615 | json_object_object_add(object, "percent", json_object_new_double(percent)); | 720 | json_object_object_add(object, "percent", json_object_new_double(percent)); |
616 | } | 721 | } |
617 | 722 | ||
@@ -692,15 +797,14 @@ json_object *ipc_json_describe_node(struct sway_node *node) { | |||
692 | }; | 797 | }; |
693 | seat_for_each_node(seat, focus_inactive_children_iterator, &data); | 798 | seat_for_each_node(seat, focus_inactive_children_iterator, &data); |
694 | 799 | ||
695 | json_object *object = ipc_json_create_node( | 800 | json_object *object = ipc_json_create_node((int)node->id, |
696 | (int)node->id, name, focused, focus, &box); | 801 | ipc_json_node_type_description(node->type), name, focused, focus, &box); |
697 | 802 | ||
698 | switch (node->type) { | 803 | switch (node->type) { |
699 | case N_ROOT: | 804 | case N_ROOT: |
700 | ipc_json_describe_root(root, object); | ||
701 | break; | 805 | break; |
702 | case N_OUTPUT: | 806 | case N_OUTPUT: |
703 | ipc_json_describe_output(node->sway_output, object); | 807 | ipc_json_describe_enabled_output(node->sway_output, object); |
704 | break; | 808 | break; |
705 | case N_CONTAINER: | 809 | case N_CONTAINER: |
706 | ipc_json_describe_container(node->sway_container, object); | 810 | ipc_json_describe_container(node->sway_container, object); |
@@ -743,10 +847,10 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { | |||
743 | } | 847 | } |
744 | break; | 848 | break; |
745 | case N_CONTAINER: | 849 | case N_CONTAINER: |
746 | if (node->sway_container->children) { | 850 | if (node->sway_container->pending.children) { |
747 | for (i = 0; i < node->sway_container->children->length; ++i) { | 851 | for (i = 0; i < node->sway_container->pending.children->length; ++i) { |
748 | struct sway_container *child = | 852 | struct sway_container *child = |
749 | node->sway_container->children->items[i]; | 853 | node->sway_container->pending.children->items[i]; |
750 | json_object_array_add(children, | 854 | json_object_array_add(children, |
751 | ipc_json_describe_node_recursive(&child->node)); | 855 | ipc_json_describe_node_recursive(&child->node)); |
752 | } | 856 | } |
@@ -758,6 +862,7 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { | |||
758 | return object; | 862 | return object; |
759 | } | 863 | } |
760 | 864 | ||
865 | #if WLR_HAS_LIBINPUT_BACKEND | ||
761 | static json_object *describe_libinput_device(struct libinput_device *device) { | 866 | static json_object *describe_libinput_device(struct libinput_device *device) { |
762 | json_object *object = json_object_new_object(); | 867 | json_object *object = json_object_new_object(); |
763 | 868 | ||
@@ -841,6 +946,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
841 | case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: | 946 | case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: |
842 | accel_profile = "adaptive"; | 947 | accel_profile = "adaptive"; |
843 | 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 | ||
844 | } | 954 | } |
845 | json_object_object_add(object, "accel_profile", | 955 | json_object_object_add(object, "accel_profile", |
846 | json_object_new_string(accel_profile)); | 956 | json_object_new_string(accel_profile)); |
@@ -920,6 +1030,17 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
920 | uint32_t button = libinput_device_config_scroll_get_button(device); | 1030 | uint32_t button = libinput_device_config_scroll_get_button(device); |
921 | json_object_object_add(object, "scroll_button", | 1031 | json_object_object_add(object, "scroll_button", |
922 | 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)); | ||
923 | } | 1044 | } |
924 | } | 1045 | } |
925 | 1046 | ||
@@ -936,6 +1057,19 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
936 | json_object_object_add(object, "dwt", json_object_new_string(dwt)); | 1057 | json_object_object_add(object, "dwt", json_object_new_string(dwt)); |
937 | } | 1058 | } |
938 | 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 | |||
939 | if (libinput_device_config_calibration_has_matrix(device)) { | 1073 | if (libinput_device_config_calibration_has_matrix(device)) { |
940 | float matrix[6]; | 1074 | float matrix[6]; |
941 | libinput_device_config_calibration_get_matrix(device, matrix); | 1075 | libinput_device_config_calibration_get_matrix(device, matrix); |
@@ -950,6 +1084,7 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
950 | 1084 | ||
951 | return object; | 1085 | return object; |
952 | } | 1086 | } |
1087 | #endif | ||
953 | 1088 | ||
954 | json_object *ipc_json_describe_input(struct sway_input_device *device) { | 1089 | json_object *ipc_json_describe_input(struct sway_input_device *device) { |
955 | if (!(sway_assert(device, "Device must not be null"))) { | 1090 | if (!(sway_assert(device, "Device must not be null"))) { |
@@ -962,19 +1097,21 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { | |||
962 | json_object_new_string(device->identifier)); | 1097 | json_object_new_string(device->identifier)); |
963 | json_object_object_add(object, "name", | 1098 | json_object_object_add(object, "name", |
964 | json_object_new_string(device->wlr_device->name)); | 1099 | json_object_new_string(device->wlr_device->name)); |
965 | json_object_object_add(object, "vendor", | ||
966 | json_object_new_int(device->wlr_device->vendor)); | ||
967 | json_object_object_add(object, "product", | ||
968 | json_object_new_int(device->wlr_device->product)); | ||
969 | json_object_object_add(object, "type", | 1100 | json_object_object_add(object, "type", |
970 | json_object_new_string( | 1101 | json_object_new_string( |
971 | input_device_get_type(device))); | 1102 | input_device_get_type(device))); |
972 | 1103 | ||
973 | if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { | 1104 | if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { |
974 | struct wlr_keyboard *keyboard = device->wlr_device->keyboard; | 1105 | struct wlr_keyboard *keyboard = |
1106 | wlr_keyboard_from_input_device(device->wlr_device); | ||
975 | struct xkb_keymap *keymap = keyboard->keymap; | 1107 | struct xkb_keymap *keymap = keyboard->keymap; |
976 | struct xkb_state *state = keyboard->xkb_state; | 1108 | struct xkb_state *state = keyboard->xkb_state; |
977 | 1109 | ||
1110 | json_object_object_add(object, "repeat_delay", | ||
1111 | json_object_new_int(keyboard->repeat_info.delay)); | ||
1112 | json_object_object_add(object, "repeat_rate", | ||
1113 | json_object_new_int(keyboard->repeat_info.rate)); | ||
1114 | |||
978 | json_object *layouts_arr = json_object_new_array(); | 1115 | json_object *layouts_arr = json_object_new_array(); |
979 | json_object_object_add(object, "xkb_layout_names", layouts_arr); | 1116 | json_object_object_add(object, "xkb_layout_names", layouts_arr); |
980 | 1117 | ||
@@ -996,12 +1133,29 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { | |||
996 | } | 1133 | } |
997 | } | 1134 | } |
998 | 1135 | ||
1136 | if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { | ||
1137 | struct input_config *ic = input_device_get_config(device); | ||
1138 | float scroll_factor = 1.0f; | ||
1139 | if (ic != NULL && !isnan(ic->scroll_factor) && | ||
1140 | ic->scroll_factor != FLT_MIN) { | ||
1141 | scroll_factor = ic->scroll_factor; | ||
1142 | } | ||
1143 | json_object_object_add(object, "scroll_factor", | ||
1144 | json_object_new_double(scroll_factor)); | ||
1145 | } | ||
1146 | |||
1147 | #if WLR_HAS_LIBINPUT_BACKEND | ||
999 | if (wlr_input_device_is_libinput(device->wlr_device)) { | 1148 | if (wlr_input_device_is_libinput(device->wlr_device)) { |
1000 | struct libinput_device *libinput_dev; | 1149 | struct libinput_device *libinput_dev; |
1001 | libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); | 1150 | libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); |
1002 | json_object_object_add(object, "libinput", | 1151 | json_object_object_add(object, "libinput", |
1003 | 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))); | ||
1004 | } | 1157 | } |
1158 | #endif | ||
1005 | 1159 | ||
1006 | return object; | 1160 | return object; |
1007 | } | 1161 | } |
@@ -1109,7 +1263,9 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { | |||
1109 | json_object_object_add(json, "verbose", | 1263 | json_object_object_add(json, "verbose", |
1110 | json_object_new_boolean(bar->verbose)); | 1264 | json_object_new_boolean(bar->verbose)); |
1111 | json_object_object_add(json, "pango_markup", | 1265 | json_object_object_add(json, "pango_markup", |
1112 | json_object_new_boolean(bar->pango_markup)); | 1266 | json_object_new_boolean(bar->pango_markup == PANGO_MARKUP_DEFAULT |
1267 | ? config->pango_markup | ||
1268 | : bar->pango_markup)); | ||
1113 | 1269 | ||
1114 | json_object *colors = json_object_new_object(); | 1270 | json_object *colors = json_object_new_object(); |
1115 | json_object_object_add(colors, "background", | 1271 | json_object_object_add(colors, "background", |
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index aad9a7b5..7f353c0e 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -1,5 +1,4 @@ | |||
1 | // See https://i3wm.org/docs/ipc.html for protocol information | 1 | // See https://i3wm.org/docs/ipc.html for protocol information |
2 | #define _POSIX_C_SOURCE 200112L | ||
3 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
4 | #include <assert.h> | 3 | #include <assert.h> |
5 | #include <errno.h> | 4 | #include <errno.h> |
@@ -56,7 +55,6 @@ struct ipc_client { | |||
56 | enum ipc_command_type pending_type; | 55 | enum ipc_command_type pending_type; |
57 | }; | 56 | }; |
58 | 57 | ||
59 | struct sockaddr_un *ipc_user_sockaddr(void); | ||
60 | int ipc_handle_connection(int fd, uint32_t mask, void *data); | 58 | int ipc_handle_connection(int fd, uint32_t mask, void *data); |
61 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); | 59 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); |
62 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); | 60 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); |
@@ -150,7 +148,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) { | |||
150 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { | 148 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { |
151 | (void) fd; | 149 | (void) fd; |
152 | struct sway_server *server = data; | 150 | struct sway_server *server = data; |
153 | sway_log(SWAY_DEBUG, "Event on IPC listening socket"); | ||
154 | assert(mask == WL_EVENT_READABLE); | 151 | assert(mask == WL_EVENT_READABLE); |
155 | 152 | ||
156 | int client_fd = accept(ipc_socket, NULL, NULL); | 153 | int client_fd = accept(ipc_socket, NULL, NULL); |
@@ -211,13 +208,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
211 | } | 208 | } |
212 | 209 | ||
213 | if (mask & WL_EVENT_HANGUP) { | 210 | if (mask & WL_EVENT_HANGUP) { |
214 | sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); | ||
215 | ipc_client_disconnect(client); | 211 | ipc_client_disconnect(client); |
216 | return 0; | 212 | return 0; |
217 | } | 213 | } |
218 | 214 | ||
219 | sway_log(SWAY_DEBUG, "Client %d readable", client->fd); | ||
220 | |||
221 | int read_available; | 215 | int read_available; |
222 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { | 216 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { |
223 | sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); | 217 | sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); |
@@ -513,6 +507,20 @@ void ipc_event_input(const char *change, struct sway_input_device *device) { | |||
513 | json_object_put(json); | 507 | json_object_put(json); |
514 | } | 508 | } |
515 | 509 | ||
510 | void ipc_event_output(void) { | ||
511 | if (!ipc_has_event_listeners(IPC_EVENT_OUTPUT)) { | ||
512 | return; | ||
513 | } | ||
514 | sway_log(SWAY_DEBUG, "Sending output event"); | ||
515 | |||
516 | json_object *json = json_object_new_object(); | ||
517 | json_object_object_add(json, "change", json_object_new_string("unspecified")); | ||
518 | |||
519 | const char *json_string = json_object_to_json_string(json); | ||
520 | ipc_send_event(json_string, IPC_EVENT_OUTPUT); | ||
521 | json_object_put(json); | ||
522 | } | ||
523 | |||
516 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | 524 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { |
517 | struct ipc_client *client = data; | 525 | struct ipc_client *client = data; |
518 | 526 | ||
@@ -523,7 +531,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
523 | } | 531 | } |
524 | 532 | ||
525 | if (mask & WL_EVENT_HANGUP) { | 533 | if (mask & WL_EVENT_HANGUP) { |
526 | sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); | ||
527 | ipc_client_disconnect(client); | 534 | ipc_client_disconnect(client); |
528 | return 0; | 535 | return 0; |
529 | } | 536 | } |
@@ -532,8 +539,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
532 | return 0; | 539 | return 0; |
533 | } | 540 | } |
534 | 541 | ||
535 | sway_log(SWAY_DEBUG, "Client %d writable", client->fd); | ||
536 | |||
537 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); | 542 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); |
538 | 543 | ||
539 | if (written == -1 && errno == EAGAIN) { | 544 | if (written == -1 && errno == EAGAIN) { |
@@ -687,11 +692,17 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
687 | } | 692 | } |
688 | struct sway_output *output; | 693 | struct sway_output *output; |
689 | wl_list_for_each(output, &root->all_outputs, link) { | 694 | wl_list_for_each(output, &root->all_outputs, link) { |
690 | if (!output->enabled && output != root->noop_output) { | 695 | if (!output->enabled && output != root->fallback_output) { |
691 | json_object_array_add(outputs, | 696 | json_object_array_add(outputs, |
692 | ipc_json_describe_disabled_output(output)); | 697 | ipc_json_describe_disabled_output(output)); |
693 | } | 698 | } |
694 | } | 699 | } |
700 | |||
701 | for (int i = 0; i < root->non_desktop_outputs->length; i++) { | ||
702 | struct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i]; | ||
703 | json_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output)); | ||
704 | } | ||
705 | |||
695 | const char *json_string = json_object_to_json_string(outputs); | 706 | const char *json_string = json_object_to_json_string(outputs); |
696 | ipc_send_reply(client, payload_type, json_string, | 707 | ipc_send_reply(client, payload_type, json_string, |
697 | (uint32_t)strlen(json_string)); | 708 | (uint32_t)strlen(json_string)); |
@@ -727,6 +738,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
727 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); | 738 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); |
728 | if (strcmp(event_type, "workspace") == 0) { | 739 | if (strcmp(event_type, "workspace") == 0) { |
729 | client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); | 740 | client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); |
741 | } else if (strcmp(event_type, "output") == 0) { | ||
742 | client->subscribed_events |= event_mask(IPC_EVENT_OUTPUT); | ||
730 | } else if (strcmp(event_type, "barconfig_update") == 0) { | 743 | } else if (strcmp(event_type, "barconfig_update") == 0) { |
731 | client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); | 744 | client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); |
732 | } else if (strcmp(event_type, "bar_state_update") == 0) { | 745 | } else if (strcmp(event_type, "bar_state_update") == 0) { |
@@ -911,7 +924,6 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
911 | 924 | ||
912 | exit_cleanup: | 925 | exit_cleanup: |
913 | free(buf); | 926 | free(buf); |
914 | return; | ||
915 | } | 927 | } |
916 | 928 | ||
917 | bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, | 929 | bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, |
@@ -955,7 +967,5 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ | |||
955 | ipc_client_handle_writable, client); | 967 | ipc_client_handle_writable, client); |
956 | } | 968 | } |
957 | 969 | ||
958 | sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s", | ||
959 | payload_type, client->fd, payload); | ||
960 | return true; | 970 | return true; |
961 | } | 971 | } |
diff --git a/sway/lock.c b/sway/lock.c new file mode 100644 index 00000000..289e8ca4 --- /dev/null +++ b/sway/lock.c | |||
@@ -0,0 +1,354 @@ | |||
1 | #include <assert.h> | ||
2 | #include <wlr/types/wlr_scene.h> | ||
3 | #include <wlr/types/wlr_session_lock_v1.h> | ||
4 | #include "log.h" | ||
5 | #include "sway/input/cursor.h" | ||
6 | #include "sway/input/keyboard.h" | ||
7 | #include "sway/input/seat.h" | ||
8 | #include "sway/layers.h" | ||
9 | #include "sway/output.h" | ||
10 | #include "sway/server.h" | ||
11 | |||
12 | struct sway_session_lock_output { | ||
13 | struct wlr_scene_tree *tree; | ||
14 | struct wlr_scene_rect *background; | ||
15 | struct sway_session_lock *lock; | ||
16 | |||
17 | struct sway_output *output; | ||
18 | |||
19 | struct wl_list link; // sway_session_lock::outputs | ||
20 | |||
21 | struct wl_listener destroy; | ||
22 | struct wl_listener commit; | ||
23 | |||
24 | struct wlr_session_lock_surface_v1 *surface; | ||
25 | |||
26 | // invalid if surface is NULL | ||
27 | struct wl_listener surface_destroy; | ||
28 | struct wl_listener surface_map; | ||
29 | }; | ||
30 | |||
31 | static void focus_surface(struct sway_session_lock *lock, | ||
32 | struct wlr_surface *focused) { | ||
33 | lock->focused = focused; | ||
34 | |||
35 | struct sway_seat *seat; | ||
36 | wl_list_for_each(seat, &server.input->seats, link) { | ||
37 | seat_set_focus_surface(seat, focused, false); | ||
38 | } | ||
39 | } | ||
40 | |||
41 | static void refocus_output(struct sway_session_lock_output *output) { | ||
42 | // Move the seat focus to another surface if one is available | ||
43 | if (output->lock->focused == output->surface->surface) { | ||
44 | struct wlr_surface *next_focus = NULL; | ||
45 | |||
46 | struct sway_session_lock_output *candidate; | ||
47 | wl_list_for_each(candidate, &output->lock->outputs, link) { | ||
48 | if (candidate == output || !candidate->surface) { | ||
49 | continue; | ||
50 | } | ||
51 | |||
52 | if (candidate->surface->surface->mapped) { | ||
53 | next_focus = candidate->surface->surface; | ||
54 | break; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | focus_surface(output->lock, next_focus); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | static void handle_surface_map(struct wl_listener *listener, void *data) { | ||
63 | struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map); | ||
64 | if (surf->lock->focused == NULL) { | ||
65 | focus_surface(surf->lock, surf->surface->surface); | ||
66 | } | ||
67 | cursor_rebase_all(); | ||
68 | } | ||
69 | |||
70 | static void handle_surface_destroy(struct wl_listener *listener, void *data) { | ||
71 | struct sway_session_lock_output *output = | ||
72 | wl_container_of(listener, output, surface_destroy); | ||
73 | refocus_output(output); | ||
74 | |||
75 | sway_assert(output->surface, "Trying to destroy a surface that the lock doesn't think exists"); | ||
76 | output->surface = NULL; | ||
77 | wl_list_remove(&output->surface_destroy.link); | ||
78 | wl_list_remove(&output->surface_map.link); | ||
79 | } | ||
80 | |||
81 | static void lock_output_reconfigure(struct sway_session_lock_output *output) { | ||
82 | int width = output->output->width; | ||
83 | int height = output->output->height; | ||
84 | |||
85 | wlr_scene_rect_set_size(output->background, width, height); | ||
86 | |||
87 | if (output->surface) { | ||
88 | wlr_session_lock_surface_v1_configure(output->surface, width, height); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | static void handle_new_surface(struct wl_listener *listener, void *data) { | ||
93 | struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface); | ||
94 | struct wlr_session_lock_surface_v1 *lock_surface = data; | ||
95 | struct sway_output *output = lock_surface->output->data; | ||
96 | |||
97 | sway_log(SWAY_DEBUG, "new lock layer surface"); | ||
98 | |||
99 | struct sway_session_lock_output *current_lock_output, *lock_output = NULL; | ||
100 | wl_list_for_each(current_lock_output, &lock->outputs, link) { | ||
101 | if (current_lock_output->output == output) { | ||
102 | lock_output = current_lock_output; | ||
103 | break; | ||
104 | } | ||
105 | } | ||
106 | sway_assert(lock_output, "Couldn't find output to lock"); | ||
107 | sway_assert(!lock_output->surface, "Tried to reassign a surface to an existing output"); | ||
108 | |||
109 | lock_output->surface = lock_surface; | ||
110 | |||
111 | wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface); | ||
112 | |||
113 | lock_output->surface_destroy.notify = handle_surface_destroy; | ||
114 | wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy); | ||
115 | lock_output->surface_map.notify = handle_surface_map; | ||
116 | wl_signal_add(&lock_surface->surface->events.map, &lock_output->surface_map); | ||
117 | |||
118 | lock_output_reconfigure(lock_output); | ||
119 | } | ||
120 | |||
121 | static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) { | ||
122 | if (output->surface) { | ||
123 | refocus_output(output); | ||
124 | wl_list_remove(&output->surface_destroy.link); | ||
125 | wl_list_remove(&output->surface_map.link); | ||
126 | } | ||
127 | |||
128 | wl_list_remove(&output->commit.link); | ||
129 | wl_list_remove(&output->destroy.link); | ||
130 | wl_list_remove(&output->link); | ||
131 | |||
132 | free(output); | ||
133 | } | ||
134 | |||
135 | static void lock_node_handle_destroy(struct wl_listener *listener, void *data) { | ||
136 | struct sway_session_lock_output *output = | ||
137 | wl_container_of(listener, output, destroy); | ||
138 | sway_session_lock_output_destroy(output); | ||
139 | } | ||
140 | |||
141 | static void lock_output_handle_commit(struct wl_listener *listener, void *data) { | ||
142 | struct wlr_output_event_commit *event = data; | ||
143 | struct sway_session_lock_output *output = | ||
144 | wl_container_of(listener, output, commit); | ||
145 | if (event->state->committed & ( | ||
146 | WLR_OUTPUT_STATE_MODE | | ||
147 | WLR_OUTPUT_STATE_SCALE | | ||
148 | WLR_OUTPUT_STATE_TRANSFORM)) { | ||
149 | lock_output_reconfigure(output); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | static struct sway_session_lock_output *session_lock_output_create( | ||
154 | struct sway_session_lock *lock, struct sway_output *output) { | ||
155 | struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output)); | ||
156 | if (!lock_output) { | ||
157 | sway_log(SWAY_ERROR, "failed to allocate a session lock output"); | ||
158 | return NULL; | ||
159 | } | ||
160 | |||
161 | struct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock); | ||
162 | if (!tree) { | ||
163 | sway_log(SWAY_ERROR, "failed to allocate a session lock output scene tree"); | ||
164 | free(lock_output); | ||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){ | ||
169 | lock->abandoned ? 1.f : 0.f, | ||
170 | 0.f, | ||
171 | 0.f, | ||
172 | 1.f, | ||
173 | }); | ||
174 | if (!background) { | ||
175 | sway_log(SWAY_ERROR, "failed to allocate a session lock output scene background"); | ||
176 | wlr_scene_node_destroy(&tree->node); | ||
177 | free(lock_output); | ||
178 | return NULL; | ||
179 | } | ||
180 | |||
181 | lock_output->output = output; | ||
182 | lock_output->tree = tree; | ||
183 | lock_output->background = background; | ||
184 | lock_output->lock = lock; | ||
185 | |||
186 | lock_output->destroy.notify = lock_node_handle_destroy; | ||
187 | wl_signal_add(&tree->node.events.destroy, &lock_output->destroy); | ||
188 | |||
189 | lock_output->commit.notify = lock_output_handle_commit; | ||
190 | wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit); | ||
191 | |||
192 | lock_output_reconfigure(lock_output); | ||
193 | |||
194 | wl_list_insert(&lock->outputs, &lock_output->link); | ||
195 | |||
196 | return lock_output; | ||
197 | } | ||
198 | |||
199 | static void sway_session_lock_destroy(struct sway_session_lock* lock) { | ||
200 | struct sway_session_lock_output *lock_output, *tmp_lock_output; | ||
201 | wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) { | ||
202 | // destroying the node will also destroy the whole lock output | ||
203 | wlr_scene_node_destroy(&lock_output->tree->node); | ||
204 | } | ||
205 | |||
206 | if (server.session_lock.lock == lock) { | ||
207 | server.session_lock.lock = NULL; | ||
208 | } | ||
209 | |||
210 | if (!lock->abandoned) { | ||
211 | wl_list_remove(&lock->destroy.link); | ||
212 | wl_list_remove(&lock->unlock.link); | ||
213 | wl_list_remove(&lock->new_surface.link); | ||
214 | } | ||
215 | |||
216 | free(lock); | ||
217 | } | ||
218 | |||
219 | static void handle_unlock(struct wl_listener *listener, void *data) { | ||
220 | struct sway_session_lock *lock = wl_container_of(listener, lock, unlock); | ||
221 | sway_log(SWAY_DEBUG, "session unlocked"); | ||
222 | |||
223 | sway_session_lock_destroy(lock); | ||
224 | |||
225 | struct sway_seat *seat; | ||
226 | wl_list_for_each(seat, &server.input->seats, link) { | ||
227 | // copied from seat_set_focus_layer -- deduplicate? | ||
228 | struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); | ||
229 | if (previous) { | ||
230 | // Hack to get seat to re-focus the return value of get_focus | ||
231 | seat_set_focus(seat, NULL); | ||
232 | seat_set_focus(seat, previous); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | // Triggers a refocus of the topmost surface layer if necessary | ||
237 | // TODO: Make layer surface focus per-output based on cursor position | ||
238 | for (int i = 0; i < root->outputs->length; ++i) { | ||
239 | struct sway_output *output = root->outputs->items[i]; | ||
240 | arrange_layers(output); | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static void handle_abandon(struct wl_listener *listener, void *data) { | ||
245 | struct sway_session_lock *lock = wl_container_of(listener, lock, destroy); | ||
246 | sway_log(SWAY_INFO, "session lock abandoned"); | ||
247 | |||
248 | struct sway_session_lock_output *lock_output; | ||
249 | wl_list_for_each(lock_output, &lock->outputs, link) { | ||
250 | wlr_scene_rect_set_color(lock_output->background, | ||
251 | (float[4]){ 1.f, 0.f, 0.f, 1.f }); | ||
252 | } | ||
253 | |||
254 | lock->abandoned = true; | ||
255 | wl_list_remove(&lock->destroy.link); | ||
256 | wl_list_remove(&lock->unlock.link); | ||
257 | wl_list_remove(&lock->new_surface.link); | ||
258 | } | ||
259 | |||
260 | static void handle_session_lock(struct wl_listener *listener, void *data) { | ||
261 | struct wlr_session_lock_v1 *lock = data; | ||
262 | struct wl_client *client = wl_resource_get_client(lock->resource); | ||
263 | |||
264 | if (server.session_lock.lock) { | ||
265 | if (server.session_lock.lock->abandoned) { | ||
266 | sway_log(SWAY_INFO, "Replacing abandoned lock"); | ||
267 | sway_session_lock_destroy(server.session_lock.lock); | ||
268 | } else { | ||
269 | sway_log(SWAY_ERROR, "Cannot lock an already locked session"); | ||
270 | wlr_session_lock_v1_destroy(lock); | ||
271 | return; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | struct sway_session_lock *sway_lock = calloc(1, sizeof(*sway_lock)); | ||
276 | if (!sway_lock) { | ||
277 | sway_log(SWAY_ERROR, "failed to allocate a session lock object"); | ||
278 | wlr_session_lock_v1_destroy(lock); | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | wl_list_init(&sway_lock->outputs); | ||
283 | |||
284 | sway_log(SWAY_DEBUG, "session locked"); | ||
285 | |||
286 | struct sway_seat *seat; | ||
287 | wl_list_for_each(seat, &server.input->seats, link) { | ||
288 | seat_unfocus_unless_client(seat, client); | ||
289 | } | ||
290 | |||
291 | struct sway_output *output; | ||
292 | wl_list_for_each(output, &root->all_outputs, link) { | ||
293 | sway_session_lock_add_output(sway_lock, output); | ||
294 | } | ||
295 | |||
296 | sway_lock->new_surface.notify = handle_new_surface; | ||
297 | wl_signal_add(&lock->events.new_surface, &sway_lock->new_surface); | ||
298 | sway_lock->unlock.notify = handle_unlock; | ||
299 | wl_signal_add(&lock->events.unlock, &sway_lock->unlock); | ||
300 | sway_lock->destroy.notify = handle_abandon; | ||
301 | wl_signal_add(&lock->events.destroy, &sway_lock->destroy); | ||
302 | |||
303 | wlr_session_lock_v1_send_locked(lock); | ||
304 | server.session_lock.lock = sway_lock; | ||
305 | } | ||
306 | |||
307 | static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { | ||
308 | // if the server shuts down while a lock is active, destroy the lock | ||
309 | if (server.session_lock.lock) { | ||
310 | sway_session_lock_destroy(server.session_lock.lock); | ||
311 | } | ||
312 | |||
313 | wl_list_remove(&server.session_lock.new_lock.link); | ||
314 | wl_list_remove(&server.session_lock.manager_destroy.link); | ||
315 | |||
316 | server.session_lock.manager = NULL; | ||
317 | } | ||
318 | |||
319 | void sway_session_lock_add_output(struct sway_session_lock *lock, | ||
320 | struct sway_output *output) { | ||
321 | struct sway_session_lock_output *lock_output = | ||
322 | session_lock_output_create(lock, output); | ||
323 | |||
324 | // if we run out of memory while trying to lock the screen, the best we | ||
325 | // can do is kill the sway process. Security conscious users will have | ||
326 | // the sway session fall back to a login shell. | ||
327 | if (!lock_output) { | ||
328 | sway_log(SWAY_ERROR, "aborting: failed to allocate a lock output"); | ||
329 | abort(); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | bool sway_session_lock_has_surface(struct sway_session_lock *lock, | ||
334 | struct wlr_surface *surface) { | ||
335 | struct sway_session_lock_output *lock_output; | ||
336 | wl_list_for_each(lock_output, &lock->outputs, link) { | ||
337 | if (lock_output->surface && lock_output->surface->surface == surface) { | ||
338 | return true; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | return false; | ||
343 | } | ||
344 | |||
345 | void sway_session_lock_init(void) { | ||
346 | server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); | ||
347 | |||
348 | server.session_lock.new_lock.notify = handle_session_lock; | ||
349 | server.session_lock.manager_destroy.notify = handle_session_lock_destroy; | ||
350 | wl_signal_add(&server.session_lock.manager->events.new_lock, | ||
351 | &server.session_lock.new_lock); | ||
352 | wl_signal_add(&server.session_lock.manager->events.destroy, | ||
353 | &server.session_lock.manager_destroy); | ||
354 | } | ||
diff --git a/sway/main.c b/sway/main.c index 0c219fb3..1c4939aa 100644 --- a/sway/main.c +++ b/sway/main.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <getopt.h> | 1 | #include <getopt.h> |
3 | #include <pango/pangocairo.h> | 2 | #include <pango/pangocairo.h> |
4 | #include <signal.h> | 3 | #include <signal.h> |
@@ -6,12 +5,14 @@ | |||
6 | #include <stdio.h> | 5 | #include <stdio.h> |
7 | #include <stdlib.h> | 6 | #include <stdlib.h> |
8 | #include <string.h> | 7 | #include <string.h> |
8 | #include <sys/resource.h> | ||
9 | #include <sys/stat.h> | 9 | #include <sys/stat.h> |
10 | #include <sys/types.h> | 10 | #include <sys/types.h> |
11 | #include <sys/wait.h> | 11 | #include <sys/wait.h> |
12 | #include <sys/un.h> | 12 | #include <sys/un.h> |
13 | #include <unistd.h> | 13 | #include <unistd.h> |
14 | #include <wlr/util/log.h> | 14 | #include <wlr/util/log.h> |
15 | #include <wlr/version.h> | ||
15 | #include "sway/commands.h" | 16 | #include "sway/commands.h" |
16 | #include "sway/config.h" | 17 | #include "sway/config.h" |
17 | #include "sway/server.h" | 18 | #include "sway/server.h" |
@@ -26,6 +27,7 @@ | |||
26 | 27 | ||
27 | static bool terminate_request = false; | 28 | static bool terminate_request = false; |
28 | static int exit_value = 0; | 29 | static int exit_value = 0; |
30 | static struct rlimit original_nofile_rlimit = {0}; | ||
29 | struct sway_server server = {0}; | 31 | struct sway_server server = {0}; |
30 | struct sway_debug debug = {0}; | 32 | struct sway_debug debug = {0}; |
31 | 33 | ||
@@ -46,81 +48,6 @@ void sig_handler(int signal) { | |||
46 | sway_terminate(EXIT_SUCCESS); | 48 | sway_terminate(EXIT_SUCCESS); |
47 | } | 49 | } |
48 | 50 | ||
49 | void detect_raspi(void) { | ||
50 | bool raspi = false; | ||
51 | FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); | ||
52 | if (!f) { | ||
53 | return; | ||
54 | } | ||
55 | char *line = NULL; | ||
56 | size_t line_size = 0; | ||
57 | while (getline(&line, &line_size, f) != -1) { | ||
58 | if (strstr(line, "Raspberry Pi")) { | ||
59 | raspi = true; | ||
60 | break; | ||
61 | } | ||
62 | } | ||
63 | fclose(f); | ||
64 | FILE *g = fopen("/proc/modules", "r"); | ||
65 | if (!g) { | ||
66 | free(line); | ||
67 | return; | ||
68 | } | ||
69 | bool vc4 = false; | ||
70 | while (getline(&line, &line_size, g) != -1) { | ||
71 | if (strstr(line, "vc4")) { | ||
72 | vc4 = true; | ||
73 | break; | ||
74 | } | ||
75 | } | ||
76 | free(line); | ||
77 | fclose(g); | ||
78 | if (!vc4 && raspi) { | ||
79 | fprintf(stderr, "\x1B[1;31mWarning: You have a " | ||
80 | "Raspberry Pi, but the vc4 Module is " | ||
81 | "not loaded! Set 'dtoverlay=vc4-kms-v3d'" | ||
82 | "in /boot/config.txt and reboot.\x1B[0m\n"); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | void detect_proprietary(int allow_unsupported_gpu) { | ||
87 | FILE *f = fopen("/proc/modules", "r"); | ||
88 | if (!f) { | ||
89 | return; | ||
90 | } | ||
91 | char *line = NULL; | ||
92 | size_t line_size = 0; | ||
93 | while (getline(&line, &line_size, f) != -1) { | ||
94 | if (strncmp(line, "nvidia ", 7) == 0) { | ||
95 | if (allow_unsupported_gpu) { | ||
96 | sway_log(SWAY_ERROR, | ||
97 | "!!! Proprietary Nvidia drivers are in use !!!"); | ||
98 | } else { | ||
99 | sway_log(SWAY_ERROR, | ||
100 | "Proprietary Nvidia drivers are NOT supported. " | ||
101 | "Use Nouveau. To launch sway anyway, launch with " | ||
102 | "--my-next-gpu-wont-be-nvidia and DO NOT report issues."); | ||
103 | exit(EXIT_FAILURE); | ||
104 | } | ||
105 | break; | ||
106 | } | ||
107 | if (strstr(line, "fglrx")) { | ||
108 | if (allow_unsupported_gpu) { | ||
109 | sway_log(SWAY_ERROR, | ||
110 | "!!! Proprietary AMD drivers are in use !!!"); | ||
111 | } else { | ||
112 | sway_log(SWAY_ERROR, "Proprietary AMD drivers do NOT support " | ||
113 | "Wayland. Use radeon. To try anyway, launch sway with " | ||
114 | "--unsupported-gpu and DO NOT report issues."); | ||
115 | exit(EXIT_FAILURE); | ||
116 | } | ||
117 | break; | ||
118 | } | ||
119 | } | ||
120 | free(line); | ||
121 | fclose(f); | ||
122 | } | ||
123 | |||
124 | void run_as_ipc_client(char *command, char *socket_path) { | 51 | void run_as_ipc_client(char *command, char *socket_path) { |
125 | int socketfd = ipc_open_socket(socket_path); | 52 | int socketfd = ipc_open_socket(socket_path); |
126 | uint32_t len = strlen(command); | 53 | uint32_t len = strlen(command); |
@@ -184,33 +111,49 @@ static void log_kernel(void) { | |||
184 | pclose(f); | 111 | pclose(f); |
185 | } | 112 | } |
186 | 113 | ||
187 | 114 | static bool detect_suid(void) { | |
188 | static bool drop_permissions(void) { | 115 | if (geteuid() != 0 && getegid() != 0) { |
189 | if (getuid() != geteuid() || getgid() != getegid()) { | 116 | return false; |
190 | // Set the gid and uid in the correct order. | ||
191 | if (setgid(getgid()) != 0) { | ||
192 | sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start"); | ||
193 | return false; | ||
194 | } | ||
195 | if (setuid(getuid()) != 0) { | ||
196 | sway_log(SWAY_ERROR, "Unable to drop root user, refusing to start"); | ||
197 | return false; | ||
198 | } | ||
199 | } | 117 | } |
200 | if (setgid(0) != -1 || setuid(0) != -1) { | 118 | |
201 | sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to " | 119 | if (getuid() == geteuid() && getgid() == getegid()) { |
202 | "restore it after setuid), refusing to start"); | ||
203 | return false; | 120 | return false; |
204 | } | 121 | } |
122 | |||
123 | sway_log(SWAY_ERROR, "SUID operation is no longer supported, refusing to start. " | ||
124 | "This check will be removed in a future release."); | ||
205 | return true; | 125 | return true; |
206 | } | 126 | } |
207 | 127 | ||
128 | static void increase_nofile_limit(void) { | ||
129 | if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { | ||
130 | sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " | ||
131 | "getrlimit(NOFILE) failed"); | ||
132 | return; | ||
133 | } | ||
134 | |||
135 | struct rlimit new_rlimit = original_nofile_rlimit; | ||
136 | new_rlimit.rlim_cur = new_rlimit.rlim_max; | ||
137 | if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) { | ||
138 | sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " | ||
139 | "setrlimit(NOFILE) failed"); | ||
140 | sway_log(SWAY_INFO, "Running with %d max open files", | ||
141 | (int)original_nofile_rlimit.rlim_cur); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | void restore_nofile_limit(void) { | ||
146 | if (original_nofile_rlimit.rlim_cur == 0) { | ||
147 | return; | ||
148 | } | ||
149 | if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { | ||
150 | sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: " | ||
151 | "setrlimit(NOFILE) failed"); | ||
152 | } | ||
153 | } | ||
154 | |||
208 | void enable_debug_flag(const char *flag) { | 155 | void enable_debug_flag(const char *flag) { |
209 | if (strcmp(flag, "damage=highlight") == 0) { | 156 | if (strcmp(flag, "noatomic") == 0) { |
210 | debug.damage = DAMAGE_HIGHLIGHT; | ||
211 | } else if (strcmp(flag, "damage=rerender") == 0) { | ||
212 | debug.damage = DAMAGE_RERENDER; | ||
213 | } else if (strcmp(flag, "noatomic") == 0) { | ||
214 | debug.noatomic = true; | 157 | debug.noatomic = true; |
215 | } else if (strcmp(flag, "txn-wait") == 0) { | 158 | } else if (strcmp(flag, "txn-wait") == 0) { |
216 | debug.txn_wait = true; | 159 | debug.txn_wait = true; |
@@ -218,6 +161,8 @@ void enable_debug_flag(const char *flag) { | |||
218 | debug.txn_timings = true; | 161 | debug.txn_timings = true; |
219 | } else if (strncmp(flag, "txn-timeout=", 12) == 0) { | 162 | } else if (strncmp(flag, "txn-timeout=", 12) == 0) { |
220 | server.txn_timeout_ms = atoi(&flag[12]); | 163 | server.txn_timeout_ms = atoi(&flag[12]); |
164 | } else if (strcmp(flag, "legacy-wl-drm") == 0) { | ||
165 | debug.legacy_wl_drm = true; | ||
221 | } else { | 166 | } else { |
222 | sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); | 167 | sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); |
223 | } | 168 | } |
@@ -242,36 +187,35 @@ static void handle_wlr_log(enum wlr_log_importance importance, | |||
242 | _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); | 187 | _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); |
243 | } | 188 | } |
244 | 189 | ||
190 | static const struct option long_options[] = { | ||
191 | {"help", no_argument, NULL, 'h'}, | ||
192 | {"config", required_argument, NULL, 'c'}, | ||
193 | {"validate", no_argument, NULL, 'C'}, | ||
194 | {"debug", no_argument, NULL, 'd'}, | ||
195 | {"version", no_argument, NULL, 'v'}, | ||
196 | {"verbose", no_argument, NULL, 'V'}, | ||
197 | {"get-socketpath", no_argument, NULL, 'p'}, | ||
198 | {"unsupported-gpu", no_argument, NULL, 'u'}, | ||
199 | {0, 0, 0, 0} | ||
200 | }; | ||
201 | |||
202 | static const char usage[] = | ||
203 | "Usage: sway [options] [command]\n" | ||
204 | "\n" | ||
205 | " -h, --help Show help message and quit.\n" | ||
206 | " -c, --config <config> Specify a config file.\n" | ||
207 | " -C, --validate Check the validity of the config file, then exit.\n" | ||
208 | " -d, --debug Enables full logging, including debug information.\n" | ||
209 | " -v, --version Show the version number and quit.\n" | ||
210 | " -V, --verbose Enables more verbose logging.\n" | ||
211 | " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" | ||
212 | "\n"; | ||
213 | |||
245 | int main(int argc, char **argv) { | 214 | int main(int argc, char **argv) { |
246 | static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; | 215 | static bool verbose = false, debug = false, validate = false; |
247 | |||
248 | static struct option long_options[] = { | ||
249 | {"help", no_argument, NULL, 'h'}, | ||
250 | {"config", required_argument, NULL, 'c'}, | ||
251 | {"validate", no_argument, NULL, 'C'}, | ||
252 | {"debug", no_argument, NULL, 'd'}, | ||
253 | {"version", no_argument, NULL, 'v'}, | ||
254 | {"verbose", no_argument, NULL, 'V'}, | ||
255 | {"get-socketpath", no_argument, NULL, 'p'}, | ||
256 | {"unsupported-gpu", no_argument, NULL, 'u'}, | ||
257 | {"my-next-gpu-wont-be-nvidia", no_argument, NULL, 'u'}, | ||
258 | {0, 0, 0, 0} | ||
259 | }; | ||
260 | 216 | ||
261 | char *config_path = NULL; | 217 | char *config_path = NULL; |
262 | 218 | ||
263 | const char* usage = | ||
264 | "Usage: sway [options] [command]\n" | ||
265 | "\n" | ||
266 | " -h, --help Show help message and quit.\n" | ||
267 | " -c, --config <config> Specify a config file.\n" | ||
268 | " -C, --validate Check the validity of the config file, then exit.\n" | ||
269 | " -d, --debug Enables full logging, including debug information.\n" | ||
270 | " -v, --version Show the version number and quit.\n" | ||
271 | " -V, --verbose Enables more verbose logging.\n" | ||
272 | " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" | ||
273 | "\n"; | ||
274 | |||
275 | int c; | 219 | int c; |
276 | while (1) { | 220 | while (1) { |
277 | int option_index = 0; | 221 | int option_index = 0; |
@@ -289,25 +233,25 @@ int main(int argc, char **argv) { | |||
289 | config_path = strdup(optarg); | 233 | config_path = strdup(optarg); |
290 | break; | 234 | break; |
291 | case 'C': // validate | 235 | case 'C': // validate |
292 | validate = 1; | 236 | validate = true; |
293 | break; | 237 | break; |
294 | case 'd': // debug | 238 | case 'd': // debug |
295 | debug = 1; | 239 | debug = true; |
296 | break; | 240 | break; |
297 | case 'D': // extended debug options | 241 | case 'D': // extended debug options |
298 | enable_debug_flag(optarg); | 242 | enable_debug_flag(optarg); |
299 | break; | 243 | break; |
300 | case 'u': | 244 | case 'u': |
301 | allow_unsupported_gpu = 1; | 245 | allow_unsupported_gpu = true; |
302 | break; | 246 | break; |
303 | case 'v': // version | 247 | case 'v': // version |
304 | printf("sway version " SWAY_VERSION "\n"); | 248 | printf("sway version " SWAY_VERSION "\n"); |
305 | exit(EXIT_SUCCESS); | 249 | exit(EXIT_SUCCESS); |
306 | break; | 250 | break; |
307 | case 'V': // verbose | 251 | case 'V': // verbose |
308 | verbose = 1; | 252 | verbose = true; |
309 | break; | 253 | break; |
310 | case 'p': ; // --get-socketpath | 254 | case 'p': // --get-socketpath |
311 | if (getenv("SWAYSOCK")) { | 255 | if (getenv("SWAYSOCK")) { |
312 | printf("%s\n", getenv("SWAYSOCK")); | 256 | printf("%s\n", getenv("SWAYSOCK")); |
313 | exit(EXIT_SUCCESS); | 257 | exit(EXIT_SUCCESS); |
@@ -322,6 +266,11 @@ int main(int argc, char **argv) { | |||
322 | } | 266 | } |
323 | } | 267 | } |
324 | 268 | ||
269 | // SUID operation is deprecated, so block it for now. | ||
270 | if (detect_suid()) { | ||
271 | exit(EXIT_FAILURE); | ||
272 | } | ||
273 | |||
325 | // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the | 274 | // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the |
326 | // clear error message (when not running as an IPC client). | 275 | // clear error message (when not running as an IPC client). |
327 | if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { | 276 | if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { |
@@ -344,11 +293,10 @@ int main(int argc, char **argv) { | |||
344 | } | 293 | } |
345 | 294 | ||
346 | sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); | 295 | sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); |
296 | sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR); | ||
347 | log_kernel(); | 297 | log_kernel(); |
348 | log_distro(); | 298 | log_distro(); |
349 | log_env(); | 299 | log_env(); |
350 | detect_proprietary(allow_unsupported_gpu); | ||
351 | detect_raspi(); | ||
352 | 300 | ||
353 | if (optind < argc) { // Behave as IPC client | 301 | if (optind < argc) { // Behave as IPC client |
354 | if (optind != 1) { | 302 | if (optind != 1) { |
@@ -361,9 +309,6 @@ int main(int argc, char **argv) { | |||
361 | "`sway -d 2>sway.log`."); | 309 | "`sway -d 2>sway.log`."); |
362 | exit(EXIT_FAILURE); | 310 | exit(EXIT_FAILURE); |
363 | } | 311 | } |
364 | if (!drop_permissions()) { | ||
365 | exit(EXIT_FAILURE); | ||
366 | } | ||
367 | char *socket_path = getenv("SWAYSOCK"); | 312 | char *socket_path = getenv("SWAYSOCK"); |
368 | if (!socket_path) { | 313 | if (!socket_path) { |
369 | sway_log(SWAY_ERROR, "Unable to retrieve socket path"); | 314 | sway_log(SWAY_ERROR, "Unable to retrieve socket path"); |
@@ -375,14 +320,7 @@ int main(int argc, char **argv) { | |||
375 | return 0; | 320 | return 0; |
376 | } | 321 | } |
377 | 322 | ||
378 | if (!server_privileged_prepare(&server)) { | 323 | increase_nofile_limit(); |
379 | return 1; | ||
380 | } | ||
381 | |||
382 | if (!drop_permissions()) { | ||
383 | server_fini(&server); | ||
384 | exit(EXIT_FAILURE); | ||
385 | } | ||
386 | 324 | ||
387 | // handle SIGTERM signals | 325 | // handle SIGTERM signals |
388 | signal(SIGTERM, sig_handler); | 326 | signal(SIGTERM, sig_handler); |
@@ -393,12 +331,14 @@ int main(int argc, char **argv) { | |||
393 | 331 | ||
394 | sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); | 332 | sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); |
395 | 333 | ||
396 | root = root_create(); | ||
397 | |||
398 | if (!server_init(&server)) { | 334 | if (!server_init(&server)) { |
399 | return 1; | 335 | return 1; |
400 | } | 336 | } |
401 | 337 | ||
338 | if (server.linux_dmabuf_v1) { | ||
339 | wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1); | ||
340 | } | ||
341 | |||
402 | if (validate) { | 342 | if (validate) { |
403 | bool valid = load_main_config(config_path, false, true); | 343 | bool valid = load_main_config(config_path, false, true); |
404 | free(config_path); | 344 | free(config_path); |
@@ -413,6 +353,8 @@ int main(int argc, char **argv) { | |||
413 | goto shutdown; | 353 | goto shutdown; |
414 | } | 354 | } |
415 | 355 | ||
356 | set_rr_scheduling(); | ||
357 | |||
416 | if (!server_start(&server)) { | 358 | if (!server_start(&server)) { |
417 | sway_terminate(EXIT_FAILURE); | 359 | sway_terminate(EXIT_FAILURE); |
418 | goto shutdown; | 360 | goto shutdown; |
diff --git a/sway/meson.build b/sway/meson.build index 6e138101..d937e425 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -5,24 +5,26 @@ sway_sources = files( | |||
5 | 'decoration.c', | 5 | 'decoration.c', |
6 | 'ipc-json.c', | 6 | 'ipc-json.c', |
7 | 'ipc-server.c', | 7 | 'ipc-server.c', |
8 | 'lock.c', | ||
8 | 'main.c', | 9 | 'main.c', |
10 | 'realtime.c', | ||
11 | 'scene_descriptor.c', | ||
9 | 'server.c', | 12 | 'server.c', |
13 | 'sway_text_node.c', | ||
10 | 'swaynag.c', | 14 | 'swaynag.c', |
15 | 'xdg_activation_v1.c', | ||
11 | 'xdg_decoration.c', | 16 | 'xdg_decoration.c', |
12 | 17 | ||
13 | 'desktop/desktop.c', | ||
14 | 'desktop/idle_inhibit_v1.c', | 18 | 'desktop/idle_inhibit_v1.c', |
15 | 'desktop/layer_shell.c', | 19 | 'desktop/layer_shell.c', |
16 | 'desktop/output.c', | 20 | 'desktop/output.c', |
17 | 'desktop/render.c', | ||
18 | 'desktop/surface.c', | ||
19 | 'desktop/transaction.c', | 21 | 'desktop/transaction.c', |
20 | 'desktop/xdg_shell.c', | 22 | 'desktop/xdg_shell.c', |
23 | 'desktop/launcher.c', | ||
21 | 24 | ||
22 | 'input/input-manager.c', | 25 | 'input/input-manager.c', |
23 | 'input/cursor.c', | 26 | 'input/cursor.c', |
24 | 'input/keyboard.c', | 27 | 'input/keyboard.c', |
25 | 'input/libinput.c', | ||
26 | 'input/seat.c', | 28 | 'input/seat.c', |
27 | 'input/seatop_default.c', | 29 | 'input/seatop_default.c', |
28 | 'input/seatop_down.c', | 30 | 'input/seatop_down.c', |
@@ -64,6 +66,7 @@ sway_sources = files( | |||
64 | 'commands/force_focus_wrapping.c', | 66 | 'commands/force_focus_wrapping.c', |
65 | 'commands/fullscreen.c', | 67 | 'commands/fullscreen.c', |
66 | 'commands/gaps.c', | 68 | 'commands/gaps.c', |
69 | 'commands/gesture.c', | ||
67 | 'commands/hide_edge_borders.c', | 70 | 'commands/hide_edge_borders.c', |
68 | 'commands/inhibit_idle.c', | 71 | 'commands/inhibit_idle.c', |
69 | 'commands/kill.c', | 72 | 'commands/kill.c', |
@@ -82,6 +85,7 @@ sway_sources = files( | |||
82 | 'commands/nop.c', | 85 | 'commands/nop.c', |
83 | 'commands/output.c', | 86 | 'commands/output.c', |
84 | 'commands/popup_during_fullscreen.c', | 87 | 'commands/popup_during_fullscreen.c', |
88 | 'commands/primary_selection.c', | ||
85 | 'commands/reload.c', | 89 | 'commands/reload.c', |
86 | 'commands/rename.c', | 90 | 'commands/rename.c', |
87 | 'commands/resize.c', | 91 | 'commands/resize.c', |
@@ -153,6 +157,7 @@ sway_sources = files( | |||
153 | 'commands/input/drag.c', | 157 | 'commands/input/drag.c', |
154 | 'commands/input/drag_lock.c', | 158 | 'commands/input/drag_lock.c', |
155 | 'commands/input/dwt.c', | 159 | 'commands/input/dwt.c', |
160 | 'commands/input/dwtp.c', | ||
156 | 'commands/input/events.c', | 161 | 'commands/input/events.c', |
157 | 'commands/input/left_handed.c', | 162 | 'commands/input/left_handed.c', |
158 | 'commands/input/map_from_region.c', | 163 | 'commands/input/map_from_region.c', |
@@ -161,9 +166,11 @@ sway_sources = files( | |||
161 | 'commands/input/middle_emulation.c', | 166 | 'commands/input/middle_emulation.c', |
162 | 'commands/input/natural_scroll.c', | 167 | 'commands/input/natural_scroll.c', |
163 | 'commands/input/pointer_accel.c', | 168 | 'commands/input/pointer_accel.c', |
169 | 'commands/input/rotation_angle.c', | ||
164 | 'commands/input/repeat_delay.c', | 170 | 'commands/input/repeat_delay.c', |
165 | 'commands/input/repeat_rate.c', | 171 | 'commands/input/repeat_rate.c', |
166 | 'commands/input/scroll_button.c', | 172 | 'commands/input/scroll_button.c', |
173 | 'commands/input/scroll_button_lock.c', | ||
167 | 'commands/input/scroll_factor.c', | 174 | 'commands/input/scroll_factor.c', |
168 | 'commands/input/scroll_method.c', | 175 | 'commands/input/scroll_method.c', |
169 | 'commands/input/tap.c', | 176 | 'commands/input/tap.c', |
@@ -187,11 +194,14 @@ sway_sources = files( | |||
187 | 'commands/output/max_render_time.c', | 194 | 'commands/output/max_render_time.c', |
188 | 'commands/output/mode.c', | 195 | 'commands/output/mode.c', |
189 | 'commands/output/position.c', | 196 | 'commands/output/position.c', |
197 | 'commands/output/power.c', | ||
198 | 'commands/output/render_bit_depth.c', | ||
190 | 'commands/output/scale.c', | 199 | 'commands/output/scale.c', |
191 | 'commands/output/scale_filter.c', | 200 | 'commands/output/scale_filter.c', |
192 | 'commands/output/subpixel.c', | 201 | 'commands/output/subpixel.c', |
193 | 'commands/output/toggle.c', | 202 | 'commands/output/toggle.c', |
194 | 'commands/output/transform.c', | 203 | 'commands/output/transform.c', |
204 | 'commands/output/unplug.c', | ||
195 | 205 | ||
196 | 'tree/arrange.c', | 206 | 'tree/arrange.c', |
197 | 'tree/container.c', | 207 | 'tree/container.c', |
@@ -204,28 +214,34 @@ sway_sources = files( | |||
204 | 214 | ||
205 | sway_deps = [ | 215 | sway_deps = [ |
206 | cairo, | 216 | cairo, |
217 | drm, | ||
207 | jsonc, | 218 | jsonc, |
208 | libevdev, | 219 | libevdev, |
209 | libinput, | 220 | libinput, |
221 | libudev, | ||
210 | math, | 222 | math, |
211 | pango, | 223 | pango, |
212 | pcre, | 224 | pcre2, |
213 | glesv2, | ||
214 | pixman, | 225 | pixman, |
215 | server_protos, | 226 | threads, |
216 | wayland_server, | 227 | wayland_server, |
217 | wlroots, | 228 | wlroots, |
218 | xkbcommon, | 229 | xkbcommon, |
230 | xcb, | ||
231 | xcb_icccm, | ||
219 | ] | 232 | ] |
220 | 233 | ||
221 | if have_xwayland | 234 | if have_xwayland |
222 | sway_sources += 'desktop/xwayland.c' | 235 | sway_sources += 'desktop/xwayland.c' |
223 | sway_deps += xcb | 236 | endif |
237 | |||
238 | if wlroots_features['libinput_backend'] | ||
239 | sway_sources += 'input/libinput.c' | ||
224 | endif | 240 | endif |
225 | 241 | ||
226 | executable( | 242 | executable( |
227 | 'sway', | 243 | 'sway', |
228 | sway_sources, | 244 | sway_sources + wl_protos_src, |
229 | include_directories: [sway_inc], | 245 | include_directories: [sway_inc], |
230 | dependencies: sway_deps, | 246 | dependencies: sway_deps, |
231 | link_with: [lib_sway_common], | 247 | link_with: [lib_sway_common], |
diff --git a/sway/realtime.c b/sway/realtime.c new file mode 100644 index 00000000..11154af0 --- /dev/null +++ b/sway/realtime.c | |||
@@ -0,0 +1,40 @@ | |||
1 | #include <sys/resource.h> | ||
2 | #include <sched.h> | ||
3 | #include <unistd.h> | ||
4 | #include <pthread.h> | ||
5 | #include "sway/server.h" | ||
6 | #include "log.h" | ||
7 | |||
8 | static void child_fork_callback(void) { | ||
9 | struct sched_param param; | ||
10 | |||
11 | param.sched_priority = 0; | ||
12 | |||
13 | int ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m); | ||
14 | if (ret != 0) { | ||
15 | sway_log(SWAY_ERROR, "Failed to reset scheduler policy on fork"); | ||
16 | } | ||
17 | } | ||
18 | |||
19 | void set_rr_scheduling(void) { | ||
20 | int prio = sched_get_priority_min(SCHED_RR); | ||
21 | int old_policy; | ||
22 | int ret; | ||
23 | struct sched_param param; | ||
24 | |||
25 | ret = pthread_getschedparam(pthread_self(), &old_policy, ¶m); | ||
26 | if (ret != 0) { | ||
27 | sway_log(SWAY_DEBUG, "Failed to get old scheduling priority"); | ||
28 | return; | ||
29 | } | ||
30 | |||
31 | param.sched_priority = prio; | ||
32 | |||
33 | ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); | ||
34 | if (ret != 0) { | ||
35 | sway_log(SWAY_INFO, "Failed to set scheduling priority to %d", prio); | ||
36 | return; | ||
37 | } | ||
38 | |||
39 | pthread_atfork(NULL, NULL, child_fork_callback); | ||
40 | } | ||
diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c new file mode 100644 index 00000000..a30d4664 --- /dev/null +++ b/sway/scene_descriptor.c | |||
@@ -0,0 +1,66 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <wlr/util/addon.h> | ||
3 | #include "log.h" | ||
4 | #include "sway/scene_descriptor.h" | ||
5 | |||
6 | struct scene_descriptor { | ||
7 | void *data; | ||
8 | struct wlr_addon addon; | ||
9 | }; | ||
10 | |||
11 | static const struct wlr_addon_interface addon_interface; | ||
12 | |||
13 | static struct scene_descriptor *scene_node_get_descriptor( | ||
14 | struct wlr_scene_node *node, enum sway_scene_descriptor_type type) { | ||
15 | struct wlr_addon *addon = wlr_addon_find(&node->addons, (void *)type, &addon_interface); | ||
16 | if (!addon) { | ||
17 | return NULL; | ||
18 | } | ||
19 | |||
20 | struct scene_descriptor *desc = wl_container_of(addon, desc, addon); | ||
21 | return desc; | ||
22 | } | ||
23 | |||
24 | static void descriptor_destroy(struct scene_descriptor *desc) { | ||
25 | wlr_addon_finish(&desc->addon); | ||
26 | free(desc); | ||
27 | } | ||
28 | |||
29 | void *scene_descriptor_try_get(struct wlr_scene_node *node, | ||
30 | enum sway_scene_descriptor_type type) { | ||
31 | struct scene_descriptor *desc = scene_node_get_descriptor(node, type); | ||
32 | if (!desc) { | ||
33 | return NULL; | ||
34 | } | ||
35 | |||
36 | return desc->data; | ||
37 | } | ||
38 | |||
39 | void scene_descriptor_destroy(struct wlr_scene_node *node, | ||
40 | enum sway_scene_descriptor_type type) { | ||
41 | struct scene_descriptor *desc = scene_node_get_descriptor(node, type); | ||
42 | descriptor_destroy(desc); | ||
43 | } | ||
44 | |||
45 | static void addon_handle_destroy(struct wlr_addon *addon) { | ||
46 | struct scene_descriptor *desc = wl_container_of(addon, desc, addon); | ||
47 | descriptor_destroy(desc); | ||
48 | } | ||
49 | |||
50 | static const struct wlr_addon_interface addon_interface = { | ||
51 | .name = "sway_scene_descriptor", | ||
52 | .destroy = addon_handle_destroy, | ||
53 | }; | ||
54 | |||
55 | bool scene_descriptor_assign(struct wlr_scene_node *node, | ||
56 | enum sway_scene_descriptor_type type, void *data) { | ||
57 | struct scene_descriptor *desc = calloc(1, sizeof(*desc)); | ||
58 | if (!desc) { | ||
59 | sway_log(SWAY_ERROR, "Could not allocate a scene descriptor"); | ||
60 | return false; | ||
61 | } | ||
62 | |||
63 | wlr_addon_init(&desc->addon, &node->addons, (void *)type, &addon_interface); | ||
64 | desc->data = data; | ||
65 | return true; | ||
66 | } | ||
diff --git a/sway/server.c b/sway/server.c index f51fcfe2..180d3a6b 100644 --- a/sway/server.c +++ b/sway/server.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -7,25 +6,45 @@ | |||
7 | #include <wlr/backend.h> | 6 | #include <wlr/backend.h> |
8 | #include <wlr/backend/headless.h> | 7 | #include <wlr/backend/headless.h> |
9 | #include <wlr/backend/multi.h> | 8 | #include <wlr/backend/multi.h> |
10 | #include <wlr/backend/noop.h> | 9 | #include <wlr/config.h> |
11 | #include <wlr/backend/session.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> |
16 | #include <wlr/types/wlr_data_device.h> | ||
17 | #include <wlr/types/wlr_drm.h> | ||
15 | #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> | ||
16 | #include <wlr/types/wlr_gamma_control_v1.h> | 22 | #include <wlr/types/wlr_gamma_control_v1.h> |
17 | #include <wlr/types/wlr_idle.h> | 23 | #include <wlr/types/wlr_idle_notify_v1.h> |
18 | #include <wlr/types/wlr_layer_shell_v1.h> | 24 | #include <wlr/types/wlr_layer_shell_v1.h> |
25 | #include <wlr/types/wlr_linux_dmabuf_v1.h> | ||
26 | #include <wlr/types/wlr_output_management_v1.h> | ||
27 | #include <wlr/types/wlr_output_power_management_v1.h> | ||
19 | #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> | ||
20 | #include <wlr/types/wlr_primary_selection_v1.h> | 30 | #include <wlr/types/wlr_primary_selection_v1.h> |
21 | #include <wlr/types/wlr_relative_pointer_v1.h> | 31 | #include <wlr/types/wlr_relative_pointer_v1.h> |
22 | #include <wlr/types/wlr_screencopy_v1.h> | 32 | #include <wlr/types/wlr_screencopy_v1.h> |
33 | #include <wlr/types/wlr_security_context_v1.h> | ||
23 | #include <wlr/types/wlr_server_decoration.h> | 34 | #include <wlr/types/wlr_server_decoration.h> |
35 | #include <wlr/types/wlr_session_lock_v1.h> | ||
36 | #include <wlr/types/wlr_single_pixel_buffer_v1.h> | ||
37 | #include <wlr/types/wlr_subcompositor.h> | ||
24 | #include <wlr/types/wlr_tablet_v2.h> | 38 | #include <wlr/types/wlr_tablet_v2.h> |
25 | #include <wlr/types/wlr_viewporter.h> | 39 | #include <wlr/types/wlr_viewporter.h> |
26 | #include <wlr/types/wlr_xcursor_manager.h> | 40 | #include <wlr/types/wlr_xcursor_manager.h> |
41 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
27 | #include <wlr/types/wlr_xdg_decoration_v1.h> | 42 | #include <wlr/types/wlr_xdg_decoration_v1.h> |
43 | #include <wlr/types/wlr_xdg_foreign_registry.h> | ||
44 | #include <wlr/types/wlr_xdg_foreign_v1.h> | ||
45 | #include <wlr/types/wlr_xdg_foreign_v2.h> | ||
28 | #include <wlr/types/wlr_xdg_output_v1.h> | 46 | #include <wlr/types/wlr_xdg_output_v1.h> |
47 | #include <xf86drm.h> | ||
29 | #include "config.h" | 48 | #include "config.h" |
30 | #include "list.h" | 49 | #include "list.h" |
31 | #include "log.h" | 50 | #include "log.h" |
@@ -34,41 +53,221 @@ | |||
34 | #include "sway/input/input-manager.h" | 53 | #include "sway/input/input-manager.h" |
35 | #include "sway/output.h" | 54 | #include "sway/output.h" |
36 | #include "sway/server.h" | 55 | #include "sway/server.h" |
56 | #include "sway/input/cursor.h" | ||
37 | #include "sway/tree/root.h" | 57 | #include "sway/tree/root.h" |
58 | |||
38 | #if HAVE_XWAYLAND | 59 | #if HAVE_XWAYLAND |
60 | #include <wlr/xwayland/shell.h> | ||
39 | #include "sway/xwayland.h" | 61 | #include "sway/xwayland.h" |
40 | #endif | 62 | #endif |
41 | 63 | ||
42 | bool server_privileged_prepare(struct sway_server *server) { | 64 | #if WLR_HAS_DRM_BACKEND |
43 | sway_log(SWAY_DEBUG, "Preparing Wayland server initialization"); | 65 | #include <wlr/types/wlr_drm_lease_v1.h> |
66 | #endif | ||
67 | |||
68 | #define SWAY_XDG_SHELL_VERSION 5 | ||
69 | #define SWAY_LAYER_SHELL_VERSION 4 | ||
70 | #define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 | ||
71 | |||
72 | bool allow_unsupported_gpu = false; | ||
73 | |||
74 | #if WLR_HAS_DRM_BACKEND | ||
75 | static void handle_drm_lease_request(struct wl_listener *listener, void *data) { | ||
76 | /* We only offer non-desktop outputs, but in the future we might want to do | ||
77 | * more logic here. */ | ||
78 | |||
79 | struct wlr_drm_lease_request_v1 *req = data; | ||
80 | struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req); | ||
81 | if (!lease) { | ||
82 | sway_log(SWAY_ERROR, "Failed to grant lease request"); | ||
83 | wlr_drm_lease_request_v1_reject(req); | ||
84 | } | ||
85 | } | ||
86 | #endif | ||
87 | |||
88 | static bool is_privileged(const struct wl_global *global) { | ||
89 | #if WLR_HAS_DRM_BACKEND | ||
90 | if (server.drm_lease_manager != NULL) { | ||
91 | struct wlr_drm_lease_device_v1 *drm_lease_dev; | ||
92 | wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) { | ||
93 | if (drm_lease_dev->global == global) { | ||
94 | return true; | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | #endif | ||
99 | |||
100 | return | ||
101 | global == server.output_manager_v1->global || | ||
102 | global == server.output_power_manager_v1->global || | ||
103 | global == server.input_method->global || | ||
104 | global == server.foreign_toplevel_list->global || | ||
105 | global == server.foreign_toplevel_manager->global || | ||
106 | global == server.data_control_manager_v1->global || | ||
107 | global == server.screencopy_manager_v1->global || | ||
108 | global == server.export_dmabuf_manager_v1->global || | ||
109 | global == server.security_context_manager_v1->global || | ||
110 | global == server.gamma_control_manager_v1->global || | ||
111 | global == server.layer_shell->global || | ||
112 | global == server.session_lock.manager->global || | ||
113 | global == server.input->keyboard_shortcuts_inhibit->global || | ||
114 | global == server.input->virtual_keyboard->global || | ||
115 | global == server.input->virtual_pointer->global || | ||
116 | global == server.input->transient_seat_manager->global; | ||
117 | } | ||
118 | |||
119 | static bool filter_global(const struct wl_client *client, | ||
120 | const struct wl_global *global, void *data) { | ||
121 | #if HAVE_XWAYLAND | ||
122 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | ||
123 | if (xwayland && global == xwayland->shell_v1->global) { | ||
124 | return xwayland->server != NULL && client == xwayland->server->client; | ||
125 | } | ||
126 | #endif | ||
127 | |||
128 | // Restrict usage of privileged protocols to unsandboxed clients | ||
129 | // TODO: add a way for users to configure an allow-list | ||
130 | const struct wlr_security_context_v1_state *security_context = | ||
131 | wlr_security_context_manager_v1_lookup_client( | ||
132 | server.security_context_manager_v1, (struct wl_client *)client); | ||
133 | if (is_privileged(global)) { | ||
134 | return security_context == NULL; | ||
135 | } | ||
136 | |||
137 | return true; | ||
138 | } | ||
139 | |||
140 | static void detect_proprietary(struct wlr_backend *backend, void *data) { | ||
141 | int drm_fd = wlr_backend_get_drm_fd(backend); | ||
142 | if (drm_fd < 0) { | ||
143 | return; | ||
144 | } | ||
145 | |||
146 | drmVersion *version = drmGetVersion(drm_fd); | ||
147 | if (version == NULL) { | ||
148 | sway_log(SWAY_ERROR, "drmGetVersion() failed"); | ||
149 | return; | ||
150 | } | ||
151 | |||
152 | bool is_unsupported = false; | ||
153 | if (strcmp(version->name, "nvidia-drm") == 0) { | ||
154 | is_unsupported = true; | ||
155 | sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); | ||
156 | if (!allow_unsupported_gpu) { | ||
157 | sway_log(SWAY_ERROR, "Use Nouveau instead"); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | if (strcmp(version->name, "evdi") == 0) { | ||
162 | is_unsupported = true; | ||
163 | sway_log(SWAY_ERROR, "!!! Proprietary DisplayLink drivers are in use !!!"); | ||
164 | } | ||
165 | |||
166 | if (!allow_unsupported_gpu && is_unsupported) { | ||
167 | sway_log(SWAY_ERROR, | ||
168 | "Proprietary drivers are NOT supported. To launch sway anyway, " | ||
169 | "launch with --unsupported-gpu and DO NOT report issues."); | ||
170 | exit(EXIT_FAILURE); | ||
171 | } | ||
172 | |||
173 | drmFreeVersion(version); | ||
174 | } | ||
175 | |||
176 | static void handle_renderer_lost(struct wl_listener *listener, void *data) { | ||
177 | struct sway_server *server = wl_container_of(listener, server, renderer_lost); | ||
178 | |||
179 | sway_log(SWAY_INFO, "Re-creating renderer after GPU reset"); | ||
180 | |||
181 | struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); | ||
182 | if (renderer == NULL) { | ||
183 | sway_log(SWAY_ERROR, "Unable to create renderer"); | ||
184 | return; | ||
185 | } | ||
186 | |||
187 | struct wlr_allocator *allocator = | ||
188 | wlr_allocator_autocreate(server->backend, renderer); | ||
189 | if (allocator == NULL) { | ||
190 | sway_log(SWAY_ERROR, "Unable to create allocator"); | ||
191 | wlr_renderer_destroy(renderer); | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | struct wlr_renderer *old_renderer = server->renderer; | ||
196 | struct wlr_allocator *old_allocator = server->allocator; | ||
197 | server->renderer = renderer; | ||
198 | server->allocator = allocator; | ||
199 | |||
200 | wl_list_remove(&server->renderer_lost.link); | ||
201 | wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); | ||
202 | |||
203 | wlr_compositor_set_renderer(server->compositor, renderer); | ||
204 | |||
205 | for (int i = 0; i < root->outputs->length; ++i) { | ||
206 | struct sway_output *output = root->outputs->items[i]; | ||
207 | wlr_output_init_render(output->wlr_output, | ||
208 | server->allocator, server->renderer); | ||
209 | } | ||
210 | |||
211 | wlr_allocator_destroy(old_allocator); | ||
212 | wlr_renderer_destroy(old_renderer); | ||
213 | } | ||
214 | |||
215 | bool server_init(struct sway_server *server) { | ||
216 | sway_log(SWAY_DEBUG, "Initializing Wayland server"); | ||
44 | server->wl_display = wl_display_create(); | 217 | server->wl_display = wl_display_create(); |
45 | 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); |
46 | server->backend = wlr_backend_autocreate(server->wl_display); | ||
47 | 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); | ||
48 | if (!server->backend) { | 225 | if (!server->backend) { |
49 | sway_log(SWAY_ERROR, "Unable to create backend"); | 226 | sway_log(SWAY_ERROR, "Unable to create backend"); |
50 | return false; | 227 | return false; |
51 | } | 228 | } |
52 | return true; | ||
53 | } | ||
54 | 229 | ||
55 | bool server_init(struct sway_server *server) { | 230 | wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL); |
56 | sway_log(SWAY_DEBUG, "Initializing Wayland server"); | ||
57 | 231 | ||
58 | struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); | 232 | server->renderer = wlr_renderer_autocreate(server->backend); |
59 | assert(renderer); | 233 | if (!server->renderer) { |
234 | sway_log(SWAY_ERROR, "Failed to create renderer"); | ||
235 | return false; | ||
236 | } | ||
60 | 237 | ||
61 | wlr_renderer_init_wl_display(renderer, server->wl_display); | 238 | server->renderer_lost.notify = handle_renderer_lost; |
239 | wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); | ||
62 | 240 | ||
63 | server->compositor = wlr_compositor_create(server->wl_display, renderer); | 241 | wlr_renderer_init_wl_shm(server->renderer, server->wl_display); |
64 | server->compositor_new_surface.notify = handle_compositor_new_surface; | 242 | |
65 | wl_signal_add(&server->compositor->events.new_surface, | 243 | if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { |
66 | &server->compositor_new_surface); | 244 | server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( |
245 | server->wl_display, 4, server->renderer); | ||
246 | if (debug.legacy_wl_drm) { | ||
247 | wlr_drm_create(server->wl_display, server->renderer); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | server->allocator = wlr_allocator_autocreate(server->backend, | ||
252 | server->renderer); | ||
253 | if (!server->allocator) { | ||
254 | sway_log(SWAY_ERROR, "Failed to create allocator"); | ||
255 | return false; | ||
256 | } | ||
257 | |||
258 | server->compositor = wlr_compositor_create(server->wl_display, 6, | ||
259 | server->renderer); | ||
260 | |||
261 | wlr_subcompositor_create(server->wl_display); | ||
67 | 262 | ||
68 | server->data_device_manager = | 263 | server->data_device_manager = |
69 | wlr_data_device_manager_create(server->wl_display); | 264 | wlr_data_device_manager_create(server->wl_display); |
70 | 265 | ||
71 | wlr_gamma_control_manager_v1_create(server->wl_display); | 266 | server->gamma_control_manager_v1 = |
267 | wlr_gamma_control_manager_v1_create(server->wl_display); | ||
268 | server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma; | ||
269 | wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma, | ||
270 | &server->gamma_control_set_gamma); | ||
72 | 271 | ||
73 | server->new_output.notify = handle_new_output; | 272 | server->new_output.notify = handle_new_output; |
74 | wl_signal_add(&server->backend->events.new_output, &server->new_output); | 273 | wl_signal_add(&server->backend->events.new_output, &server->new_output); |
@@ -78,19 +277,20 @@ bool server_init(struct sway_server *server) { | |||
78 | 277 | ||
79 | wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); | 278 | wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); |
80 | 279 | ||
81 | server->idle = wlr_idle_create(server->wl_display); | 280 | server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); |
82 | server->idle_inhibit_manager_v1 = | 281 | sway_idle_inhibit_manager_v1_init(); |
83 | sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle); | ||
84 | 282 | ||
85 | server->layer_shell = wlr_layer_shell_v1_create(server->wl_display); | 283 | server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, |
284 | SWAY_LAYER_SHELL_VERSION); | ||
86 | wl_signal_add(&server->layer_shell->events.new_surface, | 285 | wl_signal_add(&server->layer_shell->events.new_surface, |
87 | &server->layer_shell_surface); | 286 | &server->layer_shell_surface); |
88 | server->layer_shell_surface.notify = handle_layer_shell_surface; | 287 | server->layer_shell_surface.notify = handle_layer_shell_surface; |
89 | 288 | ||
90 | server->xdg_shell = wlr_xdg_shell_create(server->wl_display); | 289 | server->xdg_shell = wlr_xdg_shell_create(server->wl_display, |
91 | wl_signal_add(&server->xdg_shell->events.new_surface, | 290 | SWAY_XDG_SHELL_VERSION); |
92 | &server->xdg_shell_surface); | 291 | wl_signal_add(&server->xdg_shell->events.new_toplevel, |
93 | server->xdg_shell_surface.notify = handle_xdg_shell_surface; | 292 | &server->xdg_shell_toplevel); |
293 | server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel; | ||
94 | 294 | ||
95 | server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); | 295 | server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); |
96 | 296 | ||
@@ -121,8 +321,7 @@ bool server_init(struct sway_server *server) { | |||
121 | wl_signal_add(&server->pointer_constraints->events.new_constraint, | 321 | wl_signal_add(&server->pointer_constraints->events.new_constraint, |
122 | &server->pointer_constraint); | 322 | &server->pointer_constraint); |
123 | 323 | ||
124 | server->presentation = | 324 | wlr_presentation_create(server->wl_display, server->backend); |
125 | wlr_presentation_create(server->wl_display, server->backend); | ||
126 | 325 | ||
127 | server->output_manager_v1 = | 326 | server->output_manager_v1 = |
128 | wlr_output_manager_v1_create(server->wl_display); | 327 | wlr_output_manager_v1_create(server->wl_display); |
@@ -141,19 +340,62 @@ bool server_init(struct sway_server *server) { | |||
141 | &server->output_power_manager_set_mode); | 340 | &server->output_power_manager_set_mode); |
142 | server->input_method = wlr_input_method_manager_v2_create(server->wl_display); | 341 | server->input_method = wlr_input_method_manager_v2_create(server->wl_display); |
143 | server->text_input = wlr_text_input_manager_v3_create(server->wl_display); | 342 | server->text_input = wlr_text_input_manager_v3_create(server->wl_display); |
343 | server->foreign_toplevel_list = | ||
344 | wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION); | ||
144 | server->foreign_toplevel_manager = | 345 | server->foreign_toplevel_manager = |
145 | wlr_foreign_toplevel_manager_v1_create(server->wl_display); | 346 | wlr_foreign_toplevel_manager_v1_create(server->wl_display); |
146 | 347 | ||
147 | wlr_export_dmabuf_manager_v1_create(server->wl_display); | 348 | sway_session_lock_init(); |
148 | wlr_screencopy_manager_v1_create(server->wl_display); | 349 | |
149 | wlr_data_control_manager_v1_create(server->wl_display); | 350 | #if WLR_HAS_DRM_BACKEND |
150 | wlr_primary_selection_v1_device_manager_create(server->wl_display); | 351 | server->drm_lease_manager= |
352 | wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); | ||
353 | if (server->drm_lease_manager) { | ||
354 | server->drm_lease_request.notify = handle_drm_lease_request; | ||
355 | wl_signal_add(&server->drm_lease_manager->events.request, | ||
356 | &server->drm_lease_request); | ||
357 | } else { | ||
358 | sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); | ||
359 | sway_log(SWAY_INFO, "VR will not be available"); | ||
360 | } | ||
361 | #endif | ||
362 | |||
363 | server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display); | ||
364 | server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); | ||
365 | server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); | ||
366 | server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); | ||
151 | wlr_viewporter_create(server->wl_display); | 367 | wlr_viewporter_create(server->wl_display); |
368 | wlr_single_pixel_buffer_manager_v1_create(server->wl_display); | ||
369 | server->content_type_manager_v1 = | ||
370 | wlr_content_type_manager_v1_create(server->wl_display, 1); | ||
371 | wlr_fractional_scale_manager_v1_create(server->wl_display, 1); | ||
372 | |||
373 | struct wlr_xdg_foreign_registry *foreign_registry = | ||
374 | wlr_xdg_foreign_registry_create(server->wl_display); | ||
375 | wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry); | ||
376 | wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry); | ||
377 | |||
378 | server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display); | ||
379 | server->xdg_activation_v1_request_activate.notify = | ||
380 | xdg_activation_v1_handle_request_activate; | ||
381 | wl_signal_add(&server->xdg_activation_v1->events.request_activate, | ||
382 | &server->xdg_activation_v1_request_activate); | ||
383 | server->xdg_activation_v1_new_token.notify = | ||
384 | xdg_activation_v1_handle_new_token; | ||
385 | wl_signal_add(&server->xdg_activation_v1->events.new_token, | ||
386 | &server->xdg_activation_v1_new_token); | ||
387 | |||
388 | struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = | ||
389 | wlr_cursor_shape_manager_v1_create(server->wl_display, 1); | ||
390 | server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; | ||
391 | wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape); | ||
392 | |||
393 | wl_list_init(&server->pending_launcher_ctxs); | ||
152 | 394 | ||
153 | // Avoid using "wayland-0" as display socket | 395 | // Avoid using "wayland-0" as display socket |
154 | char name_candidate[16]; | 396 | char name_candidate[16]; |
155 | for (int i = 1; i <= 32; ++i) { | 397 | for (unsigned int i = 1; i <= 32; ++i) { |
156 | sprintf(name_candidate, "wayland-%d", i); | 398 | snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i); |
157 | if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { | 399 | if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { |
158 | server->socket = strdup(name_candidate); | 400 | server->socket = strdup(name_candidate); |
159 | break; | 401 | break; |
@@ -166,27 +408,26 @@ bool server_init(struct sway_server *server) { | |||
166 | return false; | 408 | return false; |
167 | } | 409 | } |
168 | 410 | ||
169 | server->noop_backend = wlr_noop_backend_create(server->wl_display); | 411 | server->headless_backend = wlr_headless_backend_create(server->wl_event_loop); |
170 | |||
171 | struct wlr_output *wlr_output = wlr_noop_add_output(server->noop_backend); | ||
172 | root->noop_output = output_create(wlr_output); | ||
173 | |||
174 | server->headless_backend = | ||
175 | wlr_headless_backend_create_with_renderer(server->wl_display, renderer); | ||
176 | if (!server->headless_backend) { | 412 | if (!server->headless_backend) { |
177 | sway_log(SWAY_INFO, "Failed to create secondary headless backend, " | 413 | sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); |
178 | "starting without it"); | 414 | wlr_backend_destroy(server->backend); |
415 | return false; | ||
179 | } else { | 416 | } else { |
180 | wlr_multi_backend_add(server->backend, server->headless_backend); | 417 | wlr_multi_backend_add(server->backend, server->headless_backend); |
181 | } | 418 | } |
182 | 419 | ||
420 | struct wlr_output *wlr_output = | ||
421 | wlr_headless_add_output(server->headless_backend, 800, 600); | ||
422 | wlr_output_set_name(wlr_output, "FALLBACK"); | ||
423 | root->fallback_output = output_create(wlr_output); | ||
424 | |||
183 | // This may have been set already via -Dtxn-timeout | 425 | // This may have been set already via -Dtxn-timeout |
184 | if (!server->txn_timeout_ms) { | 426 | if (!server->txn_timeout_ms) { |
185 | server->txn_timeout_ms = 200; | 427 | server->txn_timeout_ms = 200; |
186 | } | 428 | } |
187 | 429 | ||
188 | server->dirty_nodes = create_list(); | 430 | server->dirty_nodes = create_list(); |
189 | server->transactions = create_list(); | ||
190 | 431 | ||
191 | server->input = input_manager_create(server); | 432 | server->input = input_manager_create(server); |
192 | input_manager_get_default_seat(); // create seat0 | 433 | input_manager_get_default_seat(); // create seat0 |
@@ -200,9 +441,9 @@ void server_fini(struct sway_server *server) { | |||
200 | wlr_xwayland_destroy(server->xwayland.wlr_xwayland); | 441 | wlr_xwayland_destroy(server->xwayland.wlr_xwayland); |
201 | #endif | 442 | #endif |
202 | wl_display_destroy_clients(server->wl_display); | 443 | wl_display_destroy_clients(server->wl_display); |
444 | wlr_backend_destroy(server->backend); | ||
203 | wl_display_destroy(server->wl_display); | 445 | wl_display_destroy(server->wl_display); |
204 | list_free(server->dirty_nodes); | 446 | list_free(server->dirty_nodes); |
205 | list_free(server->transactions); | ||
206 | } | 447 | } |
207 | 448 | ||
208 | bool server_start(struct sway_server *server) { | 449 | bool server_start(struct sway_server *server) { |
@@ -231,6 +472,10 @@ bool server_start(struct sway_server *server) { | |||
231 | } | 472 | } |
232 | #endif | 473 | #endif |
233 | 474 | ||
475 | if (config->primary_selection) { | ||
476 | wlr_primary_selection_v1_device_manager_create(server->wl_display); | ||
477 | } | ||
478 | |||
234 | sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", | 479 | sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", |
235 | server->socket); | 480 | server->socket); |
236 | if (!wlr_backend_start(server->backend)) { | 481 | if (!wlr_backend_start(server->backend)) { |
@@ -238,6 +483,7 @@ bool server_start(struct sway_server *server) { | |||
238 | wlr_backend_destroy(server->backend); | 483 | wlr_backend_destroy(server->backend); |
239 | return false; | 484 | return false; |
240 | } | 485 | } |
486 | |||
241 | return true; | 487 | return true; |
242 | } | 488 | } |
243 | 489 | ||
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 80d08449..42e59d57 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd | |||
@@ -40,7 +40,7 @@ runtime. | |||
40 | *font* <font> | 40 | *font* <font> |
41 | Specifies the font to be used in the bar. _font_ should be specified as a | 41 | Specifies the font to be used in the bar. _font_ should be specified as a |
42 | pango font description. For more information on pango font descriptions, | 42 | pango font description. For more information on pango font descriptions, |
43 | see https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string | 43 | see https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description |
44 | 44 | ||
45 | *gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left> | 45 | *gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left> |
46 | Sets the gaps from the edge of the screen for the bar. Gaps can either be | 46 | Sets the gaps from the edge of the screen for the bar. Gaps can either be |
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index dbf21d93..442311bb 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd | |||
@@ -111,6 +111,9 @@ The following commands may only be used in the configuration file. | |||
111 | Maps inputs from this device to the specified output. Only meaningful if the | 111 | Maps inputs from this device to the specified output. Only meaningful if the |
112 | device is a pointer, touch, or drawing tablet device. | 112 | device is a pointer, touch, or drawing tablet device. |
113 | 113 | ||
114 | The wildcard _\*_ can be used to map the input device to the whole desktop | ||
115 | layout. | ||
116 | |||
114 | *input* <identifier> map_to_region <X> <Y> <width> <height> | 117 | *input* <identifier> map_to_region <X> <Y> <width> <height> |
115 | Maps inputs from this device to the specified region of the global output | 118 | Maps inputs from this device to the specified region of the global output |
116 | layout. Only meaningful if the device is a pointer, touch, or drawing tablet | 119 | layout. Only meaningful if the device is a pointer, touch, or drawing tablet |
@@ -118,11 +121,16 @@ The following commands may only be used in the configuration file. | |||
118 | 121 | ||
119 | *input* <identifier> map_from_region <X1xY1> <X2xY2> | 122 | *input* <identifier> map_from_region <X1xY1> <X2xY2> |
120 | 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 |
121 | 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 |
122 | (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 |
123 | 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 |
124 | 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 |
125 | 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. | ||
126 | 134 | ||
127 | ## LIBINPUT CONFIGURATION | 135 | ## LIBINPUT CONFIGURATION |
128 | 136 | ||
@@ -144,6 +152,10 @@ The following commands may only be used in the configuration file. | |||
144 | *input* <identifier> dwt enabled|disabled | 152 | *input* <identifier> dwt enabled|disabled |
145 | Enables or disables disable-while-typing for the specified input device. | 153 | Enables or disables disable-while-typing for the specified input device. |
146 | 154 | ||
155 | *input* <identifier> dwtp enabled|disabled | ||
156 | Enables or disables disable-while-trackpointing for the specified input | ||
157 | device. | ||
158 | |||
147 | *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>] |
148 | Enables or disables send_events for specified input device. Disabling | 160 | Enables or disables send_events for specified input device. Disabling |
149 | send_events disables the input device. | 161 | send_events disables the input device. |
@@ -168,12 +180,19 @@ The following commands may only be used in the configuration file. | |||
168 | *input* <identifier> pointer_accel [<-1|1>] | 180 | *input* <identifier> pointer_accel [<-1|1>] |
169 | Changes the pointer acceleration for the specified input device. | 181 | Changes the pointer acceleration for the specified input device. |
170 | 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 | |||
171 | *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> |
172 | 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 |
173 | 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 |
174 | 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 |
175 | _disable_, it disables the scroll_method on_button_down. | 191 | _disable_, it disables the scroll_method on_button_down. |
176 | 192 | ||
193 | *input* <identifier> scroll_button_lock enabled|disabled | ||
194 | Enables or disables scroll button lock for specified input device. | ||
195 | |||
177 | *input* <identifier> scroll_factor <floating point value> | 196 | *input* <identifier> scroll_factor <floating point value> |
178 | 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 |
179 | be scaled by the given value, which must be non-negative. | 198 | be scaled by the given value, which must be non-negative. |
@@ -217,6 +236,8 @@ correct seat. | |||
217 | absolute coordinates (with respect to the global coordinate space). | 236 | absolute coordinates (with respect to the global coordinate space). |
218 | Specifying either value as 0 will not update that coordinate. | 237 | Specifying either value as 0 will not update that coordinate. |
219 | 238 | ||
239 | Deprecated: use the virtual-pointer Wayland protocol instead. | ||
240 | |||
220 | *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> |
221 | Simulate pressing (or releasing) the specified mouse button on the | 242 | Simulate pressing (or releasing) the specified mouse button on the |
222 | 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 |
@@ -225,12 +246,14 @@ correct seat. | |||
225 | 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 |
226 | both will occur. | 247 | both will occur. |
227 | 248 | ||
249 | Deprecated: use the virtual-pointer Wayland protocol instead. | ||
250 | |||
228 | *seat* <name> fallback true|false | 251 | *seat* <name> fallback true|false |
229 | 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 |
230 | not explicitly attached to another seat (similar to a "default" seat). | 253 | not explicitly attached to another seat (similar to a "default" seat). |
231 | 254 | ||
232 | *seat* <name> hide_cursor <timeout>|when-typing [enable|disable] | 255 | *seat* <name> hide_cursor <timeout>|when-typing [enable|disable] |
233 | Hides the cursor image after the specified event occured. | 256 | Hides the cursor image after the specified event occurred. |
234 | 257 | ||
235 | If _timeout_ is specified, then the cursor will be hidden after _timeout_ | 258 | If _timeout_ is specified, then the cursor will be hidden after _timeout_ |
236 | (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0 | 259 | (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0 |
@@ -240,18 +263,16 @@ correct seat. | |||
240 | 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 |
241 | is pressed. | 264 | is pressed. |
242 | 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 | |||
243 | *seat* <name> idle_inhibit <sources...> | 270 | *seat* <name> idle_inhibit <sources...> |
244 | 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 |
245 | 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 |
246 | "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", | 273 | "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", |
247 | 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. |
248 | 275 | ||
249 | *seat* <name> idle_wake <sources...> | ||
250 | Sets the set of input event sources which can wake the seat from | ||
251 | its idle state, as a space separated list of source names. Valid names are | ||
252 | "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", | ||
253 | and "switch". The default behavior is to wake from idle on any event. | ||
254 | |||
255 | *seat* <name> keyboard_grouping none|smart | 276 | *seat* <name> keyboard_grouping none|smart |
256 | 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 |
257 | 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 |
@@ -274,7 +295,7 @@ correct seat. | |||
274 | whether future inhibitors are honoured by default, i.e. activated | 295 | whether future inhibitors are honoured by default, i.e. activated |
275 | automatically, the default being _enable_. When used at runtime, | 296 | automatically, the default being _enable_. When used at runtime, |
276 | _disable_ also disables any currently active inhibitors. _activate_, | 297 | _disable_ also disables any currently active inhibitors. _activate_, |
277 | _deactivate_ and _toggle_ are only useable at runtime and change the | 298 | _deactivate_ and _toggle_ are only usable at runtime and change the |
278 | state of a potentially existing inhibitor on the currently focused | 299 | state of a potentially existing inhibitor on the currently focused |
279 | window. This can be used with the current seat alias (_-_) to affect | 300 | window. This can be used with the current seat alias (_-_) to affect |
280 | only the currently focused window of the current seat. Subcommand | 301 | only the currently focused window of the current seat. Subcommand |
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 1b855959..2f697248 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd | |||
@@ -213,7 +213,10 @@ following properties: | |||
213 | : Whether this output is active/enabled | 213 | : Whether this output is active/enabled |
214 | |- dpms | 214 | |- dpms |
215 | : boolean | 215 | : boolean |
216 | : Whether this output is on/off (via DPMS) | 216 | : (Deprecated, use _power_ instead) Whether this output is on/off (via DPMS) |
217 | |- power | ||
218 | : boolean | ||
219 | : Whether this output is on/off | ||
217 | |- primary | 220 | |- primary |
218 | : boolean | 221 | : boolean |
219 | : For i3 compatibility, this will be false. It does not make sense in Wayland | 222 | : For i3 compatibility, this will be false. It does not make sense in Wayland |
@@ -294,7 +297,7 @@ following properties: | |||
294 | Retrieve a JSON representation of the tree | 297 | Retrieve a JSON representation of the tree |
295 | 298 | ||
296 | *REPLY*++ | 299 | *REPLY*++ |
297 | An array of object the represent the current tree. Each object represents one | 300 | An array of objects that represent the current tree. Each object represents one |
298 | node and will have the following properties: | 301 | node and will have the following properties: |
299 | 302 | ||
300 | [- *PROPERTY* | 303 | [- *PROPERTY* |
@@ -334,8 +337,9 @@ node and will have the following properties: | |||
334 | this, but borders are included. | 337 | this, but borders are included. |
335 | |- window_rect | 338 | |- window_rect |
336 | : object | 339 | : object |
337 | : The geometry of the contents inside the node. The window decorations are | 340 | : The geometry of the content inside the node. These coordinates are relative |
338 | excluded from this calculation, but borders are included. | 341 | to the node itself. Window decorations and borders are outside the |
342 | _window_rect_ | ||
339 | |- deco_rect | 343 | |- deco_rect |
340 | : object | 344 | : object |
341 | : The geometry of the decorations for the node relative to the parent node | 345 | : The geometry of the decorations for the node relative to the parent node |
@@ -370,8 +374,14 @@ node and will have the following properties: | |||
370 | that can be used as an aid in submitting reproduction steps for bug reports | 374 | that can be used as an aid in submitting reproduction steps for bug reports |
371 | |- fullscreen_mode | 375 | |- fullscreen_mode |
372 | : integer | 376 | : integer |
373 | : (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means | 377 | : (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means |
374 | full workspace, and 2 means global fullscreen | 378 | full workspace, and 2 means global fullscreen |
379 | |- floating | ||
380 | : string | ||
381 | : Floating state of container. Can be either "auto_off" or "user_on" | ||
382 | |- scratchpad_state | ||
383 | : string | ||
384 | : Whether the window is in the scratchpad. Can be either "none" or "fresh" | ||
375 | |- app_id | 385 | |- app_id |
376 | : string | 386 | : string |
377 | : (Only views) For an xdg-shell view, the name of the application, if set. | 387 | : (Only views) For an xdg-shell view, the name of the application, if set. |
@@ -1036,7 +1046,7 @@ An object with a single string property containing the contents of the config | |||
1036 | *Example Reply:* | 1046 | *Example Reply:* |
1037 | ``` | 1047 | ``` |
1038 | { | 1048 | { |
1039 | "config": "set $mod Mod4\nbindsym $mod+q exit\n" | 1049 | "config": "set $mod Mod4\\nbindsym $mod+q exit\\n" |
1040 | } | 1050 | } |
1041 | ``` | 1051 | ``` |
1042 | 1052 | ||
@@ -1131,6 +1141,9 @@ following properties: | |||
1131 | |- xkb_active_layout_index | 1141 | |- xkb_active_layout_index |
1132 | : integer | 1142 | : integer |
1133 | : (Only keyboards) The index of the active keyboard layout in use | 1143 | : (Only keyboards) The index of the active keyboard layout in use |
1144 | |- scroll_factor | ||
1145 | : floating | ||
1146 | : (Only pointers) Multiplier applied on scroll event values. | ||
1134 | |- libinput | 1147 | |- libinput |
1135 | : object | 1148 | : object |
1136 | : (Only libinput devices) An object describing the current device settings. | 1149 | : (Only libinput devices) An object describing the current device settings. |
@@ -1188,9 +1201,16 @@ following properties will be included for devices that support them: | |||
1188 | : int | 1201 | : int |
1189 | : 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 |
1190 | 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_ | ||
1191 | |- dwt | 1207 | |- dwt |
1192 | : string | 1208 | : string |
1193 | : 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_ | ||
1194 | |- calibration_matrix | 1214 | |- calibration_matrix |
1195 | : array | 1215 | : array |
1196 | : An array of 6 floats representing the calibration matrix for absolute | 1216 | : An array of 6 floats representing the calibration matrix for absolute |
@@ -1230,7 +1250,8 @@ following properties will be included for devices that support them: | |||
1230 | "click_method": "button_areas", | 1250 | "click_method": "button_areas", |
1231 | "middle_emulation": "disabled", | 1251 | "middle_emulation": "disabled", |
1232 | "scroll_method": "edge", | 1252 | "scroll_method": "edge", |
1233 | "dwt": "enabled" | 1253 | "dwt": "enabled", |
1254 | "dwtp": "enabled" | ||
1234 | } | 1255 | } |
1235 | }, | 1256 | }, |
1236 | { | 1257 | { |
@@ -1357,7 +1378,8 @@ one seat. Each object has the following properties: | |||
1357 | "click_method": "button_areas", | 1378 | "click_method": "button_areas", |
1358 | "middle_emulation": "disabled", | 1379 | "middle_emulation": "disabled", |
1359 | "scroll_method": "edge", | 1380 | "scroll_method": "edge", |
1360 | "dwt": "enabled" | 1381 | "dwt": "enabled", |
1382 | "dwtp": "enabled" | ||
1361 | } | 1383 | } |
1362 | }, | 1384 | }, |
1363 | { | 1385 | { |
@@ -1433,6 +1455,9 @@ available: | |||
1433 | : workspace | 1455 | : workspace |
1434 | :[ Sent whenever an event involving a workspace occurs such as initialization | 1456 | :[ Sent whenever an event involving a workspace occurs such as initialization |
1435 | 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 | ||
1436 | |- 0x80000002 | 1461 | |- 0x80000002 |
1437 | : mode | 1462 | : mode |
1438 | : Sent whenever the binding mode changes | 1463 | : Sent whenever the binding mode changes |
@@ -1553,6 +1578,20 @@ The following change types are currently available: | |||
1553 | } | 1578 | } |
1554 | ``` | 1579 | ``` |
1555 | 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 | |||
1556 | ## 0x80000002. MODE | 1595 | ## 0x80000002. MODE |
1557 | 1596 | ||
1558 | 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 69f529fe..7d088d5d 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd | |||
@@ -24,7 +24,7 @@ must be separated by one space. For example: | |||
24 | 24 | ||
25 | # COMMANDS | 25 | # COMMANDS |
26 | 26 | ||
27 | *output* <name> mode|resolution|res [--custom] <WIDTHxHEIGHT>[@<RATE>Hz] | 27 | *output* <name> mode|resolution|res [--custom] <width>x<height>[@<rate>Hz] |
28 | Configures the specified output to use the given mode. Modes are a | 28 | Configures the specified output to use the given mode. Modes are a |
29 | combination of width and height (in pixels) and a refresh rate that your | 29 | combination of width and height (in pixels) and a refresh rate that your |
30 | display can be configured to use. For a list of available modes for each | 30 | display can be configured to use. For a list of available modes for each |
@@ -40,6 +40,16 @@ must be separated by one space. For example: | |||
40 | 40 | ||
41 | output HDMI-A-1 mode 1920x1080@60Hz | 41 | output HDMI-A-1 mode 1920x1080@60Hz |
42 | 42 | ||
43 | *output* <name> modeline <clock> <hdisplay> <hsync_start> <hsync_end> <htotal> <vdisplay> <vsync_start> <vsync_end> <vtotal> <hsync> <vsync> | ||
44 | Configures the specified output to use the given modeline. It can be | ||
45 | generated using *cvt*(1) and *gtf*(1) commands. See *xorg.conf*(5). | ||
46 | Only supported on DRM backend. | ||
47 | |||
48 | Example: | ||
49 | |||
50 | output HDMI-A-1 modeline 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync | ||
51 | |||
52 | |||
43 | *output* <name> position|pos <X> <Y> | 53 | *output* <name> position|pos <X> <Y> |
44 | Places the specified output at the specific position in the global | 54 | Places the specified output at the specific position in the global |
45 | coordinate space. The cursor may only be moved between immediately | 55 | coordinate space. The cursor may only be moved between immediately |
@@ -62,13 +72,11 @@ must be separated by one space. For example: | |||
62 | 72 | ||
63 | *output* <name> scale <factor> | 73 | *output* <name> scale <factor> |
64 | Scales the specified output by the specified scale _factor_. An integer is | 74 | Scales the specified output by the specified scale _factor_. An integer is |
65 | recommended, but fractional values are also supported. If a fractional | 75 | recommended, but fractional values are also supported. You may be better |
66 | value are specified, be warned that it is not possible to faithfully | 76 | served by setting an integer scale factor and adjusting the font size of |
67 | represent the contents of your windows - they will be rendered at the next | 77 | your applications to taste. HiDPI isn't supported with Xwayland clients |
68 | highest integer scale factor and downscaled. You may be better served by | 78 | (windows will blur). A fractional scale may be slightly adjusted to match |
69 | setting an integer scale factor and adjusting the font size of your | 79 | requirements of the protocol. |
70 | applications to taste. HiDPI isn't supported with Xwayland clients (windows | ||
71 | will blur). | ||
72 | 80 | ||
73 | *output* <name> scale_filter linear|nearest|smart | 81 | *output* <name> scale_filter linear|nearest|smart |
74 | Indicates how to scale application buffers that are rendered at a scale | 82 | Indicates how to scale application buffers that are rendered at a scale |
@@ -109,12 +117,20 @@ must be separated by one space. For example: | |||
109 | Enables or disables the specified output (all outputs are enabled by | 117 | Enables or disables the specified output (all outputs are enabled by |
110 | default). | 118 | default). |
111 | 119 | ||
120 | As opposed to the _power_ command, the output will lose its current | ||
121 | workspace and windows. | ||
122 | |||
112 | *output* <name> toggle | 123 | *output* <name> toggle |
113 | Toggle the specified output. | 124 | Toggle the specified output. |
114 | 125 | ||
115 | *output* <name> dpms on|off | 126 | *output* <name> power on|off|toggle |
116 | Enables or disables the specified output via DPMS. To turn an output off | 127 | Turns on or off the specified output. |
117 | (ie. blank the screen but keep workspaces as-is), one can set DPMS to off. | 128 | |
129 | As opposed to the _enable_ and _disable_ commands, the output keeps its | ||
130 | current workspaces and windows. | ||
131 | |||
132 | *output* <name> dpms on|off|toggle | ||
133 | Deprecated. Alias for _power_. | ||
118 | 134 | ||
119 | *output* <name> max_render_time off|<msec> | 135 | *output* <name> max_render_time off|<msec> |
120 | Controls when sway composites the output, as a positive number of | 136 | Controls when sway composites the output, as a positive number of |
@@ -142,11 +158,26 @@ must be separated by one space. For example: | |||
142 | Enables or disables adaptive synchronization (often referred to as Variable | 158 | Enables or disables adaptive synchronization (often referred to as Variable |
143 | Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). | 159 | Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). |
144 | 160 | ||
145 | Adaptive sync allows clients to submit frames a little to late without | 161 | Adaptive sync allows clients to submit frames a little too late without |
146 | having to wait a whole refresh period to display it on screen. Enabling | 162 | having to wait a whole refresh period to display it on screen. Enabling |
147 | adaptive sync can improve latency, but can cause flickering on some | 163 | adaptive sync can improve latency, but can cause flickering on some |
148 | hardware. | 164 | hardware. |
149 | 165 | ||
166 | *output* <name> render_bit_depth 8|10 | ||
167 | Controls the color channel bit depth at which frames are rendered; the | ||
168 | default is currently 8 bits per channel. | ||
169 | |||
170 | Setting higher values will not have an effect if hardware and software lack | ||
171 | support for such bit depths. Successfully increasing the render bit depth | ||
172 | will not necessarily increase the bit depth of the frames sent to a display. | ||
173 | An increased render bit depth may provide smoother rendering of gradients, | ||
174 | and screenshots which can more precisely store the colors of programs | ||
175 | which display high bit depth colors. | ||
176 | |||
177 | Warnings: this can break screenshot/screencast programs which have not been | ||
178 | updated to work with different bit depths. This command is experimental, | ||
179 | and may be removed or changed in the future. | ||
180 | |||
150 | # SEE ALSO | 181 | # SEE ALSO |
151 | 182 | ||
152 | *sway*(5) *sway-input*(5) | 183 | *sway*(5) *sway-input*(5) |
diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 02592b5f..9f823947 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd | |||
@@ -155,7 +155,7 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). | |||
155 | is specified, the view will be fullscreen across all outputs. | 155 | is specified, the view will be fullscreen across all outputs. |
156 | 156 | ||
157 | *gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current | 157 | *gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current |
158 | set|plus|minus <amount> | 158 | set|plus|minus|toggle <amount> |
159 | Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the | 159 | Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the |
160 | _current_ workspace. _outer_ gaps can be altered per side with _top_, | 160 | _current_ workspace. _outer_ gaps can be altered per side with _top_, |
161 | _right_, _bottom_, and _left_ or per direction with _horizontal_ and | 161 | _right_, _bottom_, and _left_ or per direction with _horizontal_ and |
@@ -176,6 +176,12 @@ set|plus|minus <amount> | |||
176 | *layout* default|splith|splitv|stacking|tabbed | 176 | *layout* default|splith|splitv|stacking|tabbed |
177 | Sets the layout mode of the focused container. | 177 | Sets the layout mode of the focused container. |
178 | 178 | ||
179 | When using the _stacking_ layout, only the focused window in the container is | ||
180 | displayed, with the opened windows' list on the top of the container. | ||
181 | |||
182 | The _tabbed_ layout is similar to _stacking_, but the windows’ list is vertically | ||
183 | split. | ||
184 | |||
179 | *layout* toggle [split|all] | 185 | *layout* toggle [split|all] |
180 | Cycles the layout mode of the focused container though a preset list of | 186 | Cycles the layout mode of the focused container though a preset list of |
181 | layouts. If no argument is given, then it cycles through stacking, tabbed | 187 | layouts. If no argument is given, then it cycles through stacking, tabbed |
@@ -210,15 +216,14 @@ set|plus|minus <amount> | |||
210 | further details. | 216 | further details. |
211 | 217 | ||
212 | *move* left|right|up|down [<px> px] | 218 | *move* left|right|up|down [<px> px] |
213 | Moves the focused container in the direction specified. If the container, | 219 | Moves the focused container in the direction specified. The optional _px_ |
214 | the optional _px_ argument specifies how many pixels to move the container. | 220 | argument specifies how many pixels to move the container. If unspecified, |
215 | If unspecified, the default is 10 pixels. Pixels are ignored when moving | 221 | the default is 10 pixels. Pixels are ignored when moving tiled containers. |
216 | tiled containers. | ||
217 | 222 | ||
218 | *move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ptt] | 223 | *move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt] |
219 | Moves the focused container to the specified position in the workspace. | 224 | Moves the focused container to the specified position in the workspace. |
220 | The position can be specified in pixels or percentage points, omitting | 225 | The position can be specified in pixels or percentage points, omitting |
221 | the unit defaults to pixels. If _absolute_ is used, the position is | 226 | the unit defaults to pixels. If _absolute_ is used, the position is |
222 | relative to all outputs. _absolute_ can not be used with percentage points. | 227 | relative to all outputs. _absolute_ can not be used with percentage points. |
223 | 228 | ||
224 | *move* [absolute] position center | 229 | *move* [absolute] position center |
@@ -319,8 +324,10 @@ set|plus|minus <amount> | |||
319 | established by the *seat* subcommand of the same name. See | 324 | established by the *seat* subcommand of the same name. See |
320 | *sway-input*(5) for more ways to affect inhibitors. | 325 | *sway-input*(5) for more ways to affect inhibitors. |
321 | 326 | ||
322 | *split* vertical|v|horizontal|h|toggle|t | 327 | *split* vertical|v|horizontal|h|none|n|toggle|t |
323 | Splits the current container, vertically or horizontally. When _toggle_ is | 328 | Splits the current container, vertically or horizontally. When _none_ is |
329 | specified, the effect of a previous split is undone if the current | ||
330 | container is the only child of a split parent. When _toggle_ is | ||
324 | specified, the current container is split opposite to the parent | 331 | specified, the current container is split opposite to the parent |
325 | container's layout. | 332 | container's layout. |
326 | 333 | ||
@@ -382,8 +389,8 @@ runtime. | |||
382 | for_window <criteria> move container to output <output> | 389 | for_window <criteria> move container to output <output> |
383 | 390 | ||
384 | *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ | 391 | *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ |
385 | [--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [Group<1-4>+]<key combo> \ | 392 | [--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \ |
386 | <command> | 393 | [Group<1-4>+]<key combo> <command> |
387 | Binds _key combo_ to execute the sway command _command_ when pressed. You | 394 | Binds _key combo_ to execute the sway command _command_ when pressed. You |
388 | may use XKB key names here (*wev*(1) is a good tool for discovering these). | 395 | may use XKB key names here (*wev*(1) is a good tool for discovering these). |
389 | With the flag _--release_, the command is executed when the key combo is | 396 | With the flag _--release_, the command is executed when the key combo is |
@@ -393,6 +400,12 @@ runtime. | |||
393 | only be available for that group. By default, if you overwrite a binding, | 400 | only be available for that group. By default, if you overwrite a binding, |
394 | swaynag will give you a warning. To silence this, use the _--no-warn_ flag. | 401 | swaynag will give you a warning. To silence this, use the _--no-warn_ flag. |
395 | 402 | ||
403 | For specifying modifier keys, you can use the XKB modifier names _Shift_, | ||
404 | _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock), | ||
405 | _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for | ||
406 | AltGr). In addition, you can use the aliases _Ctrl_ (for Control), _Alt_ | ||
407 | (for Alt), and _Super_ (for the Logo key). | ||
408 | |||
396 | Unless the flag _--locked_ is set, the command will not be run when a | 409 | Unless the flag _--locked_ is set, the command will not be run when a |
397 | screen locking program is active. If there is a matching binding with | 410 | screen locking program is active. If there is a matching binding with |
398 | and without _--locked_, the one with will be preferred when locked and the | 411 | and without _--locked_, the one with will be preferred when locked and the |
@@ -404,7 +417,7 @@ runtime. | |||
404 | a keyboard shortcuts inhibitor is active for the currently focused | 417 | a keyboard shortcuts inhibitor is active for the currently focused |
405 | window. Such inhibitors are usually requested by remote desktop and | 418 | window. Such inhibitors are usually requested by remote desktop and |
406 | virtualization software to enable the user to send keyboard shortcuts | 419 | virtualization software to enable the user to send keyboard shortcuts |
407 | to the remote or virtual session. The _--inhibited_ flag allows to | 420 | to the remote or virtual session. The _--inhibited_ flag allows one to |
408 | define bindings which will be exempt from pass-through to such | 421 | define bindings which will be exempt from pass-through to such |
409 | software. The same preference logic as for _--locked_ applies. | 422 | software. The same preference logic as for _--locked_ applies. |
410 | 423 | ||
@@ -447,7 +460,8 @@ runtime. | |||
447 | ``` | 460 | ``` |
448 | 461 | ||
449 | *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ | 462 | *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ |
450 | [--locked] [--input-device=<device>] [--no-warn] [Group<1-4>+]<code> <command> | 463 | [--locked] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \ |
464 | [Group<1-4>+]<code> <command> | ||
451 | is also available for binding with key/button codes instead of key/button names. | 465 | is also available for binding with key/button codes instead of key/button names. |
452 | 466 | ||
453 | *bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> | 467 | *bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> |
@@ -480,6 +494,62 @@ runtime. | |||
480 | bindswitch lid:toggle exec echo "Lid moved" | 494 | bindswitch lid:toggle exec echo "Lid moved" |
481 | ``` | 495 | ``` |
482 | 496 | ||
497 | *bindgesture* [--exact] [--input-device=<device>] [--no-warn] \ | ||
498 | <gesture>[:<fingers>][:directions] <command> | ||
499 | Binds _gesture_ to execute the sway command _command_ when detected. | ||
500 | Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally | ||
501 | can be limited to bind to a certain number of _fingers_ or, for a | ||
502 | _pinch_ or _swipe_ gesture, to certain _directions_. | ||
503 | |||
504 | [[ *type* | ||
505 | :[ *fingers* | ||
506 | :< *direction* | ||
507 | | hold | ||
508 | :- 1 - 5 | ||
509 | : none | ||
510 | | swipe | ||
511 | : 3 - 5 | ||
512 | : up, down, left, right | ||
513 | | pinch | ||
514 | : 2 - 5 | ||
515 | : all above + inward, outward, clockwise, counterclockwise | ||
516 | |||
517 | The _fingers_ can be limited to any sensible number or left empty to accept | ||
518 | any finger counts. | ||
519 | Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_, | ||
520 | _outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture. | ||
521 | Multiple directions can be combined by a plus. | ||
522 | |||
523 | If a _input-device_ is given, the binding will only be executed for | ||
524 | that input device and will be executed instead of any binding that is | ||
525 | generic to all devices. By default, if you overwrite a binding, | ||
526 | swaynag will give you a warning. To silence this, use the _--no-warn_ flag. | ||
527 | |||
528 | The _--exact_ flag can be used to ensure a binding only matches when exactly | ||
529 | all specified directions are matched and nothing more. If there is matching | ||
530 | binding with _--exact_, it will be preferred. | ||
531 | |||
532 | The priority for matching bindings is as follows: input device, then | ||
533 | exact matches followed by matches with the highest number of matching | ||
534 | directions. | ||
535 | |||
536 | Gestures executed while the pointer is above a bar are not handled by sway. | ||
537 | See the respective documentation, e.g. *bindgesture* in *sway-bar*(5). | ||
538 | |||
539 | Example: | ||
540 | ``` | ||
541 | # Allow switching between workspaces with left and right swipes | ||
542 | bindgesture swipe:right workspace prev | ||
543 | bindgesture swipe:left workspace next | ||
544 | |||
545 | # Allow container movements by pinching them | ||
546 | bindgesture pinch:inward+up move up | ||
547 | bindgesture pinch:inward+down move down | ||
548 | bindgesture pinch:inward+left move left | ||
549 | bindgesture pinch:inward+right move right | ||
550 | |||
551 | ``` | ||
552 | |||
483 | *client.background* <color> | 553 | *client.background* <color> |
484 | This command is ignored and is only present for i3 compatibility. | 554 | This command is ignored and is only present for i3 compatibility. |
485 | 555 | ||
@@ -497,6 +567,12 @@ runtime. | |||
497 | *client.focused_inactive* | 567 | *client.focused_inactive* |
498 | The most recently focused view within a container which is not focused. | 568 | The most recently focused view within a container which is not focused. |
499 | 569 | ||
570 | *client.focused_tab_title* | ||
571 | A view that has focused descendant container. | ||
572 | Tab or stack container title that is the parent of the focused container | ||
573 | but is not directly focused. Defaults to focused_inactive if not | ||
574 | specified and does not use the indicator and child_border colors. | ||
575 | |||
500 | *client.placeholder* | 576 | *client.placeholder* |
501 | Ignored (present for i3 compatibility). | 577 | Ignored (present for i3 compatibility). |
502 | 578 | ||
@@ -552,6 +628,12 @@ The default colors are: | |||
552 | : #ffffff | 628 | : #ffffff |
553 | : #484e50 | 629 | : #484e50 |
554 | : #5f676a | 630 | : #5f676a |
631 | | *focused_tab_title* | ||
632 | : #333333 | ||
633 | : #5f676a | ||
634 | : #ffffff | ||
635 | : n/a | ||
636 | : n/a | ||
555 | | *unfocused* | 637 | | *unfocused* |
556 | : #333333 | 638 | : #333333 |
557 | : #222222 | 639 | : #222222 |
@@ -573,7 +655,8 @@ The default colors are: | |||
573 | 655 | ||
574 | 656 | ||
575 | *default_border* normal|none|pixel [<n>] | 657 | *default_border* normal|none|pixel [<n>] |
576 | Set default border style for new tiled windows. | 658 | Set default border style for new tiled windows. Config reload won't affect |
659 | existing windows, only newly created ones after the reload. | ||
577 | 660 | ||
578 | *default_floating_border* normal|none|pixel [<n>] | 661 | *default_floating_border* normal|none|pixel [<n>] |
579 | Set default border style for new floating windows. This only applies to | 662 | Set default border style for new floating windows. This only applies to |
@@ -606,11 +689,11 @@ The default colors are: | |||
606 | after switching between workspaces. | 689 | after switching between workspaces. |
607 | 690 | ||
608 | *focus_on_window_activation* smart|urgent|focus|none | 691 | *focus_on_window_activation* smart|urgent|focus|none |
609 | This option determines what to do when an xwayland client requests | 692 | This option determines what to do when a client requests window activation. |
610 | window activation. If set to _urgent_, the urgent state will be set | 693 | If set to _urgent_, the urgent state will be set for that window. If set to |
611 | for that window. If set to _focus_, the window will become focused. | 694 | _focus_, the window will become focused. If set to _smart_, the window will |
612 | If set to _smart_, the window will become focused only if it is already | 695 | become focused only if it is already visible, otherwise the urgent state |
613 | visible, otherwise the urgent state will be set. Default is _urgent_. | 696 | will be set. Default is _urgent_. |
614 | 697 | ||
615 | *focus_wrapping* yes|no|force|workspace | 698 | *focus_wrapping* yes|no|force|workspace |
616 | This option determines what to do when attempting to focus over the edge | 699 | This option determines what to do when attempting to focus over the edge |
@@ -630,14 +713,14 @@ The default colors are: | |||
630 | should be used instead. Regardless of whether pango markup is enabled, | 713 | should be used instead. Regardless of whether pango markup is enabled, |
631 | _font_ should be specified as a pango font description. For more | 714 | _font_ should be specified as a pango font description. For more |
632 | information on pango font descriptions, see | 715 | information on pango font descriptions, see |
633 | https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string | 716 | https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description |
634 | 717 | ||
635 | *force_display_urgency_hint* <timeout> [ms] | 718 | *force_display_urgency_hint* <timeout> [ms] |
636 | If an application on another workspace sets an urgency hint, switching to this | 719 | If an application on another workspace sets an urgency hint, switching to this |
637 | workspace may lead to immediate focus of the application, which also means the | 720 | workspace may lead to immediate focus of the application, which also means the |
638 | window decoration color would be immediately resetted to *client.focused*. This | 721 | window decoration color would be immediately reset to *client.focused*. This |
639 | may make it unnecessarily hard to tell which window originally raised the | 722 | may make it unnecessarily hard to tell which window originally raised the |
640 | event. This option allows to set a _timeout_ in ms to delay the urgency hint reset. | 723 | event. This option allows one to set a _timeout_ in ms to delay the urgency hint reset. |
641 | 724 | ||
642 | *titlebar_border_thickness* <thickness> | 725 | *titlebar_border_thickness* <thickness> |
643 | Thickness of the titlebar border in pixels | 726 | Thickness of the titlebar border in pixels |
@@ -690,9 +773,10 @@ The default colors are: | |||
690 | borders will only be enabled if the workspace has more than one visible | 773 | borders will only be enabled if the workspace has more than one visible |
691 | child and gaps equal to zero. | 774 | child and gaps equal to zero. |
692 | 775 | ||
693 | *smart_gaps* on|off | 776 | *smart_gaps* on|off|toggle|inverse_outer |
694 | If smart_gaps are _on_ gaps will only be enabled if a workspace has more | 777 | If smart_gaps are _on_ gaps will only be enabled if a workspace has more |
695 | than one child. | 778 | than one child. If smart_gaps are _inverse_outer_ outer gaps will only |
779 | be enabled if a workspace has exactly one child. | ||
696 | 780 | ||
697 | *mark* --add|--replace [--toggle] <identifier> | 781 | *mark* --add|--replace [--toggle] <identifier> |
698 | Marks are arbitrary labels that can be used to identify certain windows and | 782 | Marks are arbitrary labels that can be used to identify certain windows and |
@@ -731,6 +815,10 @@ The default colors are: | |||
731 | dialog will not be rendered. If _leave_fullscreen_, the view will exit | 815 | dialog will not be rendered. If _leave_fullscreen_, the view will exit |
732 | fullscreen mode and the dialog will be rendered. | 816 | fullscreen mode and the dialog will be rendered. |
733 | 817 | ||
818 | *primary_selection* enabled|disabled | ||
819 | Enable or disable the primary selection clipboard. May only be configured | ||
820 | at launch. Default is _enabled_. | ||
821 | |||
734 | *set* $<name> <value> | 822 | *set* $<name> <value> |
735 | Sets variable $_name_ to _value_. You can use the new variable in the | 823 | Sets variable $_name_ to _value_. You can use the new variable in the |
736 | arguments of future commands. When the variable is used, it can be escaped | 824 | arguments of future commands. When the variable is used, it can be escaped |
@@ -771,6 +859,11 @@ The default colors are: | |||
771 | *unbindswitch* <switch>:<state> | 859 | *unbindswitch* <switch>:<state> |
772 | Removes a binding for when <switch> changes to <state>. | 860 | Removes a binding for when <switch> changes to <state>. |
773 | 861 | ||
862 | *unbindgesture* [--exact] [--input-device=<device>] \ | ||
863 | <gesture>[:<fingers>][:directions] | ||
864 | Removes a binding for the specified _gesture_, _fingers_ | ||
865 | and _directions_ combination. | ||
866 | |||
774 | *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ | 867 | *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ |
775 | [--to-code] [--input-device=<device>] <key combo> | 868 | [--to-code] [--input-device=<device>] <key combo> |
776 | Removes the binding for _key combo_ that was previously bound with the | 869 | Removes the binding for _key combo_ that was previously bound with the |
@@ -876,6 +969,9 @@ properties in practice for your applications. | |||
876 | 969 | ||
877 | The following attributes may be matched with: | 970 | The following attributes may be matched with: |
878 | 971 | ||
972 | *all* | ||
973 | Matches all windows. | ||
974 | |||
879 | *app_id* | 975 | *app_id* |
880 | Compare value against the app id. Can be a regular expression. If value is | 976 | Compare value against the app id. Can be a regular expression. If value is |
881 | \_\_focused\_\_, then the app id must be the same as that of the currently | 977 | \_\_focused\_\_, then the app id must be the same as that of the currently |
@@ -884,7 +980,8 @@ The following attributes may be matched with: | |||
884 | *class* | 980 | *class* |
885 | Compare value against the window class. Can be a regular expression. If | 981 | Compare value against the window class. Can be a regular expression. If |
886 | value is \_\_focused\_\_, then the window class must be the same as that of | 982 | value is \_\_focused\_\_, then the window class must be the same as that of |
887 | the currently focused window. _class_ are specific to X11 applications. | 983 | the currently focused window. _class_ are specific to X11 applications and |
984 | require XWayland. | ||
888 | 985 | ||
889 | *con_id* | 986 | *con_id* |
890 | Compare against the internal container ID, which you can find via IPC. If | 987 | Compare against the internal container ID, which you can find via IPC. If |
@@ -898,12 +995,14 @@ The following attributes may be matched with: | |||
898 | Matches floating windows. | 995 | Matches floating windows. |
899 | 996 | ||
900 | *id* | 997 | *id* |
901 | Compare value against the X11 window ID. Must be numeric. | 998 | Compare value against the X11 window ID. Must be numeric. id is specific to |
999 | X11 applications and requires XWayland. | ||
902 | 1000 | ||
903 | *instance* | 1001 | *instance* |
904 | Compare value against the window instance. Can be a regular expression. If | 1002 | Compare value against the window instance. Can be a regular expression. If |
905 | value is \_\_focused\_\_, then the window instance must be the same as that | 1003 | value is \_\_focused\_\_, then the window instance must be the same as that |
906 | of the currently focused window. | 1004 | of the currently focused window. instance is specific to X11 applications and |
1005 | requires XWayland. | ||
907 | 1006 | ||
908 | *pid* | 1007 | *pid* |
909 | Compare value against the window's process ID. Must be numeric. | 1008 | Compare value against the window's process ID. Must be numeric. |
@@ -928,12 +1027,14 @@ The following attributes may be matched with: | |||
928 | *window_role* | 1027 | *window_role* |
929 | Compare against the window role (WM_WINDOW_ROLE). Can be a regular | 1028 | Compare against the window role (WM_WINDOW_ROLE). Can be a regular |
930 | expression. If value is \_\_focused\_\_, then the window role must be the | 1029 | expression. If value is \_\_focused\_\_, then the window role must be the |
931 | same as that of the currently focused window. | 1030 | same as that of the currently focused window. window_role is specific to X11 |
1031 | applications and requires XWayland. | ||
932 | 1032 | ||
933 | *window_type* | 1033 | *window_type* |
934 | Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values | 1034 | Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values |
935 | are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, | 1035 | are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, |
936 | popup_menu, tooltip and notification. | 1036 | popup_menu, tooltip and notification. window_type is specific to X11 |
1037 | applications and requires XWayland. | ||
937 | 1038 | ||
938 | *workspace* | 1039 | *workspace* |
939 | Compare against the workspace name for this view. Can be a regular | 1040 | Compare against the workspace name for this view. Can be a regular |
diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c new file mode 100644 index 00000000..5eba53ba --- /dev/null +++ b/sway/sway_text_node.c | |||
@@ -0,0 +1,302 @@ | |||
1 | #include <drm_fourcc.h> | ||
2 | #include <stdio.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <string.h> | ||
5 | #include <wlr/types/wlr_buffer.h> | ||
6 | #include <wlr/interfaces/wlr_buffer.h> | ||
7 | #include "cairo_util.h" | ||
8 | #include "log.h" | ||
9 | #include "pango.h" | ||
10 | #include "sway/config.h" | ||
11 | #include "sway/sway_text_node.h" | ||
12 | |||
13 | struct cairo_buffer { | ||
14 | struct wlr_buffer base; | ||
15 | cairo_surface_t *surface; | ||
16 | cairo_t *cairo; | ||
17 | }; | ||
18 | |||
19 | static void cairo_buffer_handle_destroy(struct wlr_buffer *wlr_buffer) { | ||
20 | struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); | ||
21 | |||
22 | cairo_surface_destroy(buffer->surface); | ||
23 | cairo_destroy(buffer->cairo); | ||
24 | free(buffer); | ||
25 | } | ||
26 | |||
27 | static bool cairo_buffer_handle_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, | ||
28 | uint32_t flags, void **data, uint32_t *format, size_t *stride) { | ||
29 | struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); | ||
30 | *data = cairo_image_surface_get_data(buffer->surface); | ||
31 | *stride = cairo_image_surface_get_stride(buffer->surface); | ||
32 | *format = DRM_FORMAT_ARGB8888; | ||
33 | return true; | ||
34 | } | ||
35 | |||
36 | static void cairo_buffer_handle_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { | ||
37 | // This space is intentionally left blank | ||
38 | } | ||
39 | |||
40 | static const struct wlr_buffer_impl cairo_buffer_impl = { | ||
41 | .destroy = cairo_buffer_handle_destroy, | ||
42 | .begin_data_ptr_access = cairo_buffer_handle_begin_data_ptr_access, | ||
43 | .end_data_ptr_access = cairo_buffer_handle_end_data_ptr_access, | ||
44 | }; | ||
45 | |||
46 | struct text_buffer { | ||
47 | struct wlr_scene_buffer *buffer_node; | ||
48 | char *text; | ||
49 | struct sway_text_node props; | ||
50 | |||
51 | bool visible; | ||
52 | float scale; | ||
53 | enum wl_output_subpixel subpixel; | ||
54 | |||
55 | struct wl_listener outputs_update; | ||
56 | struct wl_listener destroy; | ||
57 | }; | ||
58 | |||
59 | static int get_text_width(struct sway_text_node *props) { | ||
60 | int width = props->width; | ||
61 | if (props->max_width) { | ||
62 | width = MIN(width, props->max_width); | ||
63 | } | ||
64 | return MAX(width, 0); | ||
65 | } | ||
66 | |||
67 | static void update_source_box(struct text_buffer *buffer) { | ||
68 | struct sway_text_node *props = &buffer->props; | ||
69 | struct wlr_fbox source_box = { | ||
70 | .x = 0, | ||
71 | .y = 0, | ||
72 | .width = ceil(get_text_width(props) * buffer->scale), | ||
73 | .height = ceil(props->height * buffer->scale), | ||
74 | }; | ||
75 | |||
76 | wlr_scene_buffer_set_source_box(buffer->buffer_node, &source_box); | ||
77 | } | ||
78 | |||
79 | static void render_backing_buffer(struct text_buffer *buffer) { | ||
80 | if (!buffer->visible) { | ||
81 | return; | ||
82 | } | ||
83 | |||
84 | float scale = buffer->scale; | ||
85 | int width = ceil(buffer->props.width * scale); | ||
86 | int height = ceil(buffer->props.height * scale); | ||
87 | float *color = (float *)&buffer->props.color; | ||
88 | float *background = (float *)&buffer->props.background; | ||
89 | PangoContext *pango = NULL; | ||
90 | |||
91 | cairo_font_options_t *fo = cairo_font_options_create(); | ||
92 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
93 | enum wl_output_subpixel subpixel = buffer->subpixel; | ||
94 | if (subpixel == WL_OUTPUT_SUBPIXEL_NONE || subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { | ||
95 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); | ||
96 | } else { | ||
97 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | ||
98 | cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(subpixel)); | ||
99 | } | ||
100 | |||
101 | cairo_surface_t *surface = cairo_image_surface_create( | ||
102 | CAIRO_FORMAT_ARGB32, width, height); | ||
103 | cairo_status_t status = cairo_surface_status(surface); | ||
104 | if (status != CAIRO_STATUS_SUCCESS) { | ||
105 | sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", | ||
106 | cairo_status_to_string(status)); | ||
107 | goto err; | ||
108 | } | ||
109 | |||
110 | struct cairo_buffer *cairo_buffer = calloc(1, sizeof(*cairo_buffer)); | ||
111 | if (!cairo_buffer) { | ||
112 | sway_log(SWAY_ERROR, "cairo_buffer allocation failed"); | ||
113 | goto err; | ||
114 | } | ||
115 | |||
116 | cairo_t *cairo = cairo_create(surface); | ||
117 | if (!cairo) { | ||
118 | sway_log(SWAY_ERROR, "cairo_create failed"); | ||
119 | free(cairo_buffer); | ||
120 | goto err; | ||
121 | } | ||
122 | |||
123 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
124 | cairo_set_font_options(cairo, fo); | ||
125 | pango = pango_cairo_create_context(cairo); | ||
126 | |||
127 | cairo_set_source_rgba(cairo, background[0], background[1], background[2], background[3]); | ||
128 | cairo_rectangle(cairo, 0, 0, width, height); | ||
129 | cairo_fill(cairo); | ||
130 | |||
131 | cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]); | ||
132 | cairo_move_to(cairo, 0, (config->font_baseline - buffer->props.baseline) * scale); | ||
133 | |||
134 | render_text(cairo, config->font_description, scale, buffer->props.pango_markup, | ||
135 | "%s", buffer->text); | ||
136 | |||
137 | cairo_surface_flush(surface); | ||
138 | |||
139 | wlr_buffer_init(&cairo_buffer->base, &cairo_buffer_impl, width, height); | ||
140 | cairo_buffer->surface = surface; | ||
141 | cairo_buffer->cairo = cairo; | ||
142 | |||
143 | wlr_scene_buffer_set_buffer(buffer->buffer_node, &cairo_buffer->base); | ||
144 | wlr_buffer_drop(&cairo_buffer->base); | ||
145 | update_source_box(buffer); | ||
146 | |||
147 | pixman_region32_t opaque; | ||
148 | pixman_region32_init(&opaque); | ||
149 | if (background[3] == 1) { | ||
150 | pixman_region32_union_rect(&opaque, &opaque, 0, 0, | ||
151 | buffer->props.width, buffer->props.height); | ||
152 | } | ||
153 | wlr_scene_buffer_set_opaque_region(buffer->buffer_node, &opaque); | ||
154 | pixman_region32_fini(&opaque); | ||
155 | |||
156 | err: | ||
157 | if (pango) g_object_unref(pango); | ||
158 | cairo_font_options_destroy(fo); | ||
159 | } | ||
160 | |||
161 | static void handle_outputs_update(struct wl_listener *listener, void *data) { | ||
162 | struct text_buffer *buffer = wl_container_of(listener, buffer, outputs_update); | ||
163 | struct wlr_scene_outputs_update_event *event = data; | ||
164 | |||
165 | float scale = 0; | ||
166 | enum wl_output_subpixel subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | ||
167 | |||
168 | for (size_t i = 0; i < event->size; i++) { | ||
169 | struct wlr_scene_output *output = event->active[i]; | ||
170 | if (subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { | ||
171 | subpixel = output->output->subpixel; | ||
172 | } else if (subpixel != output->output->subpixel) { | ||
173 | subpixel = WL_OUTPUT_SUBPIXEL_NONE; | ||
174 | } | ||
175 | |||
176 | if (scale != 0 && scale != output->output->scale) { | ||
177 | // drop down to gray scale if we encounter outputs with different | ||
178 | // scales or else we will have chromatic aberations | ||
179 | subpixel = WL_OUTPUT_SUBPIXEL_NONE; | ||
180 | } | ||
181 | |||
182 | if (scale < output->output->scale) { | ||
183 | scale = output->output->scale; | ||
184 | } | ||
185 | } | ||
186 | |||
187 | buffer->visible = event->size > 0; | ||
188 | |||
189 | if (scale != buffer->scale || subpixel != buffer->subpixel) { | ||
190 | buffer->scale = scale; | ||
191 | buffer->subpixel = subpixel; | ||
192 | render_backing_buffer(buffer); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
197 | struct text_buffer *buffer = wl_container_of(listener, buffer, destroy); | ||
198 | |||
199 | wl_list_remove(&buffer->outputs_update.link); | ||
200 | wl_list_remove(&buffer->destroy.link); | ||
201 | |||
202 | free(buffer->text); | ||
203 | free(buffer); | ||
204 | } | ||
205 | |||
206 | static void text_calc_size(struct text_buffer *buffer) { | ||
207 | struct sway_text_node *props = &buffer->props; | ||
208 | |||
209 | cairo_t *c = cairo_create(NULL); | ||
210 | if (!c) { | ||
211 | sway_log(SWAY_ERROR, "cairo_t allocation failed"); | ||
212 | return; | ||
213 | } | ||
214 | |||
215 | cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); | ||
216 | get_text_size(c, config->font_description, &props->width, NULL, | ||
217 | &props->baseline, 1, props->pango_markup, "%s", buffer->text); | ||
218 | cairo_destroy(c); | ||
219 | |||
220 | wlr_scene_buffer_set_dest_size(buffer->buffer_node, | ||
221 | get_text_width(props), props->height); | ||
222 | } | ||
223 | |||
224 | struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, | ||
225 | char *text, float color[4], bool pango_markup) { | ||
226 | struct text_buffer *buffer = calloc(1, sizeof(*buffer)); | ||
227 | if (!buffer) { | ||
228 | return NULL; | ||
229 | } | ||
230 | |||
231 | struct wlr_scene_buffer *node = wlr_scene_buffer_create(parent, NULL); | ||
232 | if (!node) { | ||
233 | free(buffer); | ||
234 | return NULL; | ||
235 | } | ||
236 | |||
237 | buffer->buffer_node = node; | ||
238 | buffer->props.node = &node->node; | ||
239 | buffer->text = strdup(text); | ||
240 | if (!buffer->text) { | ||
241 | free(buffer); | ||
242 | wlr_scene_node_destroy(&node->node); | ||
243 | return NULL; | ||
244 | } | ||
245 | |||
246 | buffer->props.height = config->font_height; | ||
247 | buffer->props.pango_markup = pango_markup; | ||
248 | memcpy(&buffer->props.color, color, sizeof(*color) * 4); | ||
249 | |||
250 | buffer->destroy.notify = handle_destroy; | ||
251 | wl_signal_add(&node->node.events.destroy, &buffer->destroy); | ||
252 | buffer->outputs_update.notify = handle_outputs_update; | ||
253 | wl_signal_add(&node->events.outputs_update, &buffer->outputs_update); | ||
254 | |||
255 | text_calc_size(buffer); | ||
256 | |||
257 | return &buffer->props; | ||
258 | } | ||
259 | |||
260 | void sway_text_node_set_color(struct sway_text_node *node, float color[4]) { | ||
261 | if (memcmp(&node->color, color, sizeof(*color) * 4) == 0) { | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | memcpy(&node->color, color, sizeof(*color) * 4); | ||
266 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
267 | |||
268 | render_backing_buffer(buffer); | ||
269 | } | ||
270 | |||
271 | void sway_text_node_set_text(struct sway_text_node *node, char *text) { | ||
272 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
273 | if (strcmp(buffer->text, text) == 0) { | ||
274 | return; | ||
275 | } | ||
276 | |||
277 | char *new_text = strdup(text); | ||
278 | if (!new_text) { | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | free(buffer->text); | ||
283 | buffer->text = new_text; | ||
284 | |||
285 | text_calc_size(buffer); | ||
286 | render_backing_buffer(buffer); | ||
287 | } | ||
288 | |||
289 | void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) { | ||
290 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
291 | buffer->props.max_width = max_width; | ||
292 | wlr_scene_buffer_set_dest_size(buffer->buffer_node, | ||
293 | get_text_width(&buffer->props), buffer->props.height); | ||
294 | update_source_box(buffer); | ||
295 | render_backing_buffer(buffer); | ||
296 | } | ||
297 | |||
298 | void sway_text_node_set_background(struct sway_text_node *node, float background[4]) { | ||
299 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
300 | memcpy(&node->background, background, sizeof(*background) * 4); | ||
301 | render_backing_buffer(buffer); | ||
302 | } | ||
diff --git a/sway/swaynag.c b/sway/swaynag.c index db5a919a..bc5e23ea 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <signal.h> | 1 | #include <signal.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -64,6 +63,8 @@ bool swaynag_spawn(const char *swaynag_command, | |||
64 | sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); | 63 | sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); |
65 | goto failed; | 64 | goto failed; |
66 | } else if (pid == 0) { | 65 | } else if (pid == 0) { |
66 | restore_nofile_limit(); | ||
67 | |||
67 | pid = fork(); | 68 | pid = fork(); |
68 | if (pid < 0) { | 69 | if (pid < 0) { |
69 | sway_log_errno(SWAY_ERROR, "fork failed"); | 70 | sway_log_errno(SWAY_ERROR, "fork failed"); |
@@ -87,8 +88,8 @@ bool swaynag_spawn(const char *swaynag_command, | |||
87 | size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; | 88 | size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; |
88 | char *cmd = malloc(length); | 89 | char *cmd = malloc(length); |
89 | snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); | 90 | snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); |
90 | execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); | 91 | execlp("sh", "sh", "-c", cmd, NULL); |
91 | sway_log_errno(SWAY_ERROR, "execl failed"); | 92 | sway_log_errno(SWAY_ERROR, "execlp failed"); |
92 | _exit(EXIT_FAILURE); | 93 | _exit(EXIT_FAILURE); |
93 | } | 94 | } |
94 | _exit(EXIT_SUCCESS); | 95 | _exit(EXIT_SUCCESS); |
@@ -143,22 +144,16 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, | |||
143 | 144 | ||
144 | va_list args; | 145 | va_list args; |
145 | va_start(args, fmt); | 146 | va_start(args, fmt); |
146 | size_t length = vsnprintf(NULL, 0, fmt, args) + 1; | 147 | char *str = vformat_str(fmt, args); |
147 | va_end(args); | 148 | va_end(args); |
148 | 149 | if (!str) { | |
149 | char *temp = malloc(length + 1); | ||
150 | if (!temp) { | ||
151 | sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry."); | 150 | sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry."); |
152 | return; | 151 | return; |
153 | } | 152 | } |
154 | 153 | ||
155 | va_start(args, fmt); | 154 | write(swaynag->fd[1], str, strlen(str)); |
156 | vsnprintf(temp, length, fmt, args); | ||
157 | va_end(args); | ||
158 | |||
159 | write(swaynag->fd[1], temp, length); | ||
160 | 155 | ||
161 | free(temp); | 156 | free(str); |
162 | } | 157 | } |
163 | 158 | ||
164 | void swaynag_show(struct swaynag_instance *swaynag) { | 159 | void swaynag_show(struct swaynag_instance *swaynag) { |
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index bac9f2fa..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> |
@@ -55,7 +54,7 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { | |||
55 | // Calculate gap size | 54 | // Calculate gap size |
56 | double inner_gap = 0; | 55 | double inner_gap = 0; |
57 | struct sway_container *child = children->items[0]; | 56 | struct sway_container *child = children->items[0]; |
58 | struct sway_workspace *ws = child->workspace; | 57 | struct sway_workspace *ws = child->pending.workspace; |
59 | if (ws) { | 58 | if (ws) { |
60 | inner_gap = ws->gaps_inner; | 59 | inner_gap = ws->gaps_inner; |
61 | } | 60 | } |
@@ -66,7 +65,7 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { | |||
66 | if (layout == L_TABBED || layout == L_STACKED) { | 65 | if (layout == L_TABBED || layout == L_STACKED) { |
67 | inner_gap = 0; | 66 | inner_gap = 0; |
68 | } | 67 | } |
69 | temp = temp->parent; | 68 | temp = temp->pending.parent; |
70 | } | 69 | } |
71 | double total_gap = fmin(inner_gap * (children->length - 1), | 70 | double total_gap = fmin(inner_gap * (children->length - 1), |
72 | fmax(0, parent->width - MIN_SANE_W * children->length)); | 71 | fmax(0, parent->width - MIN_SANE_W * children->length)); |
@@ -79,15 +78,15 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { | |||
79 | for (int i = 0; i < children->length; ++i) { | 78 | for (int i = 0; i < children->length; ++i) { |
80 | struct sway_container *child = children->items[i]; | 79 | struct sway_container *child = children->items[i]; |
81 | child->child_total_width = child_total_width; | 80 | child->child_total_width = child_total_width; |
82 | child->x = child_x; | 81 | child->pending.x = child_x; |
83 | child->y = parent->y; | 82 | child->pending.y = parent->y; |
84 | child->width = round(child->width_fraction * child_total_width); | 83 | child->pending.width = round(child->width_fraction * child_total_width); |
85 | child->height = parent->height; | 84 | child->pending.height = parent->height; |
86 | child_x += child->width + inner_gap; | 85 | child_x += child->pending.width + inner_gap; |
87 | 86 | ||
88 | // Make last child use remaining width of parent | 87 | // Make last child use remaining width of parent |
89 | if (i == children->length - 1) { | 88 | if (i == children->length - 1) { |
90 | child->width = parent->x + parent->width - child->x; | 89 | child->pending.width = parent->x + parent->width - child->pending.x; |
91 | } | 90 | } |
92 | } | 91 | } |
93 | } | 92 | } |
@@ -134,7 +133,7 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) { | |||
134 | // Calculate gap size | 133 | // Calculate gap size |
135 | double inner_gap = 0; | 134 | double inner_gap = 0; |
136 | struct sway_container *child = children->items[0]; | 135 | struct sway_container *child = children->items[0]; |
137 | struct sway_workspace *ws = child->workspace; | 136 | struct sway_workspace *ws = child->pending.workspace; |
138 | if (ws) { | 137 | if (ws) { |
139 | inner_gap = ws->gaps_inner; | 138 | inner_gap = ws->gaps_inner; |
140 | } | 139 | } |
@@ -145,7 +144,7 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) { | |||
145 | if (layout == L_TABBED || layout == L_STACKED) { | 144 | if (layout == L_TABBED || layout == L_STACKED) { |
146 | inner_gap = 0; | 145 | inner_gap = 0; |
147 | } | 146 | } |
148 | temp = temp->parent; | 147 | temp = temp->pending.parent; |
149 | } | 148 | } |
150 | double total_gap = fmin(inner_gap * (children->length - 1), | 149 | double total_gap = fmin(inner_gap * (children->length - 1), |
151 | fmax(0, parent->height - MIN_SANE_H * children->length)); | 150 | fmax(0, parent->height - MIN_SANE_H * children->length)); |
@@ -158,15 +157,15 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) { | |||
158 | for (int i = 0; i < children->length; ++i) { | 157 | for (int i = 0; i < children->length; ++i) { |
159 | struct sway_container *child = children->items[i]; | 158 | struct sway_container *child = children->items[i]; |
160 | child->child_total_height = child_total_height; | 159 | child->child_total_height = child_total_height; |
161 | child->x = parent->x; | 160 | child->pending.x = parent->x; |
162 | child->y = child_y; | 161 | child->pending.y = child_y; |
163 | child->width = parent->width; | 162 | child->pending.width = parent->width; |
164 | child->height = round(child->height_fraction * child_total_height); | 163 | child->pending.height = round(child->height_fraction * child_total_height); |
165 | child_y += child->height + inner_gap; | 164 | child_y += child->pending.height + inner_gap; |
166 | 165 | ||
167 | // Make last child use remaining height of parent | 166 | // Make last child use remaining height of parent |
168 | if (i == children->length - 1) { | 167 | if (i == children->length - 1) { |
169 | child->height = parent->y + parent->height - child->y; | 168 | child->pending.height = parent->y + parent->height - child->pending.y; |
170 | } | 169 | } |
171 | } | 170 | } |
172 | } | 171 | } |
@@ -178,10 +177,10 @@ static void apply_tabbed_layout(list_t *children, struct wlr_box *parent) { | |||
178 | for (int i = 0; i < children->length; ++i) { | 177 | for (int i = 0; i < children->length; ++i) { |
179 | struct sway_container *child = children->items[i]; | 178 | struct sway_container *child = children->items[i]; |
180 | int parent_offset = child->view ? 0 : container_titlebar_height(); | 179 | int parent_offset = child->view ? 0 : container_titlebar_height(); |
181 | child->x = parent->x; | 180 | child->pending.x = parent->x; |
182 | child->y = parent->y + parent_offset; | 181 | child->pending.y = parent->y + parent_offset; |
183 | child->width = parent->width; | 182 | child->pending.width = parent->width; |
184 | child->height = parent->height - parent_offset; | 183 | child->pending.height = parent->height - parent_offset; |
185 | } | 184 | } |
186 | } | 185 | } |
187 | 186 | ||
@@ -193,10 +192,10 @@ static void apply_stacked_layout(list_t *children, struct wlr_box *parent) { | |||
193 | struct sway_container *child = children->items[i]; | 192 | struct sway_container *child = children->items[i]; |
194 | int parent_offset = child->view ? 0 : | 193 | int parent_offset = child->view ? 0 : |
195 | container_titlebar_height() * children->length; | 194 | container_titlebar_height() * children->length; |
196 | child->x = parent->x; | 195 | child->pending.x = parent->x; |
197 | child->y = parent->y + parent_offset; | 196 | child->pending.y = parent->y + parent_offset; |
198 | child->width = parent->width; | 197 | child->pending.width = parent->width; |
199 | child->height = parent->height - parent_offset; | 198 | child->pending.height = parent->height - parent_offset; |
200 | } | 199 | } |
201 | } | 200 | } |
202 | 201 | ||
@@ -246,7 +245,7 @@ void arrange_container(struct sway_container *container) { | |||
246 | } | 245 | } |
247 | struct wlr_box box; | 246 | struct wlr_box box; |
248 | container_get_box(container, &box); | 247 | container_get_box(container, &box); |
249 | arrange_children(container->children, container->layout, &box); | 248 | arrange_children(container->pending.children, container->pending.layout, &box); |
250 | node_set_dirty(&container->node); | 249 | node_set_dirty(&container->node); |
251 | } | 250 | } |
252 | 251 | ||
@@ -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->x + floater->width / 2; | ||
282 | double center_y = floater->y + floater->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 | } |
@@ -294,10 +297,10 @@ void arrange_workspace(struct sway_workspace *workspace) { | |||
294 | workspace->x, workspace->y); | 297 | workspace->x, workspace->y); |
295 | if (workspace->fullscreen) { | 298 | if (workspace->fullscreen) { |
296 | struct sway_container *fs = workspace->fullscreen; | 299 | struct sway_container *fs = workspace->fullscreen; |
297 | fs->x = output->lx; | 300 | fs->pending.x = output->lx; |
298 | fs->y = output->ly; | 301 | fs->pending.y = output->ly; |
299 | fs->width = output->width; | 302 | fs->pending.width = output->width; |
300 | fs->height = output->height; | 303 | fs->pending.height = output->height; |
301 | arrange_container(fs); | 304 | arrange_container(fs); |
302 | } else { | 305 | } else { |
303 | struct wlr_box box; | 306 | struct wlr_box box; |
@@ -311,12 +314,13 @@ void arrange_output(struct sway_output *output) { | |||
311 | if (config->reloading) { | 314 | if (config->reloading) { |
312 | return; | 315 | return; |
313 | } | 316 | } |
314 | const struct wlr_box *output_box = wlr_output_layout_get_box( | 317 | struct wlr_box output_box; |
315 | root->output_layout, output->wlr_output); | 318 | wlr_output_layout_get_box(root->output_layout, |
316 | output->lx = output_box->x; | 319 | output->wlr_output, &output_box); |
317 | output->ly = output_box->y; | 320 | output->lx = output_box.x; |
318 | output->width = output_box->width; | 321 | output->ly = output_box.y; |
319 | output->height = output_box->height; | 322 | output->width = output_box.width; |
323 | output->height = output_box.height; | ||
320 | 324 | ||
321 | for (int i = 0; i < output->workspaces->length; ++i) { | 325 | for (int i = 0; i < output->workspaces->length; ++i) { |
322 | struct sway_workspace *workspace = output->workspaces->items[i]; | 326 | struct sway_workspace *workspace = output->workspaces->items[i]; |
@@ -328,19 +332,19 @@ void arrange_root(void) { | |||
328 | if (config->reloading) { | 332 | if (config->reloading) { |
329 | return; | 333 | return; |
330 | } | 334 | } |
331 | const struct wlr_box *layout_box = | 335 | struct wlr_box layout_box; |
332 | wlr_output_layout_get_box(root->output_layout, NULL); | 336 | wlr_output_layout_get_box(root->output_layout, NULL, &layout_box); |
333 | root->x = layout_box->x; | 337 | root->x = layout_box.x; |
334 | root->y = layout_box->y; | 338 | root->y = layout_box.y; |
335 | root->width = layout_box->width; | 339 | root->width = layout_box.width; |
336 | root->height = layout_box->height; | 340 | root->height = layout_box.height; |
337 | 341 | ||
338 | if (root->fullscreen_global) { | 342 | if (root->fullscreen_global) { |
339 | struct sway_container *fs = root->fullscreen_global; | 343 | struct sway_container *fs = root->fullscreen_global; |
340 | fs->x = root->x; | 344 | fs->pending.x = root->x; |
341 | fs->y = root->y; | 345 | fs->pending.y = root->y; |
342 | fs->width = root->width; | 346 | fs->pending.width = root->width; |
343 | fs->height = root->height; | 347 | fs->pending.height = root->height; |
344 | arrange_container(fs); | 348 | arrange_container(fs); |
345 | } else { | 349 | } else { |
346 | for (int i = 0; i < root->outputs->length; ++i) { | 350 | for (int i = 0; i < root->outputs->length; ++i) { |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 6a9ce1c4..9224b4fb 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -1,28 +1,77 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include <drm_fourcc.h> | ||
3 | #include <stdint.h> | 3 | #include <stdint.h> |
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include <string.h> | ||
6 | #include <strings.h> | ||
7 | #include <wayland-server-core.h> | 5 | #include <wayland-server-core.h> |
6 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
7 | #include <wlr/types/wlr_linux_dmabuf_v1.h> | ||
8 | #include <wlr/types/wlr_output_layout.h> | 8 | #include <wlr/types/wlr_output_layout.h> |
9 | #include "cairo.h" | 9 | #include <wlr/types/wlr_subcompositor.h> |
10 | #include "pango.h" | 10 | #include "linux-dmabuf-unstable-v1-protocol.h" |
11 | #include "sway/config.h" | 11 | #include "sway/config.h" |
12 | #include "sway/desktop.h" | ||
13 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
14 | #include "sway/input/input-manager.h" | 13 | #include "sway/input/input-manager.h" |
15 | #include "sway/input/seat.h" | 14 | #include "sway/input/seat.h" |
16 | #include "sway/ipc-server.h" | 15 | #include "sway/ipc-server.h" |
16 | #include "sway/scene_descriptor.h" | ||
17 | #include "sway/sway_text_node.h" | ||
17 | #include "sway/output.h" | 18 | #include "sway/output.h" |
18 | #include "sway/server.h" | 19 | #include "sway/server.h" |
19 | #include "sway/tree/arrange.h" | 20 | #include "sway/tree/arrange.h" |
20 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
21 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
23 | #include "sway/xdg_decoration.h" | ||
22 | #include "list.h" | 24 | #include "list.h" |
23 | #include "log.h" | 25 | #include "log.h" |
24 | #include "stringop.h" | 26 | #include "stringop.h" |
25 | 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 | |||
26 | struct sway_container *container_create(struct sway_view *view) { | 75 | struct sway_container *container_create(struct sway_view *view) { |
27 | struct sway_container *c = calloc(1, sizeof(struct sway_container)); | 76 | struct sway_container *c = calloc(1, sizeof(struct sway_container)); |
28 | if (!c) { | 77 | if (!c) { |
@@ -30,23 +79,411 @@ struct sway_container *container_create(struct sway_view *view) { | |||
30 | return NULL; | 79 | return NULL; |
31 | } | 80 | } |
32 | node_init(&c->node, N_CONTAINER, c); | 81 | node_init(&c->node, N_CONTAINER, c); |
33 | c->layout = L_NONE; | 82 | |
34 | c->view = view; | 83 | // Container tree structure |
35 | 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 | } | ||
36 | 151 | ||
37 | if (!view) { | 152 | if (!view) { |
38 | c->children = create_list(); | 153 | c->pending.children = create_list(); |
39 | c->current.children = create_list(); | 154 | c->current.children = create_list(); |
40 | } | 155 | } |
156 | |||
157 | c->pending.layout = L_NONE; | ||
158 | c->view = view; | ||
159 | c->alpha = 1.0f; | ||
41 | c->marks = create_list(); | 160 | c->marks = create_list(); |
42 | c->outputs = create_list(); | ||
43 | 161 | ||
44 | wl_signal_init(&c->events.destroy); | 162 | wl_signal_init(&c->events.destroy); |
45 | 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); | ||
46 | 166 | ||
47 | return c; | 167 | return c; |
48 | } | 168 | } |
49 | 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 | |||
50 | void container_destroy(struct sway_container *con) { | 487 | void container_destroy(struct sway_container *con) { |
51 | if (!sway_assert(con->node.destroying, | 488 | if (!sway_assert(con->node.destroying, |
52 | "Tried to free container which wasn't marked as destroying")) { | 489 | "Tried to free container which wasn't marked as destroying")) { |
@@ -58,29 +495,21 @@ void container_destroy(struct sway_container *con) { | |||
58 | } | 495 | } |
59 | free(con->title); | 496 | free(con->title); |
60 | free(con->formatted_title); | 497 | free(con->formatted_title); |
61 | wlr_texture_destroy(con->title_focused); | 498 | list_free(con->pending.children); |
62 | wlr_texture_destroy(con->title_focused_inactive); | ||
63 | wlr_texture_destroy(con->title_unfocused); | ||
64 | wlr_texture_destroy(con->title_urgent); | ||
65 | list_free(con->children); | ||
66 | list_free(con->current.children); | 499 | list_free(con->current.children); |
67 | list_free(con->outputs); | ||
68 | 500 | ||
69 | list_free_items_and_destroy(con->marks); | 501 | list_free_items_and_destroy(con->marks); |
70 | wlr_texture_destroy(con->marks_focused); | ||
71 | wlr_texture_destroy(con->marks_focused_inactive); | ||
72 | wlr_texture_destroy(con->marks_unfocused); | ||
73 | wlr_texture_destroy(con->marks_urgent); | ||
74 | 502 | ||
75 | if (con->view) { | 503 | if (con->view && con->view->container == con) { |
76 | if (con->view->container == con) { | 504 | con->view->container = NULL; |
77 | con->view->container = NULL; | 505 | wlr_scene_node_destroy(&con->output_handler->node); |
78 | } | ||
79 | if (con->view->destroying) { | 506 | if (con->view->destroying) { |
80 | view_destroy(con->view); | 507 | view_destroy(con->view); |
81 | } | 508 | } |
82 | } | 509 | } |
83 | 510 | ||
511 | scene_node_disown_children(con->content_tree); | ||
512 | wlr_scene_node_destroy(&con->scene_tree->node); | ||
84 | free(con); | 513 | free(con); |
85 | } | 514 | } |
86 | 515 | ||
@@ -90,14 +519,14 @@ void container_begin_destroy(struct sway_container *con) { | |||
90 | } | 519 | } |
91 | // The workspace must have the fullscreen pointer cleared so that the | 520 | // The workspace must have the fullscreen pointer cleared so that the |
92 | // seat code can find an appropriate new focus. | 521 | // seat code can find an appropriate new focus. |
93 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) { | 522 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE && con->pending.workspace) { |
94 | con->workspace->fullscreen = NULL; | 523 | con->pending.workspace->fullscreen = NULL; |
95 | } | 524 | } |
96 | if (con->scratchpad && con->fullscreen_mode == FULLSCREEN_GLOBAL) { | 525 | if (con->scratchpad && con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
97 | container_fullscreen_disable(con); | 526 | container_fullscreen_disable(con); |
98 | } | 527 | } |
99 | 528 | ||
100 | wl_signal_emit(&con->node.events.destroy, &con->node); | 529 | wl_signal_emit_mutable(&con->node.events.destroy, &con->node); |
101 | 530 | ||
102 | container_end_mouse_operation(con); | 531 | container_end_mouse_operation(con); |
103 | 532 | ||
@@ -108,11 +537,11 @@ void container_begin_destroy(struct sway_container *con) { | |||
108 | root_scratchpad_remove_container(con); | 537 | root_scratchpad_remove_container(con); |
109 | } | 538 | } |
110 | 539 | ||
111 | if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { | 540 | if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
112 | container_fullscreen_disable(con); | 541 | container_fullscreen_disable(con); |
113 | } | 542 | } |
114 | 543 | ||
115 | if (con->parent || con->workspace) { | 544 | if (con->pending.parent || con->pending.workspace) { |
116 | container_detach(con); | 545 | container_detach(con); |
117 | } | 546 | } |
118 | } | 547 | } |
@@ -121,12 +550,12 @@ void container_reap_empty(struct sway_container *con) { | |||
121 | if (con->view) { | 550 | if (con->view) { |
122 | return; | 551 | return; |
123 | } | 552 | } |
124 | struct sway_workspace *ws = con->workspace; | 553 | struct sway_workspace *ws = con->pending.workspace; |
125 | while (con) { | 554 | while (con) { |
126 | if (con->children->length) { | 555 | if (con->pending.children->length) { |
127 | return; | 556 | return; |
128 | } | 557 | } |
129 | struct sway_container *parent = con->parent; | 558 | struct sway_container *parent = con->pending.parent; |
130 | container_begin_destroy(con); | 559 | container_begin_destroy(con); |
131 | con = parent; | 560 | con = parent; |
132 | } | 561 | } |
@@ -139,9 +568,9 @@ struct sway_container *container_flatten(struct sway_container *container) { | |||
139 | if (container->view) { | 568 | if (container->view) { |
140 | return NULL; | 569 | return NULL; |
141 | } | 570 | } |
142 | while (container && container->children->length == 1) { | 571 | while (container && container->pending.children->length == 1) { |
143 | struct sway_container *child = container->children->items[0]; | 572 | struct sway_container *child = container->pending.children->items[0]; |
144 | struct sway_container *parent = container->parent; | 573 | struct sway_container *parent = container->pending.parent; |
145 | container_replace(container, child); | 574 | container_replace(container, child); |
146 | container_begin_destroy(container); | 575 | container_begin_destroy(container); |
147 | container = parent; | 576 | container = parent; |
@@ -151,11 +580,11 @@ struct sway_container *container_flatten(struct sway_container *container) { | |||
151 | 580 | ||
152 | struct sway_container *container_find_child(struct sway_container *container, | 581 | struct sway_container *container_find_child(struct sway_container *container, |
153 | bool (*test)(struct sway_container *con, void *data), void *data) { | 582 | bool (*test)(struct sway_container *con, void *data), void *data) { |
154 | if (!container->children) { | 583 | if (!container->pending.children) { |
155 | return NULL; | 584 | return NULL; |
156 | } | 585 | } |
157 | for (int i = 0; i < container->children->length; ++i) { | 586 | for (int i = 0; i < container->pending.children->length; ++i) { |
158 | struct sway_container *child = container->children->items[i]; | 587 | struct sway_container *child = container->pending.children->items[i]; |
159 | if (test(child, data)) { | 588 | if (test(child, data)) { |
160 | return child; | 589 | return child; |
161 | } | 590 | } |
@@ -167,260 +596,44 @@ struct sway_container *container_find_child(struct sway_container *container, | |||
167 | return NULL; | 596 | return NULL; |
168 | } | 597 | } |
169 | 598 | ||
170 | static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly, | 599 | void container_for_each_child(struct sway_container *container, |
171 | struct wlr_surface **surface, double *sx, double *sy) { | 600 | void (*f)(struct sway_container *container, void *data), |
172 | if (!sway_assert(con->view, "Expected a view")) { | 601 | void *data) { |
173 | return NULL; | 602 | if (container->pending.children) { |
174 | } | 603 | for (int i = 0; i < container->pending.children->length; ++i) { |
175 | struct sway_view *view = con->view; | 604 | struct sway_container *child = container->pending.children->items[i]; |
176 | double view_sx = lx - con->surface_x + view->geometry.x; | 605 | f(child, data); |
177 | double view_sy = ly - con->surface_y + view->geometry.y; | 606 | container_for_each_child(child, f, data); |
178 | |||
179 | double _sx, _sy; | ||
180 | struct wlr_surface *_surface = NULL; | ||
181 | switch (view->type) { | ||
182 | #if HAVE_XWAYLAND | ||
183 | case SWAY_VIEW_XWAYLAND: | ||
184 | _surface = wlr_surface_surface_at(view->surface, | ||
185 | view_sx, view_sy, &_sx, &_sy); | ||
186 | break; | ||
187 | #endif | ||
188 | case SWAY_VIEW_XDG_SHELL: | ||
189 | _surface = wlr_xdg_surface_surface_at( | ||
190 | view->wlr_xdg_surface, | ||
191 | view_sx, view_sy, &_sx, &_sy); | ||
192 | break; | ||
193 | } | ||
194 | if (_surface) { | ||
195 | *sx = _sx; | ||
196 | *sy = _sy; | ||
197 | *surface = _surface; | ||
198 | return con; | ||
199 | } | ||
200 | return NULL; | ||
201 | } | ||
202 | |||
203 | /** | ||
204 | * container_at for a container with layout L_TABBED. | ||
205 | */ | ||
206 | static struct sway_container *container_at_tabbed(struct sway_node *parent, | ||
207 | double lx, double ly, | ||
208 | struct wlr_surface **surface, double *sx, double *sy) { | ||
209 | struct wlr_box box; | ||
210 | node_get_box(parent, &box); | ||
211 | if (lx < box.x || lx > box.x + box.width || | ||
212 | ly < box.y || ly > box.y + box.height) { | ||
213 | return NULL; | ||
214 | } | ||
215 | struct sway_seat *seat = input_manager_current_seat(); | ||
216 | list_t *children = node_get_children(parent); | ||
217 | if (!children->length) { | ||
218 | return NULL; | ||
219 | } | ||
220 | |||
221 | // Tab titles | ||
222 | int title_height = container_titlebar_height(); | ||
223 | if (ly < box.y + title_height) { | ||
224 | int tab_width = box.width / children->length; | ||
225 | int child_index = (lx - box.x) / tab_width; | ||
226 | if (child_index >= children->length) { | ||
227 | child_index = children->length - 1; | ||
228 | } | ||
229 | struct sway_container *child = children->items[child_index]; | ||
230 | return child; | ||
231 | } | ||
232 | |||
233 | // Surfaces | ||
234 | struct sway_node *current = seat_get_active_tiling_child(seat, parent); | ||
235 | return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; | ||
236 | } | ||
237 | |||
238 | /** | ||
239 | * container_at for a container with layout L_STACKED. | ||
240 | */ | ||
241 | static struct sway_container *container_at_stacked(struct sway_node *parent, | ||
242 | double lx, double ly, | ||
243 | struct wlr_surface **surface, double *sx, double *sy) { | ||
244 | struct wlr_box box; | ||
245 | node_get_box(parent, &box); | ||
246 | if (lx < box.x || lx > box.x + box.width || | ||
247 | ly < box.y || ly > box.y + box.height) { | ||
248 | return NULL; | ||
249 | } | ||
250 | struct sway_seat *seat = input_manager_current_seat(); | ||
251 | list_t *children = node_get_children(parent); | ||
252 | |||
253 | // Title bars | ||
254 | int title_height = container_titlebar_height(); | ||
255 | if (title_height > 0) { | ||
256 | int child_index = (ly - box.y) / title_height; | ||
257 | if (child_index < children->length) { | ||
258 | struct sway_container *child = children->items[child_index]; | ||
259 | return child; | ||
260 | } | 607 | } |
261 | } | 608 | } |
262 | |||
263 | // Surfaces | ||
264 | struct sway_node *current = seat_get_active_tiling_child(seat, parent); | ||
265 | return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; | ||
266 | } | 609 | } |
267 | 610 | ||
268 | /** | 611 | struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container) |
269 | * container_at for a container with layout L_HORIZ or L_VERT. | 612 | { |
270 | */ | 613 | struct sway_workspace *workspace = container->pending.workspace; |
271 | static struct sway_container *container_at_linear(struct sway_node *parent, | ||
272 | double lx, double ly, | ||
273 | struct wlr_surface **surface, double *sx, double *sy) { | ||
274 | list_t *children = node_get_children(parent); | ||
275 | for (int i = 0; i < children->length; ++i) { | ||
276 | struct sway_container *child = children->items[i]; | ||
277 | struct sway_container *container = | ||
278 | tiling_container_at(&child->node, lx, ly, surface, sx, sy); | ||
279 | if (container) { | ||
280 | return container; | ||
281 | } | ||
282 | } | ||
283 | return NULL; | ||
284 | } | ||
285 | 614 | ||
286 | static struct sway_container *floating_container_at(double lx, double ly, | 615 | if (workspace && workspace->fullscreen && !container_is_fullscreen_or_child(container)) { |
287 | struct wlr_surface **surface, double *sx, double *sy) { | 616 | if (container_is_transient_for(container, workspace->fullscreen)) { |
288 | // For outputs with floating containers that overhang the output bounds, | 617 | return NULL; |
289 | // those at the end of the output list appear on top of floating | ||
290 | // containers from other outputs, so iterate the list in reverse. | ||
291 | for (int i = root->outputs->length - 1; i >= 0; --i) { | ||
292 | struct sway_output *output = root->outputs->items[i]; | ||
293 | for (int j = 0; j < output->workspaces->length; ++j) { | ||
294 | struct sway_workspace *ws = output->workspaces->items[j]; | ||
295 | if (!workspace_is_visible(ws)) { | ||
296 | continue; | ||
297 | } | ||
298 | // Items at the end of the list are on top, so iterate the list in | ||
299 | // reverse. | ||
300 | for (int k = ws->floating->length - 1; k >= 0; --k) { | ||
301 | struct sway_container *floater = ws->floating->items[k]; | ||
302 | struct sway_container *container = | ||
303 | tiling_container_at(&floater->node, lx, ly, surface, sx, sy); | ||
304 | if (container) { | ||
305 | return container; | ||
306 | } | ||
307 | } | ||
308 | } | 618 | } |
309 | } | 619 | return workspace->fullscreen; |
310 | return NULL; | ||
311 | } | ||
312 | |||
313 | struct sway_container *view_container_at(struct sway_node *parent, | ||
314 | double lx, double ly, | ||
315 | struct wlr_surface **surface, double *sx, double *sy) { | ||
316 | if (!sway_assert(node_is_view(parent), "Expected a view")) { | ||
317 | return NULL; | ||
318 | } | ||
319 | |||
320 | struct sway_container *container = parent->sway_container; | ||
321 | struct wlr_box box = { | ||
322 | .x = container->x, | ||
323 | .y = container->y, | ||
324 | .width = container->width, | ||
325 | .height = container->height, | ||
326 | }; | ||
327 | |||
328 | if (wlr_box_contains_point(&box, lx, ly)) { | ||
329 | surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); | ||
330 | return container; | ||
331 | } | 620 | } |
332 | 621 | ||
333 | return NULL; | 622 | struct sway_container *fullscreen_global = root->fullscreen_global; |
334 | } | 623 | if (fullscreen_global && container != fullscreen_global && !container_has_ancestor(container, fullscreen_global)) { |
335 | 624 | if (container_is_transient_for(container, fullscreen_global)) { | |
336 | struct sway_container *tiling_container_at(struct sway_node *parent, | 625 | return NULL; |
337 | double lx, double ly, | ||
338 | struct wlr_surface **surface, double *sx, double *sy) { | ||
339 | if (node_is_view(parent)) { | ||
340 | return view_container_at(parent, lx, ly, surface, sx, sy); | ||
341 | } | ||
342 | if (!node_get_children(parent)) { | ||
343 | return NULL; | ||
344 | } | ||
345 | switch (node_get_layout(parent)) { | ||
346 | case L_HORIZ: | ||
347 | case L_VERT: | ||
348 | return container_at_linear(parent, lx, ly, surface, sx, sy); | ||
349 | case L_TABBED: | ||
350 | return container_at_tabbed(parent, lx, ly, surface, sx, sy); | ||
351 | case L_STACKED: | ||
352 | return container_at_stacked(parent, lx, ly, surface, sx, sy); | ||
353 | case L_NONE: | ||
354 | return NULL; | ||
355 | } | ||
356 | return NULL; | ||
357 | } | ||
358 | |||
359 | static bool surface_is_popup(struct wlr_surface *surface) { | ||
360 | if (wlr_surface_is_xdg_surface(surface)) { | ||
361 | struct wlr_xdg_surface *xdg_surface = | ||
362 | wlr_xdg_surface_from_wlr_surface(surface); | ||
363 | while (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) { | ||
364 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
365 | return true; | ||
366 | } | ||
367 | xdg_surface = xdg_surface->toplevel->parent; | ||
368 | } | 626 | } |
369 | return false; | 627 | return fullscreen_global; |
370 | } | 628 | } |
371 | 629 | ||
372 | return false; | ||
373 | } | ||
374 | |||
375 | struct sway_container *container_at(struct sway_workspace *workspace, | ||
376 | double lx, double ly, | ||
377 | struct wlr_surface **surface, double *sx, double *sy) { | ||
378 | struct sway_container *c; | ||
379 | |||
380 | struct sway_seat *seat = input_manager_current_seat(); | ||
381 | struct sway_container *focus = seat_get_focused_container(seat); | ||
382 | bool is_floating = focus && container_is_floating_or_child(focus); | ||
383 | // Focused view's popups | ||
384 | if (focus && focus->view) { | ||
385 | c = surface_at_view(focus, lx, ly, surface, sx, sy); | ||
386 | if (c && surface_is_popup(*surface)) { | ||
387 | return c; | ||
388 | } | ||
389 | *surface = NULL; | ||
390 | } | ||
391 | // Floating | ||
392 | if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) { | ||
393 | return c; | ||
394 | } | ||
395 | // Tiling (focused) | ||
396 | if (focus && focus->view && !is_floating) { | ||
397 | if ((c = surface_at_view(focus, lx, ly, surface, sx, sy))) { | ||
398 | return c; | ||
399 | } | ||
400 | } | ||
401 | // Tiling (non-focused) | ||
402 | if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { | ||
403 | return c; | ||
404 | } | ||
405 | return NULL; | 630 | return NULL; |
406 | } | 631 | } |
407 | 632 | ||
408 | void container_for_each_child(struct sway_container *container, | ||
409 | void (*f)(struct sway_container *container, void *data), | ||
410 | void *data) { | ||
411 | if (container->children) { | ||
412 | for (int i = 0; i < container->children->length; ++i) { | ||
413 | struct sway_container *child = container->children->items[i]; | ||
414 | f(child, data); | ||
415 | container_for_each_child(child, f, data); | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | |||
420 | bool container_has_ancestor(struct sway_container *descendant, | 633 | bool container_has_ancestor(struct sway_container *descendant, |
421 | struct sway_container *ancestor) { | 634 | struct sway_container *ancestor) { |
422 | while (descendant) { | 635 | while (descendant) { |
423 | descendant = descendant->parent; | 636 | descendant = descendant->pending.parent; |
424 | if (descendant == ancestor) { | 637 | if (descendant == ancestor) { |
425 | return true; | 638 | return true; |
426 | } | 639 | } |
@@ -428,119 +641,6 @@ bool container_has_ancestor(struct sway_container *descendant, | |||
428 | return false; | 641 | return false; |
429 | } | 642 | } |
430 | 643 | ||
431 | void container_damage_whole(struct sway_container *container) { | ||
432 | for (int i = 0; i < root->outputs->length; ++i) { | ||
433 | struct sway_output *output = root->outputs->items[i]; | ||
434 | output_damage_whole_container(output, container); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * Return the output which will be used for scale purposes. | ||
440 | * This is the most recently entered output. | ||
441 | */ | ||
442 | struct sway_output *container_get_effective_output(struct sway_container *con) { | ||
443 | if (con->outputs->length == 0) { | ||
444 | return NULL; | ||
445 | } | ||
446 | return con->outputs->items[con->outputs->length - 1]; | ||
447 | } | ||
448 | |||
449 | static void update_title_texture(struct sway_container *con, | ||
450 | struct wlr_texture **texture, struct border_colors *class) { | ||
451 | struct sway_output *output = container_get_effective_output(con); | ||
452 | if (!output) { | ||
453 | return; | ||
454 | } | ||
455 | if (*texture) { | ||
456 | wlr_texture_destroy(*texture); | ||
457 | *texture = NULL; | ||
458 | } | ||
459 | if (!con->formatted_title) { | ||
460 | return; | ||
461 | } | ||
462 | |||
463 | double scale = output->wlr_output->scale; | ||
464 | int width = 0; | ||
465 | int height = con->title_height * scale; | ||
466 | |||
467 | // We must use a non-nil cairo_t for cairo_set_font_options to work. | ||
468 | // Therefore, we cannot use cairo_create(NULL). | ||
469 | cairo_surface_t *dummy_surface = cairo_image_surface_create( | ||
470 | CAIRO_FORMAT_ARGB32, 0, 0); | ||
471 | cairo_t *c = cairo_create(dummy_surface); | ||
472 | cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); | ||
473 | cairo_font_options_t *fo = cairo_font_options_create(); | ||
474 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
475 | if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { | ||
476 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); | ||
477 | } else { | ||
478 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | ||
479 | cairo_font_options_set_subpixel_order(fo, | ||
480 | to_cairo_subpixel_order(output->wlr_output->subpixel)); | ||
481 | } | ||
482 | cairo_set_font_options(c, fo); | ||
483 | get_text_size(c, config->font, &width, NULL, NULL, scale, | ||
484 | config->pango_markup, "%s", con->formatted_title); | ||
485 | cairo_surface_destroy(dummy_surface); | ||
486 | cairo_destroy(c); | ||
487 | |||
488 | cairo_surface_t *surface = cairo_image_surface_create( | ||
489 | CAIRO_FORMAT_ARGB32, width, height); | ||
490 | cairo_t *cairo = cairo_create(surface); | ||
491 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
492 | cairo_set_font_options(cairo, fo); | ||
493 | cairo_font_options_destroy(fo); | ||
494 | cairo_set_source_rgba(cairo, class->background[0], class->background[1], | ||
495 | class->background[2], class->background[3]); | ||
496 | cairo_paint(cairo); | ||
497 | PangoContext *pango = pango_cairo_create_context(cairo); | ||
498 | cairo_set_source_rgba(cairo, class->text[0], class->text[1], | ||
499 | class->text[2], class->text[3]); | ||
500 | cairo_move_to(cairo, 0, 0); | ||
501 | |||
502 | pango_printf(cairo, config->font, scale, config->pango_markup, | ||
503 | "%s", con->formatted_title); | ||
504 | |||
505 | cairo_surface_flush(surface); | ||
506 | unsigned char *data = cairo_image_surface_get_data(surface); | ||
507 | int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); | ||
508 | struct wlr_renderer *renderer = wlr_backend_get_renderer( | ||
509 | output->wlr_output->backend); | ||
510 | *texture = wlr_texture_from_pixels( | ||
511 | renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); | ||
512 | cairo_surface_destroy(surface); | ||
513 | g_object_unref(pango); | ||
514 | cairo_destroy(cairo); | ||
515 | } | ||
516 | |||
517 | void container_update_title_textures(struct sway_container *container) { | ||
518 | update_title_texture(container, &container->title_focused, | ||
519 | &config->border_colors.focused); | ||
520 | update_title_texture(container, &container->title_focused_inactive, | ||
521 | &config->border_colors.focused_inactive); | ||
522 | update_title_texture(container, &container->title_unfocused, | ||
523 | &config->border_colors.unfocused); | ||
524 | update_title_texture(container, &container->title_urgent, | ||
525 | &config->border_colors.urgent); | ||
526 | container_damage_whole(container); | ||
527 | } | ||
528 | |||
529 | void container_calculate_title_height(struct sway_container *container) { | ||
530 | if (!container->formatted_title) { | ||
531 | container->title_height = 0; | ||
532 | return; | ||
533 | } | ||
534 | cairo_t *cairo = cairo_create(NULL); | ||
535 | int height; | ||
536 | int baseline; | ||
537 | get_text_size(cairo, config->font, NULL, &height, &baseline, 1, | ||
538 | config->pango_markup, "%s", container->formatted_title); | ||
539 | cairo_destroy(cairo); | ||
540 | container->title_height = height; | ||
541 | container->title_baseline = baseline; | ||
542 | } | ||
543 | |||
544 | /** | 644 | /** |
545 | * Calculate and return the length of the tree representation. | 645 | * Calculate and return the length of the tree representation. |
546 | * An example tree representation is: V[Terminal, Firefox] | 646 | * An example tree representation is: V[Terminal, Firefox] |
@@ -596,23 +696,28 @@ size_t container_build_representation(enum sway_container_layout layout, | |||
596 | 696 | ||
597 | void container_update_representation(struct sway_container *con) { | 697 | void container_update_representation(struct sway_container *con) { |
598 | if (!con->view) { | 698 | if (!con->view) { |
599 | size_t len = container_build_representation(con->layout, | 699 | size_t len = container_build_representation(con->pending.layout, |
600 | con->children, NULL); | 700 | con->pending.children, NULL); |
601 | free(con->formatted_title); | 701 | free(con->formatted_title); |
602 | con->formatted_title = calloc(len + 1, sizeof(char)); | 702 | con->formatted_title = calloc(len + 1, sizeof(char)); |
603 | if (!sway_assert(con->formatted_title, | 703 | if (!sway_assert(con->formatted_title, |
604 | "Unable to allocate title string")) { | 704 | "Unable to allocate title string")) { |
605 | return; | 705 | return; |
606 | } | 706 | } |
607 | container_build_representation(con->layout, con->children, | 707 | container_build_representation(con->pending.layout, con->pending.children, |
608 | con->formatted_title); | 708 | con->formatted_title); |
609 | container_calculate_title_height(con); | 709 | |
610 | container_update_title_textures(con); | 710 | if (con->title_bar.title_text) { |
711 | sway_text_node_set_text(con->title_bar.title_text, con->formatted_title); | ||
712 | container_arrange_title_bar(con); | ||
713 | } else { | ||
714 | container_update_title_bar(con); | ||
715 | } | ||
611 | } | 716 | } |
612 | if (con->parent) { | 717 | if (con->pending.parent) { |
613 | container_update_representation(con->parent); | 718 | container_update_representation(con->pending.parent); |
614 | } else if (con->workspace) { | 719 | } else if (con->pending.workspace) { |
615 | workspace_update_representation(con->workspace); | 720 | workspace_update_representation(con->pending.workspace); |
616 | } | 721 | } |
617 | } | 722 | } |
618 | 723 | ||
@@ -638,12 +743,13 @@ void floating_calculate_constraints(int *min_width, int *max_width, | |||
638 | *min_height = config->floating_minimum_height; | 743 | *min_height = config->floating_minimum_height; |
639 | } | 744 | } |
640 | 745 | ||
641 | struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); | 746 | struct wlr_box box; |
747 | wlr_output_layout_get_box(root->output_layout, NULL, &box); | ||
642 | 748 | ||
643 | if (config->floating_maximum_width == -1) { // no maximum | 749 | if (config->floating_maximum_width == -1) { // no maximum |
644 | *max_width = INT_MAX; | 750 | *max_width = INT_MAX; |
645 | } else if (config->floating_maximum_width == 0) { // automatic | 751 | } else if (config->floating_maximum_width == 0) { // automatic |
646 | *max_width = box->width; | 752 | *max_width = box.width; |
647 | } else { | 753 | } else { |
648 | *max_width = config->floating_maximum_width; | 754 | *max_width = config->floating_maximum_width; |
649 | } | 755 | } |
@@ -651,99 +757,112 @@ void floating_calculate_constraints(int *min_width, int *max_width, | |||
651 | if (config->floating_maximum_height == -1) { // no maximum | 757 | if (config->floating_maximum_height == -1) { // no maximum |
652 | *max_height = INT_MAX; | 758 | *max_height = INT_MAX; |
653 | } else if (config->floating_maximum_height == 0) { // automatic | 759 | } else if (config->floating_maximum_height == 0) { // automatic |
654 | *max_height = box->height; | 760 | *max_height = box.height; |
655 | } else { | 761 | } else { |
656 | *max_height = config->floating_maximum_height; | 762 | *max_height = config->floating_maximum_height; |
657 | } | 763 | } |
658 | 764 | ||
659 | } | 765 | } |
660 | 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 | |||
661 | static void floating_natural_resize(struct sway_container *con) { | 782 | static void floating_natural_resize(struct sway_container *con) { |
662 | int min_width, max_width, min_height, max_height; | 783 | int min_width, max_width, min_height, max_height; |
663 | floating_calculate_constraints(&min_width, &max_width, | 784 | floating_calculate_constraints(&min_width, &max_width, |
664 | &min_height, &max_height); | 785 | &min_height, &max_height); |
665 | if (!con->view) { | 786 | if (!con->view) { |
666 | con->width = fmax(min_width, fmin(con->width, max_width)); | 787 | con->pending.width = fmax(min_width, fmin(con->pending.width, max_width)); |
667 | con->height = fmax(min_height, fmin(con->height, max_height)); | 788 | con->pending.height = fmax(min_height, fmin(con->pending.height, max_height)); |
668 | } else { | 789 | } else { |
669 | struct sway_view *view = con->view; | 790 | struct sway_view *view = con->view; |
670 | con->content_width = | 791 | con->pending.content_width = |
671 | fmax(min_width, fmin(view->natural_width, max_width)); | 792 | fmax(min_width, fmin(view->natural_width, max_width)); |
672 | con->content_height = | 793 | con->pending.content_height = |
673 | fmax(min_height, fmin(view->natural_height, max_height)); | 794 | fmax(min_height, fmin(view->natural_height, max_height)); |
674 | container_set_geometry_from_content(con); | 795 | container_set_geometry_from_content(con); |
675 | } | 796 | } |
676 | } | 797 | } |
677 | 798 | ||
678 | void container_floating_resize_and_center(struct sway_container *con) { | 799 | void container_floating_resize_and_center(struct sway_container *con) { |
679 | struct sway_workspace *ws = con->workspace; | 800 | struct sway_workspace *ws = con->pending.workspace; |
680 | if (!ws) { | 801 | if (!ws) { |
681 | // On scratchpad, just resize | 802 | // On scratchpad, just resize |
682 | floating_natural_resize(con); | 803 | floating_natural_resize(con); |
683 | return; | 804 | return; |
684 | } | 805 | } |
685 | 806 | ||
686 | struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, | 807 | struct wlr_box ob; |
687 | ws->output->wlr_output); | 808 | wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output, &ob); |
688 | if (!ob) { | 809 | if (wlr_box_empty(&ob)) { |
689 | // On NOOP output. Will be called again when moved to an output | 810 | // On NOOP output. Will be called again when moved to an output |
690 | con->x = 0; | 811 | con->pending.x = 0; |
691 | con->y = 0; | 812 | con->pending.y = 0; |
692 | con->width = 0; | 813 | con->pending.width = 0; |
693 | con->height = 0; | 814 | con->pending.height = 0; |
694 | return; | 815 | return; |
695 | } | 816 | } |
696 | 817 | ||
697 | floating_natural_resize(con); | 818 | floating_natural_resize(con); |
698 | if (!con->view) { | 819 | if (!con->view) { |
699 | if (con->width > ws->width || con->height > ws->height) { | 820 | if (con->pending.width > ws->width || con->pending.height > ws->height) { |
700 | con->x = ob->x + (ob->width - con->width) / 2; | 821 | con->pending.x = ob.x + (ob.width - con->pending.width) / 2; |
701 | con->y = ob->y + (ob->height - con->height) / 2; | 822 | con->pending.y = ob.y + (ob.height - con->pending.height) / 2; |
702 | } else { | 823 | } else { |
703 | con->x = ws->x + (ws->width - con->width) / 2; | 824 | con->pending.x = ws->x + (ws->width - con->pending.width) / 2; |
704 | con->y = ws->y + (ws->height - con->height) / 2; | 825 | con->pending.y = ws->y + (ws->height - con->pending.height) / 2; |
705 | } | 826 | } |
706 | } else { | 827 | } else { |
707 | if (con->content_width > ws->width | 828 | if (con->pending.content_width > ws->width |
708 | || con->content_height > ws->height) { | 829 | || con->pending.content_height > ws->height) { |
709 | con->content_x = ob->x + (ob->width - con->content_width) / 2; | 830 | con->pending.content_x = ob.x + (ob.width - con->pending.content_width) / 2; |
710 | con->content_y = ob->y + (ob->height - con->content_height) / 2; | 831 | con->pending.content_y = ob.y + (ob.height - con->pending.content_height) / 2; |
711 | } else { | 832 | } else { |
712 | con->content_x = ws->x + (ws->width - con->content_width) / 2; | 833 | con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2; |
713 | con->content_y = ws->y + (ws->height - con->content_height) / 2; | 834 | con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2; |
714 | } | 835 | } |
715 | 836 | ||
716 | // If the view's border is B_NONE then these properties are ignored. | 837 | // If the view's border is B_NONE then these properties are ignored. |
717 | con->border_top = con->border_bottom = true; | 838 | con->pending.border_top = con->pending.border_bottom = true; |
718 | con->border_left = con->border_right = true; | 839 | con->pending.border_left = con->pending.border_right = true; |
719 | 840 | ||
720 | container_set_geometry_from_content(con); | 841 | container_set_geometry_from_content(con); |
721 | } | 842 | } |
722 | } | 843 | } |
723 | 844 | ||
724 | void container_floating_set_default_size(struct sway_container *con) { | 845 | void container_floating_set_default_size(struct sway_container *con) { |
725 | if (!sway_assert(con->workspace, "Expected a container on a workspace")) { | 846 | if (!sway_assert(con->pending.workspace, "Expected a container on a workspace")) { |
726 | return; | 847 | return; |
727 | } | 848 | } |
728 | 849 | ||
729 | int min_width, max_width, min_height, max_height; | 850 | int min_width, max_width, min_height, max_height; |
730 | floating_calculate_constraints(&min_width, &max_width, | 851 | floating_calculate_constraints(&min_width, &max_width, |
731 | &min_height, &max_height); | 852 | &min_height, &max_height); |
732 | struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); | 853 | struct wlr_box box; |
733 | workspace_get_box(con->workspace, box); | 854 | workspace_get_box(con->pending.workspace, &box); |
734 | 855 | ||
735 | 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)); |
736 | 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)); |
737 | if (!con->view) { | 858 | if (!con->view) { |
738 | con->width = width; | 859 | con->pending.width = width; |
739 | con->height = height; | 860 | con->pending.height = height; |
740 | } else { | 861 | } else { |
741 | con->content_width = width; | 862 | con->pending.content_width = width; |
742 | con->content_height = height; | 863 | con->pending.content_height = height; |
743 | container_set_geometry_from_content(con); | 864 | container_set_geometry_from_content(con); |
744 | } | 865 | } |
745 | |||
746 | free(box); | ||
747 | } | 866 | } |
748 | 867 | ||
749 | 868 | ||
@@ -761,8 +880,8 @@ void container_set_resizing(struct sway_container *con, bool resizing) { | |||
761 | con->view->impl->set_resizing(con->view, resizing); | 880 | con->view->impl->set_resizing(con->view, resizing); |
762 | } | 881 | } |
763 | } else { | 882 | } else { |
764 | for (int i = 0; i < con->children->length; ++i ) { | 883 | for (int i = 0; i < con->pending.children->length; ++i ) { |
765 | struct sway_container *child = con->children->items[i]; | 884 | struct sway_container *child = con->pending.children->items[i]; |
766 | container_set_resizing(child, resizing); | 885 | container_set_resizing(child, resizing); |
767 | } | 886 | } |
768 | } | 887 | } |
@@ -774,21 +893,33 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
774 | } | 893 | } |
775 | 894 | ||
776 | struct sway_seat *seat = input_manager_current_seat(); | 895 | struct sway_seat *seat = input_manager_current_seat(); |
777 | struct sway_workspace *workspace = container->workspace; | 896 | struct sway_workspace *workspace = container->pending.workspace; |
897 | struct sway_container *focus = seat_get_focused_container(seat); | ||
898 | bool set_focus = focus == container; | ||
778 | 899 | ||
779 | if (enable) { | 900 | if (enable) { |
780 | struct sway_container *old_parent = container->parent; | 901 | struct sway_container *old_parent = container->pending.parent; |
781 | container_detach(container); | 902 | container_detach(container); |
782 | workspace_add_floating(workspace, container); | 903 | workspace_add_floating(workspace, container); |
783 | if (container->view) { | 904 | if (container->view) { |
784 | view_set_tiled(container->view, false); | 905 | view_set_tiled(container->view, false); |
785 | if (container->view->using_csd) { | 906 | if (container->view->using_csd) { |
786 | container->border = B_CSD; | 907 | container->saved_border = container->pending.border; |
908 | container->pending.border = B_CSD; | ||
909 | if (container->view->xdg_decoration) { | ||
910 | struct sway_xdg_decoration *deco = container->view->xdg_decoration; | ||
911 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | ||
912 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); | ||
913 | } | ||
787 | } | 914 | } |
788 | } | 915 | } |
789 | container_floating_set_default_size(container); | 916 | container_floating_set_default_size(container); |
790 | container_floating_resize_and_center(container); | 917 | container_floating_resize_and_center(container); |
791 | if (old_parent) { | 918 | if (old_parent) { |
919 | if (set_focus) { | ||
920 | seat_set_raw_focus(seat, &old_parent->node); | ||
921 | seat_set_raw_focus(seat, &container->node); | ||
922 | } | ||
792 | container_reap_empty(old_parent); | 923 | container_reap_empty(old_parent); |
793 | } | 924 | } |
794 | } else { | 925 | } else { |
@@ -800,19 +931,28 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
800 | struct sway_container *reference = | 931 | struct sway_container *reference = |
801 | seat_get_focus_inactive_tiling(seat, workspace); | 932 | seat_get_focus_inactive_tiling(seat, workspace); |
802 | if (reference) { | 933 | if (reference) { |
803 | container_add_sibling(reference, container, 1); | 934 | if (reference->view) { |
804 | container->width = reference->width; | 935 | container_add_sibling(reference, container, 1); |
805 | container->height = reference->height; | 936 | } else { |
937 | container_add_child(reference, container); | ||
938 | } | ||
939 | container->pending.width = reference->pending.width; | ||
940 | container->pending.height = reference->pending.height; | ||
806 | } else { | 941 | } else { |
807 | struct sway_container *other = | 942 | struct sway_container *other = |
808 | workspace_add_tiling(workspace, container); | 943 | workspace_add_tiling(workspace, container); |
809 | other->width = workspace->width; | 944 | other->pending.width = workspace->width; |
810 | other->height = workspace->height; | 945 | other->pending.height = workspace->height; |
811 | } | 946 | } |
812 | if (container->view) { | 947 | if (container->view) { |
813 | view_set_tiled(container->view, true); | 948 | view_set_tiled(container->view, true); |
814 | if (container->view->using_csd) { | 949 | if (container->view->using_csd) { |
815 | container->border = container->saved_border; | 950 | container->pending.border = container->saved_border; |
951 | if (container->view->xdg_decoration) { | ||
952 | struct sway_xdg_decoration *deco = container->view->xdg_decoration; | ||
953 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | ||
954 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); | ||
955 | } | ||
816 | } | 956 | } |
817 | } | 957 | } |
818 | container->width_fraction = 0; | 958 | container->width_fraction = 0; |
@@ -834,22 +974,22 @@ void container_set_geometry_from_content(struct sway_container *con) { | |||
834 | size_t border_width = 0; | 974 | size_t border_width = 0; |
835 | size_t top = 0; | 975 | size_t top = 0; |
836 | 976 | ||
837 | if (con->border != B_CSD) { | 977 | if (con->pending.border != B_CSD && !con->pending.fullscreen_mode) { |
838 | border_width = con->border_thickness * (con->border != B_NONE); | 978 | border_width = con->pending.border_thickness * (con->pending.border != B_NONE); |
839 | top = con->border == B_NORMAL ? | 979 | top = con->pending.border == B_NORMAL ? |
840 | container_titlebar_height() : border_width; | 980 | container_titlebar_height() : border_width; |
841 | } | 981 | } |
842 | 982 | ||
843 | con->x = con->content_x - border_width; | 983 | con->pending.x = con->pending.content_x - border_width; |
844 | con->y = con->content_y - top; | 984 | con->pending.y = con->pending.content_y - top; |
845 | con->width = con->content_width + border_width * 2; | 985 | con->pending.width = con->pending.content_width + border_width * 2; |
846 | con->height = top + con->content_height + border_width; | 986 | con->pending.height = top + con->pending.content_height + border_width; |
847 | node_set_dirty(&con->node); | 987 | node_set_dirty(&con->node); |
848 | } | 988 | } |
849 | 989 | ||
850 | bool container_is_floating(struct sway_container *container) { | 990 | bool container_is_floating(struct sway_container *container) { |
851 | if (!container->parent && container->workspace && | 991 | if (!container->pending.parent && container->pending.workspace && |
852 | list_find(container->workspace->floating, container) != -1) { | 992 | list_find(container->pending.workspace->floating, container) != -1) { |
853 | return true; | 993 | return true; |
854 | } | 994 | } |
855 | if (container->scratchpad) { | 995 | if (container->scratchpad) { |
@@ -859,10 +999,10 @@ bool container_is_floating(struct sway_container *container) { | |||
859 | } | 999 | } |
860 | 1000 | ||
861 | 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) { |
862 | box->x = container->x; | 1002 | box->x = container->pending.x; |
863 | box->y = container->y; | 1003 | box->y = container->pending.y; |
864 | box->width = container->width; | 1004 | box->width = container->pending.width; |
865 | box->height = container->height; | 1005 | box->height = container->pending.height; |
866 | } | 1006 | } |
867 | 1007 | ||
868 | /** | 1008 | /** |
@@ -870,14 +1010,14 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { | |||
870 | */ | 1010 | */ |
871 | void container_floating_translate(struct sway_container *con, | 1011 | void container_floating_translate(struct sway_container *con, |
872 | double x_amount, double y_amount) { | 1012 | double x_amount, double y_amount) { |
873 | con->x += x_amount; | 1013 | con->pending.x += x_amount; |
874 | con->y += y_amount; | 1014 | con->pending.y += y_amount; |
875 | con->content_x += x_amount; | 1015 | con->pending.content_x += x_amount; |
876 | con->content_y += y_amount; | 1016 | con->pending.content_y += y_amount; |
877 | 1017 | ||
878 | if (con->children) { | 1018 | if (con->pending.children) { |
879 | for (int i = 0; i < con->children->length; ++i) { | 1019 | for (int i = 0; i < con->pending.children->length; ++i) { |
880 | struct sway_container *child = con->children->items[i]; | 1020 | struct sway_container *child = con->pending.children->items[i]; |
881 | container_floating_translate(child, x_amount, y_amount); | 1021 | container_floating_translate(child, x_amount, y_amount); |
882 | } | 1022 | } |
883 | } | 1023 | } |
@@ -893,8 +1033,8 @@ void container_floating_translate(struct sway_container *con, | |||
893 | * center. | 1033 | * center. |
894 | */ | 1034 | */ |
895 | struct sway_output *container_floating_find_output(struct sway_container *con) { | 1035 | struct sway_output *container_floating_find_output(struct sway_container *con) { |
896 | double center_x = con->x + con->width / 2; | 1036 | double center_x = con->pending.x + con->pending.width / 2; |
897 | double center_y = con->y + con->height / 2; | 1037 | double center_y = con->pending.y + con->pending.height / 2; |
898 | struct sway_output *closest_output = NULL; | 1038 | struct sway_output *closest_output = NULL; |
899 | double closest_distance = DBL_MAX; | 1039 | double closest_distance = DBL_MAX; |
900 | for (int i = 0; i < root->outputs->length; ++i) { | 1040 | for (int i = 0; i < root->outputs->length; ++i) { |
@@ -925,11 +1065,11 @@ void container_floating_move_to(struct sway_container *con, | |||
925 | "Expected a floating container")) { | 1065 | "Expected a floating container")) { |
926 | return; | 1066 | return; |
927 | } | 1067 | } |
928 | container_floating_translate(con, lx - con->x, ly - con->y); | 1068 | container_floating_translate(con, lx - con->pending.x, ly - con->pending.y); |
929 | if (container_is_scratchpad_hidden(con)) { | 1069 | if (container_is_scratchpad_hidden(con)) { |
930 | return; | 1070 | return; |
931 | } | 1071 | } |
932 | struct sway_workspace *old_workspace = con->workspace; | 1072 | struct sway_workspace *old_workspace = con->pending.workspace; |
933 | struct sway_output *new_output = container_floating_find_output(con); | 1073 | struct sway_output *new_output = container_floating_find_output(con); |
934 | if (!sway_assert(new_output, "Unable to find any output")) { | 1074 | if (!sway_assert(new_output, "Unable to find any output")) { |
935 | return; | 1075 | return; |
@@ -941,6 +1081,13 @@ void container_floating_move_to(struct sway_container *con, | |||
941 | workspace_add_floating(new_workspace, con); | 1081 | workspace_add_floating(new_workspace, con); |
942 | arrange_workspace(old_workspace); | 1082 | arrange_workspace(old_workspace); |
943 | 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 | } | ||
944 | workspace_detect_urgent(old_workspace); | 1091 | workspace_detect_urgent(old_workspace); |
945 | workspace_detect_urgent(new_workspace); | 1092 | workspace_detect_urgent(new_workspace); |
946 | } | 1093 | } |
@@ -951,10 +1098,10 @@ void container_floating_move_to_center(struct sway_container *con) { | |||
951 | "Expected a floating container")) { | 1098 | "Expected a floating container")) { |
952 | return; | 1099 | return; |
953 | } | 1100 | } |
954 | struct sway_workspace *ws = con->workspace; | 1101 | struct sway_workspace *ws = con->pending.workspace; |
955 | double new_lx = ws->x + (ws->width - con->width) / 2; | 1102 | double new_lx = ws->x + (ws->width - con->pending.width) / 2; |
956 | double new_ly = ws->y + (ws->height - con->height) / 2; | 1103 | double new_ly = ws->y + (ws->height - con->pending.height) / 2; |
957 | container_floating_translate(con, new_lx - con->x, new_ly - con->y); | 1104 | container_floating_translate(con, new_lx - con->pending.x, new_ly - con->pending.y); |
958 | } | 1105 | } |
959 | 1106 | ||
960 | static bool find_urgent_iterator(struct sway_container *con, void *data) { | 1107 | static bool find_urgent_iterator(struct sway_container *con, void *data) { |
@@ -972,42 +1119,39 @@ void container_end_mouse_operation(struct sway_container *container) { | |||
972 | } | 1119 | } |
973 | } | 1120 | } |
974 | 1121 | ||
975 | static void set_fullscreen_iterator(struct sway_container *con, void *data) { | 1122 | static void set_fullscreen(struct sway_container *con, bool enable) { |
976 | if (!con->view) { | 1123 | if (!con->view) { |
977 | return; | 1124 | return; |
978 | } | 1125 | } |
979 | if (con->view->impl->set_fullscreen) { | 1126 | if (con->view->impl->set_fullscreen) { |
980 | bool *enable = data; | 1127 | con->view->impl->set_fullscreen(con->view, enable); |
981 | con->view->impl->set_fullscreen(con->view, *enable); | ||
982 | if (con->view->foreign_toplevel) { | 1128 | if (con->view->foreign_toplevel) { |
983 | wlr_foreign_toplevel_handle_v1_set_fullscreen( | 1129 | wlr_foreign_toplevel_handle_v1_set_fullscreen( |
984 | con->view->foreign_toplevel, *enable); | 1130 | con->view->foreign_toplevel, enable); |
985 | } | 1131 | } |
986 | } | 1132 | } |
987 | } | 1133 | } |
988 | 1134 | ||
989 | static void container_fullscreen_workspace(struct sway_container *con) { | 1135 | static void container_fullscreen_workspace(struct sway_container *con) { |
990 | if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, | 1136 | if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE, |
991 | "Expected a non-fullscreen container")) { | 1137 | "Expected a non-fullscreen container")) { |
992 | return; | 1138 | return; |
993 | } | 1139 | } |
994 | bool enable = true; | 1140 | set_fullscreen(con, true); |
995 | set_fullscreen_iterator(con, &enable); | 1141 | con->pending.fullscreen_mode = FULLSCREEN_WORKSPACE; |
996 | container_for_each_child(con, set_fullscreen_iterator, &enable); | ||
997 | con->fullscreen_mode = FULLSCREEN_WORKSPACE; | ||
998 | 1142 | ||
999 | con->saved_x = con->x; | 1143 | con->saved_x = con->pending.x; |
1000 | con->saved_y = con->y; | 1144 | con->saved_y = con->pending.y; |
1001 | con->saved_width = con->width; | 1145 | con->saved_width = con->pending.width; |
1002 | con->saved_height = con->height; | 1146 | con->saved_height = con->pending.height; |
1003 | 1147 | ||
1004 | if (con->workspace) { | 1148 | if (con->pending.workspace) { |
1005 | con->workspace->fullscreen = con; | 1149 | con->pending.workspace->fullscreen = con; |
1006 | struct sway_seat *seat; | 1150 | struct sway_seat *seat; |
1007 | struct sway_workspace *focus_ws; | 1151 | struct sway_workspace *focus_ws; |
1008 | wl_list_for_each(seat, &server.input->seats, link) { | 1152 | wl_list_for_each(seat, &server.input->seats, link) { |
1009 | focus_ws = seat_get_focused_workspace(seat); | 1153 | focus_ws = seat_get_focused_workspace(seat); |
1010 | if (focus_ws == con->workspace) { | 1154 | if (focus_ws == con->pending.workspace) { |
1011 | seat_set_focus_container(seat, con); | 1155 | seat_set_focus_container(seat, con); |
1012 | } else { | 1156 | } else { |
1013 | struct sway_node *focus = | 1157 | struct sway_node *focus = |
@@ -1023,19 +1167,17 @@ static void container_fullscreen_workspace(struct sway_container *con) { | |||
1023 | } | 1167 | } |
1024 | 1168 | ||
1025 | static void container_fullscreen_global(struct sway_container *con) { | 1169 | static void container_fullscreen_global(struct sway_container *con) { |
1026 | if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, | 1170 | if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE, |
1027 | "Expected a non-fullscreen container")) { | 1171 | "Expected a non-fullscreen container")) { |
1028 | return; | 1172 | return; |
1029 | } | 1173 | } |
1030 | bool enable = true; | 1174 | set_fullscreen(con, true); |
1031 | set_fullscreen_iterator(con, &enable); | ||
1032 | container_for_each_child(con, set_fullscreen_iterator, &enable); | ||
1033 | 1175 | ||
1034 | root->fullscreen_global = con; | 1176 | root->fullscreen_global = con; |
1035 | con->saved_x = con->x; | 1177 | con->saved_x = con->pending.x; |
1036 | con->saved_y = con->y; | 1178 | con->saved_y = con->pending.y; |
1037 | con->saved_width = con->width; | 1179 | con->saved_width = con->pending.width; |
1038 | con->saved_height = con->height; | 1180 | con->saved_height = con->pending.height; |
1039 | 1181 | ||
1040 | struct sway_seat *seat; | 1182 | struct sway_seat *seat; |
1041 | wl_list_for_each(seat, &server.input->seats, link) { | 1183 | wl_list_for_each(seat, &server.input->seats, link) { |
@@ -1045,34 +1187,32 @@ static void container_fullscreen_global(struct sway_container *con) { | |||
1045 | } | 1187 | } |
1046 | } | 1188 | } |
1047 | 1189 | ||
1048 | con->fullscreen_mode = FULLSCREEN_GLOBAL; | 1190 | con->pending.fullscreen_mode = FULLSCREEN_GLOBAL; |
1049 | container_end_mouse_operation(con); | 1191 | container_end_mouse_operation(con); |
1050 | ipc_event_window(con, "fullscreen_mode"); | 1192 | ipc_event_window(con, "fullscreen_mode"); |
1051 | } | 1193 | } |
1052 | 1194 | ||
1053 | void container_fullscreen_disable(struct sway_container *con) { | 1195 | void container_fullscreen_disable(struct sway_container *con) { |
1054 | if (!sway_assert(con->fullscreen_mode != FULLSCREEN_NONE, | 1196 | if (!sway_assert(con->pending.fullscreen_mode != FULLSCREEN_NONE, |
1055 | "Expected a fullscreen container")) { | 1197 | "Expected a fullscreen container")) { |
1056 | return; | 1198 | return; |
1057 | } | 1199 | } |
1058 | bool enable = false; | 1200 | set_fullscreen(con, false); |
1059 | set_fullscreen_iterator(con, &enable); | ||
1060 | container_for_each_child(con, set_fullscreen_iterator, &enable); | ||
1061 | 1201 | ||
1062 | if (container_is_floating(con)) { | 1202 | if (container_is_floating(con)) { |
1063 | con->x = con->saved_x; | 1203 | con->pending.x = con->saved_x; |
1064 | con->y = con->saved_y; | 1204 | con->pending.y = con->saved_y; |
1065 | con->width = con->saved_width; | 1205 | con->pending.width = con->saved_width; |
1066 | con->height = con->saved_height; | 1206 | con->pending.height = con->saved_height; |
1067 | } | 1207 | } |
1068 | 1208 | ||
1069 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 1209 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
1070 | if (con->workspace) { | 1210 | if (con->pending.workspace) { |
1071 | con->workspace->fullscreen = NULL; | 1211 | con->pending.workspace->fullscreen = NULL; |
1072 | if (container_is_floating(con)) { | 1212 | if (container_is_floating(con)) { |
1073 | struct sway_output *output = | 1213 | struct sway_output *output = |
1074 | container_floating_find_output(con); | 1214 | container_floating_find_output(con); |
1075 | if (con->workspace->output != output) { | 1215 | if (con->pending.workspace->output != output) { |
1076 | container_floating_move_to_center(con); | 1216 | container_floating_move_to_center(con); |
1077 | } | 1217 | } |
1078 | } | 1218 | } |
@@ -1084,11 +1224,11 @@ void container_fullscreen_disable(struct sway_container *con) { | |||
1084 | // If the container was mapped as fullscreen and set as floating by | 1224 | // If the container was mapped as fullscreen and set as floating by |
1085 | // criteria, it needs to be reinitialized as floating to get the proper | 1225 | // criteria, it needs to be reinitialized as floating to get the proper |
1086 | // size and location | 1226 | // size and location |
1087 | if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { | 1227 | if (container_is_floating(con) && (con->pending.width == 0 || con->pending.height == 0)) { |
1088 | container_floating_resize_and_center(con); | 1228 | container_floating_resize_and_center(con); |
1089 | } | 1229 | } |
1090 | 1230 | ||
1091 | con->fullscreen_mode = FULLSCREEN_NONE; | 1231 | con->pending.fullscreen_mode = FULLSCREEN_NONE; |
1092 | container_end_mouse_operation(con); | 1232 | container_end_mouse_operation(con); |
1093 | ipc_event_window(con, "fullscreen_mode"); | 1233 | ipc_event_window(con, "fullscreen_mode"); |
1094 | 1234 | ||
@@ -1106,7 +1246,7 @@ void container_fullscreen_disable(struct sway_container *con) { | |||
1106 | 1246 | ||
1107 | void container_set_fullscreen(struct sway_container *con, | 1247 | void container_set_fullscreen(struct sway_container *con, |
1108 | enum sway_fullscreen_mode mode) { | 1248 | enum sway_fullscreen_mode mode) { |
1109 | if (con->fullscreen_mode == mode) { | 1249 | if (con->pending.fullscreen_mode == mode) { |
1110 | return; | 1250 | return; |
1111 | } | 1251 | } |
1112 | 1252 | ||
@@ -1118,8 +1258,8 @@ void container_set_fullscreen(struct sway_container *con, | |||
1118 | if (root->fullscreen_global) { | 1258 | if (root->fullscreen_global) { |
1119 | container_fullscreen_disable(root->fullscreen_global); | 1259 | container_fullscreen_disable(root->fullscreen_global); |
1120 | } | 1260 | } |
1121 | if (con->workspace && con->workspace->fullscreen) { | 1261 | if (con->pending.workspace && con->pending.workspace->fullscreen) { |
1122 | container_fullscreen_disable(con->workspace->fullscreen); | 1262 | container_fullscreen_disable(con->pending.workspace->fullscreen); |
1123 | } | 1263 | } |
1124 | container_fullscreen_workspace(con); | 1264 | container_fullscreen_workspace(con); |
1125 | break; | 1265 | break; |
@@ -1127,7 +1267,7 @@ void container_set_fullscreen(struct sway_container *con, | |||
1127 | if (root->fullscreen_global) { | 1267 | if (root->fullscreen_global) { |
1128 | container_fullscreen_disable(root->fullscreen_global); | 1268 | container_fullscreen_disable(root->fullscreen_global); |
1129 | } | 1269 | } |
1130 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 1270 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
1131 | container_fullscreen_disable(con); | 1271 | container_fullscreen_disable(con); |
1132 | } | 1272 | } |
1133 | container_fullscreen_global(con); | 1273 | container_fullscreen_global(con); |
@@ -1137,8 +1277,8 @@ void container_set_fullscreen(struct sway_container *con, | |||
1137 | 1277 | ||
1138 | struct sway_container *container_toplevel_ancestor( | 1278 | struct sway_container *container_toplevel_ancestor( |
1139 | struct sway_container *container) { | 1279 | struct sway_container *container) { |
1140 | while (container->parent) { | 1280 | while (container->pending.parent) { |
1141 | container = container->parent; | 1281 | container = container->pending.parent; |
1142 | } | 1282 | } |
1143 | 1283 | ||
1144 | return container; | 1284 | return container; |
@@ -1150,148 +1290,67 @@ bool container_is_floating_or_child(struct sway_container *container) { | |||
1150 | 1290 | ||
1151 | bool container_is_fullscreen_or_child(struct sway_container *container) { | 1291 | bool container_is_fullscreen_or_child(struct sway_container *container) { |
1152 | do { | 1292 | do { |
1153 | if (container->fullscreen_mode) { | 1293 | if (container->pending.fullscreen_mode) { |
1154 | return true; | 1294 | return true; |
1155 | } | 1295 | } |
1156 | container = container->parent; | 1296 | container = container->pending.parent; |
1157 | } while (container); | 1297 | } while (container); |
1158 | 1298 | ||
1159 | return false; | 1299 | return false; |
1160 | } | 1300 | } |
1161 | 1301 | ||
1162 | static void surface_send_enter_iterator(struct wlr_surface *surface, | ||
1163 | int x, int y, void *data) { | ||
1164 | struct wlr_output *wlr_output = data; | ||
1165 | wlr_surface_send_enter(surface, wlr_output); | ||
1166 | } | ||
1167 | |||
1168 | static void surface_send_leave_iterator(struct wlr_surface *surface, | ||
1169 | int x, int y, void *data) { | ||
1170 | struct wlr_output *wlr_output = data; | ||
1171 | wlr_surface_send_leave(surface, wlr_output); | ||
1172 | } | ||
1173 | |||
1174 | void container_discover_outputs(struct sway_container *con) { | ||
1175 | struct wlr_box con_box = { | ||
1176 | .x = con->current.x, | ||
1177 | .y = con->current.y, | ||
1178 | .width = con->current.width, | ||
1179 | .height = con->current.height, | ||
1180 | }; | ||
1181 | struct sway_output *old_output = container_get_effective_output(con); | ||
1182 | |||
1183 | for (int i = 0; i < root->outputs->length; ++i) { | ||
1184 | struct sway_output *output = root->outputs->items[i]; | ||
1185 | struct wlr_box output_box; | ||
1186 | output_get_box(output, &output_box); | ||
1187 | struct wlr_box intersection; | ||
1188 | bool intersects = | ||
1189 | wlr_box_intersection(&intersection, &con_box, &output_box); | ||
1190 | int index = list_find(con->outputs, output); | ||
1191 | |||
1192 | if (intersects && index == -1) { | ||
1193 | // Send enter | ||
1194 | sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output); | ||
1195 | if (con->view) { | ||
1196 | view_for_each_surface(con->view, | ||
1197 | surface_send_enter_iterator, output->wlr_output); | ||
1198 | if (con->view->foreign_toplevel) { | ||
1199 | wlr_foreign_toplevel_handle_v1_output_enter( | ||
1200 | con->view->foreign_toplevel, output->wlr_output); | ||
1201 | } | ||
1202 | } | ||
1203 | list_add(con->outputs, output); | ||
1204 | } else if (!intersects && index != -1) { | ||
1205 | // Send leave | ||
1206 | sway_log(SWAY_DEBUG, "Container %p left output %p", con, output); | ||
1207 | if (con->view) { | ||
1208 | view_for_each_surface(con->view, | ||
1209 | surface_send_leave_iterator, output->wlr_output); | ||
1210 | if (con->view->foreign_toplevel) { | ||
1211 | wlr_foreign_toplevel_handle_v1_output_leave( | ||
1212 | con->view->foreign_toplevel, output->wlr_output); | ||
1213 | } | ||
1214 | } | ||
1215 | list_del(con->outputs, index); | ||
1216 | } | ||
1217 | } | ||
1218 | struct sway_output *new_output = container_get_effective_output(con); | ||
1219 | double old_scale = old_output && old_output->enabled ? | ||
1220 | old_output->wlr_output->scale : -1; | ||
1221 | double new_scale = new_output ? new_output->wlr_output->scale : -1; | ||
1222 | if (old_scale != new_scale) { | ||
1223 | container_update_title_textures(con); | ||
1224 | container_update_marks_textures(con); | ||
1225 | } | ||
1226 | } | ||
1227 | |||
1228 | enum sway_container_layout container_parent_layout(struct sway_container *con) { | 1302 | enum sway_container_layout container_parent_layout(struct sway_container *con) { |
1229 | if (con->parent) { | 1303 | if (con->pending.parent) { |
1230 | return con->parent->layout; | 1304 | return con->pending.parent->pending.layout; |
1231 | } | 1305 | } |
1232 | if (con->workspace) { | 1306 | if (con->pending.workspace) { |
1233 | return con->workspace->layout; | 1307 | return con->pending.workspace->layout; |
1234 | } | 1308 | } |
1235 | return L_NONE; | 1309 | return L_NONE; |
1236 | } | 1310 | } |
1237 | 1311 | ||
1238 | enum sway_container_layout container_current_parent_layout( | ||
1239 | struct sway_container *con) { | ||
1240 | if (con->current.parent) { | ||
1241 | return con->current.parent->current.layout; | ||
1242 | } | ||
1243 | return con->current.workspace->current.layout; | ||
1244 | } | ||
1245 | |||
1246 | list_t *container_get_siblings(struct sway_container *container) { | 1312 | list_t *container_get_siblings(struct sway_container *container) { |
1247 | if (container->parent) { | 1313 | if (container->pending.parent) { |
1248 | return container->parent->children; | 1314 | return container->pending.parent->pending.children; |
1249 | } | 1315 | } |
1250 | if (container_is_scratchpad_hidden(container)) { | 1316 | if (!container->pending.workspace) { |
1251 | return NULL; | 1317 | return NULL; |
1252 | } | 1318 | } |
1253 | if (list_find(container->workspace->tiling, container) != -1) { | 1319 | if (list_find(container->pending.workspace->tiling, container) != -1) { |
1254 | return container->workspace->tiling; | 1320 | return container->pending.workspace->tiling; |
1255 | } | 1321 | } |
1256 | return container->workspace->floating; | 1322 | return container->pending.workspace->floating; |
1257 | } | 1323 | } |
1258 | 1324 | ||
1259 | int container_sibling_index(struct sway_container *child) { | 1325 | int container_sibling_index(struct sway_container *child) { |
1260 | return list_find(container_get_siblings(child), child); | 1326 | return list_find(container_get_siblings(child), child); |
1261 | } | 1327 | } |
1262 | 1328 | ||
1263 | list_t *container_get_current_siblings(struct sway_container *container) { | ||
1264 | if (container->current.parent) { | ||
1265 | return container->current.parent->current.children; | ||
1266 | } | ||
1267 | return container->current.workspace->current.tiling; | ||
1268 | } | ||
1269 | |||
1270 | void container_handle_fullscreen_reparent(struct sway_container *con) { | 1329 | void container_handle_fullscreen_reparent(struct sway_container *con) { |
1271 | if (con->fullscreen_mode != FULLSCREEN_WORKSPACE || !con->workspace || | 1330 | if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || |
1272 | con->workspace->fullscreen == con) { | 1331 | con->pending.workspace->fullscreen == con) { |
1273 | return; | 1332 | return; |
1274 | } | 1333 | } |
1275 | if (con->workspace->fullscreen) { | 1334 | if (con->pending.workspace->fullscreen) { |
1276 | container_fullscreen_disable(con->workspace->fullscreen); | 1335 | container_fullscreen_disable(con->pending.workspace->fullscreen); |
1277 | } | 1336 | } |
1278 | con->workspace->fullscreen = con; | 1337 | con->pending.workspace->fullscreen = con; |
1279 | 1338 | ||
1280 | arrange_workspace(con->workspace); | 1339 | arrange_workspace(con->pending.workspace); |
1281 | } | 1340 | } |
1282 | 1341 | ||
1283 | static void set_workspace(struct sway_container *container, void *data) { | 1342 | static void set_workspace(struct sway_container *container, void *data) { |
1284 | container->workspace = container->parent->workspace; | 1343 | container->pending.workspace = container->pending.parent->pending.workspace; |
1285 | } | 1344 | } |
1286 | 1345 | ||
1287 | void container_insert_child(struct sway_container *parent, | 1346 | void container_insert_child(struct sway_container *parent, |
1288 | struct sway_container *child, int i) { | 1347 | struct sway_container *child, int i) { |
1289 | if (child->workspace) { | 1348 | if (child->pending.workspace) { |
1290 | container_detach(child); | 1349 | container_detach(child); |
1291 | } | 1350 | } |
1292 | list_insert(parent->children, i, child); | 1351 | list_insert(parent->pending.children, i, child); |
1293 | child->parent = parent; | 1352 | child->pending.parent = parent; |
1294 | child->workspace = parent->workspace; | 1353 | child->pending.workspace = parent->pending.workspace; |
1295 | container_for_each_child(child, set_workspace, NULL); | 1354 | container_for_each_child(child, set_workspace, NULL); |
1296 | container_handle_fullscreen_reparent(child); | 1355 | container_handle_fullscreen_reparent(child); |
1297 | container_update_representation(parent); | 1356 | container_update_representation(parent); |
@@ -1299,14 +1358,14 @@ void container_insert_child(struct sway_container *parent, | |||
1299 | 1358 | ||
1300 | void container_add_sibling(struct sway_container *fixed, | 1359 | void container_add_sibling(struct sway_container *fixed, |
1301 | struct sway_container *active, bool after) { | 1360 | struct sway_container *active, bool after) { |
1302 | if (active->workspace) { | 1361 | if (active->pending.workspace) { |
1303 | container_detach(active); | 1362 | container_detach(active); |
1304 | } | 1363 | } |
1305 | list_t *siblings = container_get_siblings(fixed); | 1364 | list_t *siblings = container_get_siblings(fixed); |
1306 | int index = list_find(siblings, fixed); | 1365 | int index = list_find(siblings, fixed); |
1307 | list_insert(siblings, index + after, active); | 1366 | list_insert(siblings, index + after, active); |
1308 | active->parent = fixed->parent; | 1367 | active->pending.parent = fixed->pending.parent; |
1309 | active->workspace = fixed->workspace; | 1368 | active->pending.workspace = fixed->pending.workspace; |
1310 | container_for_each_child(active, set_workspace, NULL); | 1369 | container_for_each_child(active, set_workspace, NULL); |
1311 | container_handle_fullscreen_reparent(active); | 1370 | container_handle_fullscreen_reparent(active); |
1312 | container_update_representation(active); | 1371 | container_update_representation(active); |
@@ -1314,17 +1373,13 @@ void container_add_sibling(struct sway_container *fixed, | |||
1314 | 1373 | ||
1315 | void container_add_child(struct sway_container *parent, | 1374 | void container_add_child(struct sway_container *parent, |
1316 | struct sway_container *child) { | 1375 | struct sway_container *child) { |
1317 | if (child->workspace) { | 1376 | if (child->pending.workspace) { |
1318 | container_detach(child); | 1377 | container_detach(child); |
1319 | } | 1378 | } |
1320 | list_add(parent->children, child); | 1379 | list_add(parent->pending.children, child); |
1321 | child->parent = parent; | 1380 | child->pending.parent = parent; |
1322 | child->workspace = parent->workspace; | 1381 | child->pending.workspace = parent->pending.workspace; |
1323 | container_for_each_child(child, set_workspace, NULL); | 1382 | container_for_each_child(child, set_workspace, NULL); |
1324 | bool fullscreen = child->fullscreen_mode != FULLSCREEN_NONE || | ||
1325 | parent->fullscreen_mode != FULLSCREEN_NONE; | ||
1326 | set_fullscreen_iterator(child, &fullscreen); | ||
1327 | container_for_each_child(child, set_fullscreen_iterator, &fullscreen); | ||
1328 | container_handle_fullscreen_reparent(child); | 1383 | container_handle_fullscreen_reparent(child); |
1329 | container_update_representation(parent); | 1384 | container_update_representation(parent); |
1330 | node_set_dirty(&child->node); | 1385 | node_set_dirty(&child->node); |
@@ -1332,15 +1387,15 @@ void container_add_child(struct sway_container *parent, | |||
1332 | } | 1387 | } |
1333 | 1388 | ||
1334 | void container_detach(struct sway_container *child) { | 1389 | void container_detach(struct sway_container *child) { |
1335 | if (child->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 1390 | if (child->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
1336 | child->workspace->fullscreen = NULL; | 1391 | child->pending.workspace->fullscreen = NULL; |
1337 | } | 1392 | } |
1338 | if (child->fullscreen_mode == FULLSCREEN_GLOBAL) { | 1393 | if (child->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
1339 | root->fullscreen_global = NULL; | 1394 | root->fullscreen_global = NULL; |
1340 | } | 1395 | } |
1341 | 1396 | ||
1342 | struct sway_container *old_parent = child->parent; | 1397 | struct sway_container *old_parent = child->pending.parent; |
1343 | struct sway_workspace *old_workspace = child->workspace; | 1398 | struct sway_workspace *old_workspace = child->pending.workspace; |
1344 | list_t *siblings = container_get_siblings(child); | 1399 | list_t *siblings = container_get_siblings(child); |
1345 | if (siblings) { | 1400 | if (siblings) { |
1346 | int index = list_find(siblings, child); | 1401 | int index = list_find(siblings, child); |
@@ -1348,8 +1403,8 @@ void container_detach(struct sway_container *child) { | |||
1348 | list_del(siblings, index); | 1403 | list_del(siblings, index); |
1349 | } | 1404 | } |
1350 | } | 1405 | } |
1351 | child->parent = NULL; | 1406 | child->pending.parent = NULL; |
1352 | child->workspace = NULL; | 1407 | child->pending.workspace = NULL; |
1353 | container_for_each_child(child, set_workspace, NULL); | 1408 | container_for_each_child(child, set_workspace, NULL); |
1354 | 1409 | ||
1355 | if (old_parent) { | 1410 | if (old_parent) { |
@@ -1364,18 +1419,18 @@ void container_detach(struct sway_container *child) { | |||
1364 | 1419 | ||
1365 | void container_replace(struct sway_container *container, | 1420 | void container_replace(struct sway_container *container, |
1366 | struct sway_container *replacement) { | 1421 | struct sway_container *replacement) { |
1367 | enum sway_fullscreen_mode fullscreen = container->fullscreen_mode; | 1422 | enum sway_fullscreen_mode fullscreen = container->pending.fullscreen_mode; |
1368 | bool scratchpad = container->scratchpad; | 1423 | bool scratchpad = container->scratchpad; |
1369 | struct sway_workspace *ws = NULL; | 1424 | struct sway_workspace *ws = NULL; |
1370 | if (fullscreen != FULLSCREEN_NONE) { | 1425 | if (fullscreen != FULLSCREEN_NONE) { |
1371 | container_fullscreen_disable(container); | 1426 | container_fullscreen_disable(container); |
1372 | } | 1427 | } |
1373 | if (scratchpad) { | 1428 | if (scratchpad) { |
1374 | ws = container->workspace; | 1429 | ws = container->pending.workspace; |
1375 | root_scratchpad_show(container); | 1430 | root_scratchpad_show(container); |
1376 | root_scratchpad_remove_container(container); | 1431 | root_scratchpad_remove_container(container); |
1377 | } | 1432 | } |
1378 | if (container->parent || container->workspace) { | 1433 | if (container->pending.parent || container->pending.workspace) { |
1379 | float width_fraction = container->width_fraction; | 1434 | float width_fraction = container->width_fraction; |
1380 | float height_fraction = container->height_fraction; | 1435 | float height_fraction = container->height_fraction; |
1381 | container_add_sibling(container, replacement, 1); | 1436 | container_add_sibling(container, replacement, 1); |
@@ -1403,7 +1458,7 @@ struct sway_container *container_split(struct sway_container *child, | |||
1403 | enum sway_container_layout layout) { | 1458 | enum sway_container_layout layout) { |
1404 | // i3 doesn't split singleton H/V containers | 1459 | // i3 doesn't split singleton H/V containers |
1405 | // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354 | 1460 | // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354 |
1406 | if (child->parent || child->workspace) { | 1461 | if (child->pending.parent || child->pending.workspace) { |
1407 | list_t *siblings = container_get_siblings(child); | 1462 | list_t *siblings = container_get_siblings(child); |
1408 | if (siblings->length == 1) { | 1463 | if (siblings->length == 1) { |
1409 | enum sway_container_layout current = container_parent_layout(child); | 1464 | enum sway_container_layout current = container_parent_layout(child); |
@@ -1411,12 +1466,12 @@ struct sway_container *container_split(struct sway_container *child, | |||
1411 | current = L_NONE; | 1466 | current = L_NONE; |
1412 | } | 1467 | } |
1413 | if (current == L_HORIZ || current == L_VERT) { | 1468 | if (current == L_HORIZ || current == L_VERT) { |
1414 | if (child->parent) { | 1469 | if (child->pending.parent) { |
1415 | child->parent->layout = layout; | 1470 | child->pending.parent->pending.layout = layout; |
1416 | container_update_representation(child->parent); | 1471 | container_update_representation(child->pending.parent); |
1417 | } else { | 1472 | } else { |
1418 | child->workspace->layout = layout; | 1473 | child->pending.workspace->layout = layout; |
1419 | workspace_update_representation(child->workspace); | 1474 | workspace_update_representation(child->pending.workspace); |
1420 | } | 1475 | } |
1421 | return child; | 1476 | return child; |
1422 | } | 1477 | } |
@@ -1429,25 +1484,25 @@ struct sway_container *container_split(struct sway_container *child, | |||
1429 | if (container_is_floating(child) && child->view) { | 1484 | if (container_is_floating(child) && child->view) { |
1430 | view_set_tiled(child->view, true); | 1485 | view_set_tiled(child->view, true); |
1431 | if (child->view->using_csd) { | 1486 | if (child->view->using_csd) { |
1432 | child->border = child->saved_border; | 1487 | child->pending.border = child->saved_border; |
1433 | } | 1488 | } |
1434 | } | 1489 | } |
1435 | 1490 | ||
1436 | struct sway_container *cont = container_create(NULL); | 1491 | struct sway_container *cont = container_create(NULL); |
1437 | cont->width = child->width; | 1492 | cont->pending.width = child->pending.width; |
1438 | cont->height = child->height; | 1493 | cont->pending.height = child->pending.height; |
1439 | cont->width_fraction = child->width_fraction; | 1494 | cont->width_fraction = child->width_fraction; |
1440 | cont->height_fraction = child->height_fraction; | 1495 | cont->height_fraction = child->height_fraction; |
1441 | cont->x = child->x; | 1496 | cont->pending.x = child->pending.x; |
1442 | cont->y = child->y; | 1497 | cont->pending.y = child->pending.y; |
1443 | cont->layout = layout; | 1498 | cont->pending.layout = layout; |
1444 | 1499 | ||
1445 | container_replace(child, cont); | 1500 | container_replace(child, cont); |
1446 | container_add_child(cont, child); | 1501 | container_add_child(cont, child); |
1447 | 1502 | ||
1448 | if (set_focus) { | 1503 | if (set_focus) { |
1449 | seat_set_raw_focus(seat, &cont->node); | 1504 | seat_set_raw_focus(seat, &cont->node); |
1450 | if (cont->fullscreen_mode == FULLSCREEN_GLOBAL) { | 1505 | if (cont->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
1451 | seat_set_focus(seat, &child->node); | 1506 | seat_set_focus(seat, &child->node); |
1452 | } else { | 1507 | } else { |
1453 | seat_set_raw_focus(seat, &child->node); | 1508 | seat_set_raw_focus(seat, &child->node); |
@@ -1485,7 +1540,7 @@ bool container_find_and_unmark(char *mark) { | |||
1485 | if (strcmp(con_mark, mark) == 0) { | 1540 | if (strcmp(con_mark, mark) == 0) { |
1486 | free(con_mark); | 1541 | free(con_mark); |
1487 | list_del(con->marks, i); | 1542 | list_del(con->marks, i); |
1488 | container_update_marks_textures(con); | 1543 | container_update_marks(con); |
1489 | ipc_event_window(con, "mark"); | 1544 | ipc_event_window(con, "mark"); |
1490 | return true; | 1545 | return true; |
1491 | } | 1546 | } |
@@ -1516,111 +1571,27 @@ void container_add_mark(struct sway_container *con, char *mark) { | |||
1516 | ipc_event_window(con, "mark"); | 1571 | ipc_event_window(con, "mark"); |
1517 | } | 1572 | } |
1518 | 1573 | ||
1519 | static void update_marks_texture(struct sway_container *con, | ||
1520 | struct wlr_texture **texture, struct border_colors *class) { | ||
1521 | struct sway_output *output = container_get_effective_output(con); | ||
1522 | if (!output) { | ||
1523 | return; | ||
1524 | } | ||
1525 | if (*texture) { | ||
1526 | wlr_texture_destroy(*texture); | ||
1527 | *texture = NULL; | ||
1528 | } | ||
1529 | if (!con->marks->length) { | ||
1530 | return; | ||
1531 | } | ||
1532 | |||
1533 | size_t len = 0; | ||
1534 | for (int i = 0; i < con->marks->length; ++i) { | ||
1535 | char *mark = con->marks->items[i]; | ||
1536 | if (mark[0] != '_') { | ||
1537 | len += strlen(mark) + 2; | ||
1538 | } | ||
1539 | } | ||
1540 | char *buffer = calloc(len + 1, 1); | ||
1541 | char *part = malloc(len + 1); | ||
1542 | |||
1543 | if (!sway_assert(buffer && part, "Unable to allocate memory")) { | ||
1544 | free(buffer); | ||
1545 | return; | ||
1546 | } | ||
1547 | |||
1548 | for (int i = 0; i < con->marks->length; ++i) { | ||
1549 | char *mark = con->marks->items[i]; | ||
1550 | if (mark[0] != '_') { | ||
1551 | sprintf(part, "[%s]", mark); | ||
1552 | strcat(buffer, part); | ||
1553 | } | ||
1554 | } | ||
1555 | free(part); | ||
1556 | |||
1557 | double scale = output->wlr_output->scale; | ||
1558 | int width = 0; | ||
1559 | int height = con->title_height * scale; | ||
1560 | |||
1561 | cairo_t *c = cairo_create(NULL); | ||
1562 | get_text_size(c, config->font, &width, NULL, NULL, scale, false, | ||
1563 | "%s", buffer); | ||
1564 | cairo_destroy(c); | ||
1565 | |||
1566 | cairo_surface_t *surface = cairo_image_surface_create( | ||
1567 | CAIRO_FORMAT_ARGB32, width, height); | ||
1568 | cairo_t *cairo = cairo_create(surface); | ||
1569 | cairo_set_source_rgba(cairo, class->background[0], class->background[1], | ||
1570 | class->background[2], class->background[3]); | ||
1571 | cairo_paint(cairo); | ||
1572 | PangoContext *pango = pango_cairo_create_context(cairo); | ||
1573 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
1574 | cairo_set_source_rgba(cairo, class->text[0], class->text[1], | ||
1575 | class->text[2], class->text[3]); | ||
1576 | cairo_move_to(cairo, 0, 0); | ||
1577 | |||
1578 | pango_printf(cairo, config->font, scale, false, "%s", buffer); | ||
1579 | |||
1580 | cairo_surface_flush(surface); | ||
1581 | unsigned char *data = cairo_image_surface_get_data(surface); | ||
1582 | int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); | ||
1583 | struct wlr_renderer *renderer = wlr_backend_get_renderer( | ||
1584 | output->wlr_output->backend); | ||
1585 | *texture = wlr_texture_from_pixels( | ||
1586 | renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); | ||
1587 | cairo_surface_destroy(surface); | ||
1588 | g_object_unref(pango); | ||
1589 | cairo_destroy(cairo); | ||
1590 | free(buffer); | ||
1591 | } | ||
1592 | |||
1593 | void container_update_marks_textures(struct sway_container *con) { | ||
1594 | if (!config->show_marks) { | ||
1595 | return; | ||
1596 | } | ||
1597 | update_marks_texture(con, &con->marks_focused, | ||
1598 | &config->border_colors.focused); | ||
1599 | update_marks_texture(con, &con->marks_focused_inactive, | ||
1600 | &config->border_colors.focused_inactive); | ||
1601 | update_marks_texture(con, &con->marks_unfocused, | ||
1602 | &config->border_colors.unfocused); | ||
1603 | update_marks_texture(con, &con->marks_urgent, | ||
1604 | &config->border_colors.urgent); | ||
1605 | container_damage_whole(con); | ||
1606 | } | ||
1607 | |||
1608 | void container_raise_floating(struct sway_container *con) { | 1574 | void container_raise_floating(struct sway_container *con) { |
1609 | // 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. |
1610 | struct sway_container *floater = container_toplevel_ancestor(con); | 1576 | struct sway_container *floater = container_toplevel_ancestor(con); |
1611 | if (container_is_floating(floater) && floater->workspace) { | 1577 | if (container_is_floating(floater) && floater->pending.workspace) { |
1612 | list_move_to_end(floater->workspace->floating, floater); | 1578 | // it's okay to just raise the scene directly instead of waiting |
1613 | node_set_dirty(&floater->workspace->node); | 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 | |||
1583 | list_move_to_end(floater->pending.workspace->floating, floater); | ||
1584 | node_set_dirty(&floater->pending.workspace->node); | ||
1614 | } | 1585 | } |
1615 | } | 1586 | } |
1616 | 1587 | ||
1617 | bool container_is_scratchpad_hidden(struct sway_container *con) { | 1588 | bool container_is_scratchpad_hidden(struct sway_container *con) { |
1618 | return con->scratchpad && !con->workspace; | 1589 | return con->scratchpad && !con->pending.workspace; |
1619 | } | 1590 | } |
1620 | 1591 | ||
1621 | bool container_is_scratchpad_hidden_or_child(struct sway_container *con) { | 1592 | bool container_is_scratchpad_hidden_or_child(struct sway_container *con) { |
1622 | con = container_toplevel_ancestor(con); | 1593 | con = container_toplevel_ancestor(con); |
1623 | return con->scratchpad && !con->workspace; | 1594 | return con->scratchpad && !con->pending.workspace; |
1624 | } | 1595 | } |
1625 | 1596 | ||
1626 | bool container_is_sticky(struct sway_container *con) { | 1597 | bool container_is_sticky(struct sway_container *con) { |
@@ -1648,39 +1619,39 @@ static bool is_parallel(enum sway_container_layout first, | |||
1648 | static bool container_is_squashable(struct sway_container *con, | 1619 | static bool container_is_squashable(struct sway_container *con, |
1649 | struct sway_container *child) { | 1620 | struct sway_container *child) { |
1650 | enum sway_container_layout gp_layout = container_parent_layout(con); | 1621 | enum sway_container_layout gp_layout = container_parent_layout(con); |
1651 | return (con->layout == L_HORIZ || con->layout == L_VERT) && | 1622 | return (con->pending.layout == L_HORIZ || con->pending.layout == L_VERT) && |
1652 | (child->layout == L_HORIZ || child->layout == L_VERT) && | 1623 | (child->pending.layout == L_HORIZ || child->pending.layout == L_VERT) && |
1653 | !is_parallel(con->layout, child->layout) && | 1624 | !is_parallel(con->pending.layout, child->pending.layout) && |
1654 | is_parallel(gp_layout, child->layout); | 1625 | is_parallel(gp_layout, child->pending.layout); |
1655 | } | 1626 | } |
1656 | 1627 | ||
1657 | static void container_squash_children(struct sway_container *con) { | 1628 | static void container_squash_children(struct sway_container *con) { |
1658 | for (int i = 0; i < con->children->length; i++) { | 1629 | for (int i = 0; i < con->pending.children->length; i++) { |
1659 | struct sway_container *child = con->children->items[i]; | 1630 | struct sway_container *child = con->pending.children->items[i]; |
1660 | i += container_squash(child); | 1631 | i += container_squash(child); |
1661 | } | 1632 | } |
1662 | } | 1633 | } |
1663 | 1634 | ||
1664 | int container_squash(struct sway_container *con) { | 1635 | int container_squash(struct sway_container *con) { |
1665 | if (!con->children) { | 1636 | if (!con->pending.children) { |
1666 | return 0; | 1637 | return 0; |
1667 | } | 1638 | } |
1668 | if (con->children->length != 1) { | 1639 | if (con->pending.children->length != 1) { |
1669 | container_squash_children(con); | 1640 | container_squash_children(con); |
1670 | return 0; | 1641 | return 0; |
1671 | } | 1642 | } |
1672 | struct sway_container *child = con->children->items[0]; | 1643 | struct sway_container *child = con->pending.children->items[0]; |
1673 | int idx = container_sibling_index(con); | 1644 | int idx = container_sibling_index(con); |
1674 | int change = 0; | 1645 | int change = 0; |
1675 | if (container_is_squashable(con, child)) { | 1646 | if (container_is_squashable(con, child)) { |
1676 | // con and child are a redundant H/V pair. Destroy them. | 1647 | // con and child are a redundant H/V pair. Destroy them. |
1677 | while (child->children->length) { | 1648 | while (child->pending.children->length) { |
1678 | struct sway_container *current = child->children->items[0]; | 1649 | struct sway_container *current = child->pending.children->items[0]; |
1679 | container_detach(current); | 1650 | container_detach(current); |
1680 | if (con->parent) { | 1651 | if (con->pending.parent) { |
1681 | container_insert_child(con->parent, current, idx); | 1652 | container_insert_child(con->pending.parent, current, idx); |
1682 | } else { | 1653 | } else { |
1683 | workspace_insert_tiling_direct(con->workspace, current, idx); | 1654 | workspace_insert_tiling_direct(con->pending.workspace, current, idx); |
1684 | } | 1655 | } |
1685 | change++; | 1656 | change++; |
1686 | } | 1657 | } |
@@ -1692,3 +1663,177 @@ int container_squash(struct sway_container *con) { | |||
1692 | } | 1663 | } |
1693 | return change; | 1664 | return change; |
1694 | } | 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 ffa7f2cc..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 | } |
@@ -75,7 +74,7 @@ void node_get_box(struct sway_node *node, struct wlr_box *box) { | |||
75 | struct sway_output *node_get_output(struct sway_node *node) { | 74 | struct sway_output *node_get_output(struct sway_node *node) { |
76 | switch (node->type) { | 75 | switch (node->type) { |
77 | case N_CONTAINER: { | 76 | case N_CONTAINER: { |
78 | struct sway_workspace *ws = node->sway_container->workspace; | 77 | struct sway_workspace *ws = node->sway_container->pending.workspace; |
79 | return ws ? ws->output : NULL; | 78 | return ws ? ws->output : NULL; |
80 | } | 79 | } |
81 | case N_WORKSPACE: | 80 | case N_WORKSPACE: |
@@ -91,7 +90,7 @@ struct sway_output *node_get_output(struct sway_node *node) { | |||
91 | enum sway_container_layout node_get_layout(struct sway_node *node) { | 90 | enum sway_container_layout node_get_layout(struct sway_node *node) { |
92 | switch (node->type) { | 91 | switch (node->type) { |
93 | case N_CONTAINER: | 92 | case N_CONTAINER: |
94 | return node->sway_container->layout; | 93 | return node->sway_container->pending.layout; |
95 | case N_WORKSPACE: | 94 | case N_WORKSPACE: |
96 | return node->sway_workspace->layout; | 95 | return node->sway_workspace->layout; |
97 | case N_OUTPUT: | 96 | case N_OUTPUT: |
@@ -105,11 +104,11 @@ struct sway_node *node_get_parent(struct sway_node *node) { | |||
105 | switch (node->type) { | 104 | switch (node->type) { |
106 | case N_CONTAINER: { | 105 | case N_CONTAINER: { |
107 | struct sway_container *con = node->sway_container; | 106 | struct sway_container *con = node->sway_container; |
108 | if (con->parent) { | 107 | if (con->pending.parent) { |
109 | return &con->parent->node; | 108 | return &con->pending.parent->node; |
110 | } | 109 | } |
111 | if (con->workspace) { | 110 | if (con->pending.workspace) { |
112 | return &con->workspace->node; | 111 | return &con->pending.workspace->node; |
113 | } | 112 | } |
114 | } | 113 | } |
115 | return NULL; | 114 | return NULL; |
@@ -131,7 +130,7 @@ struct sway_node *node_get_parent(struct sway_node *node) { | |||
131 | list_t *node_get_children(struct sway_node *node) { | 130 | list_t *node_get_children(struct sway_node *node) { |
132 | switch (node->type) { | 131 | switch (node->type) { |
133 | case N_CONTAINER: | 132 | case N_CONTAINER: |
134 | return node->sway_container->children; | 133 | return node->sway_container->pending.children; |
135 | case N_WORKSPACE: | 134 | case N_WORKSPACE: |
136 | return node->sway_workspace->tiling; | 135 | return node->sway_workspace->tiling; |
137 | case N_OUTPUT: | 136 | case N_OUTPUT: |
@@ -143,7 +142,7 @@ list_t *node_get_children(struct sway_node *node) { | |||
143 | 142 | ||
144 | bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { | 143 | bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { |
145 | if (ancestor->type == N_ROOT && node->type == N_CONTAINER && | 144 | if (ancestor->type == N_ROOT && node->type == N_CONTAINER && |
146 | node->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 145 | node->sway_container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
147 | return true; | 146 | return true; |
148 | } | 147 | } |
149 | struct sway_node *parent = node_get_parent(node); | 148 | struct sway_node *parent = node_get_parent(node); |
@@ -152,10 +151,39 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { | |||
152 | return true; | 151 | return true; |
153 | } | 152 | } |
154 | if (ancestor->type == N_ROOT && parent->type == N_CONTAINER && | 153 | if (ancestor->type == N_ROOT && parent->type == N_CONTAINER && |
155 | parent->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 154 | parent->sway_container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
156 | return true; | 155 | return true; |
157 | } | 156 | } |
158 | parent = node_get_parent(parent); | 157 | parent = node_get_parent(parent); |
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 a8ae30f7..2d11195e 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -1,14 +1,13 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <ctype.h> | 2 | #include <ctype.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <strings.h> | 4 | #include <strings.h> |
6 | #include <wlr/types/wlr_output_damage.h> | ||
7 | #include "sway/ipc-server.h" | 5 | #include "sway/ipc-server.h" |
8 | #include "sway/layers.h" | 6 | #include "sway/layers.h" |
9 | #include "sway/output.h" | 7 | #include "sway/output.h" |
10 | #include "sway/tree/arrange.h" | 8 | #include "sway/tree/arrange.h" |
11 | #include "sway/tree/workspace.h" | 9 | #include "sway/tree/workspace.h" |
10 | #include "sway/server.h" | ||
12 | #include "log.h" | 11 | #include "log.h" |
13 | #include "util.h" | 12 | #include "util.h" |
14 | 13 | ||
@@ -56,8 +55,8 @@ static void restore_workspaces(struct sway_output *output) { | |||
56 | } | 55 | } |
57 | 56 | ||
58 | // Saved workspaces | 57 | // Saved workspaces |
59 | while (root->noop_output->workspaces->length) { | 58 | while (root->fallback_output->workspaces->length) { |
60 | struct sway_workspace *ws = root->noop_output->workspaces->items[0]; | 59 | struct sway_workspace *ws = root->fallback_output->workspaces->items[0]; |
61 | workspace_detach(ws); | 60 | workspace_detach(ws); |
62 | output_add_workspace(output, ws); | 61 | output_add_workspace(output, ws); |
63 | 62 | ||
@@ -70,13 +69,13 @@ static void restore_workspaces(struct sway_output *output) { | |||
70 | // floater re-centered | 69 | // floater re-centered |
71 | for (int i = 0; i < ws->floating->length; i++) { | 70 | for (int i = 0; i < ws->floating->length; i++) { |
72 | struct sway_container *floater = ws->floating->items[i]; | 71 | struct sway_container *floater = ws->floating->items[i]; |
73 | if (floater->width == 0 || floater->height == 0 || | 72 | if (floater->pending.width == 0 || floater->pending.height == 0 || |
74 | floater->width > output->width || | 73 | floater->pending.width > output->width || |
75 | floater->height > output->height || | 74 | floater->pending.height > output->height || |
76 | floater->x > output->lx + output->width || | 75 | floater->pending.x > output->lx + output->width || |
77 | floater->y > output->ly + output->height || | 76 | floater->pending.y > output->ly + output->height || |
78 | floater->x + floater->width < output->lx || | 77 | floater->pending.x + floater->pending.width < output->lx || |
79 | floater->y + floater->height < output->ly) { | 78 | floater->pending.y + floater->pending.height < output->ly) { |
80 | container_floating_resize_and_center(floater); | 79 | container_floating_resize_and_center(floater); |
81 | } | 80 | } |
82 | } | 81 | } |
@@ -87,26 +86,63 @@ static void restore_workspaces(struct sway_output *output) { | |||
87 | output_sort_workspaces(output); | 86 | output_sort_workspaces(output); |
88 | } | 87 | } |
89 | 88 | ||
89 | static void destroy_scene_layers(struct sway_output *output) { | ||
90 | wlr_scene_node_destroy(&output->fullscreen_background->node); | ||
91 | |||
92 | scene_node_disown_children(output->layers.tiling); | ||
93 | scene_node_disown_children(output->layers.fullscreen); | ||
94 | |||
95 | wlr_scene_node_destroy(&output->layers.shell_background->node); | ||
96 | wlr_scene_node_destroy(&output->layers.shell_bottom->node); | ||
97 | wlr_scene_node_destroy(&output->layers.tiling->node); | ||
98 | wlr_scene_node_destroy(&output->layers.fullscreen->node); | ||
99 | wlr_scene_node_destroy(&output->layers.shell_top->node); | ||
100 | wlr_scene_node_destroy(&output->layers.shell_overlay->node); | ||
101 | wlr_scene_node_destroy(&output->layers.session_lock->node); | ||
102 | } | ||
103 | |||
90 | struct sway_output *output_create(struct wlr_output *wlr_output) { | 104 | struct sway_output *output_create(struct wlr_output *wlr_output) { |
91 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); | 105 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); |
92 | node_init(&output->node, N_OUTPUT, output); | 106 | node_init(&output->node, N_OUTPUT, output); |
107 | |||
108 | bool failed = false; | ||
109 | output->layers.shell_background = alloc_scene_tree(root->staging, &failed); | ||
110 | output->layers.shell_bottom = alloc_scene_tree(root->staging, &failed); | ||
111 | output->layers.tiling = alloc_scene_tree(root->staging, &failed); | ||
112 | output->layers.fullscreen = alloc_scene_tree(root->staging, &failed); | ||
113 | output->layers.shell_top = alloc_scene_tree(root->staging, &failed); | ||
114 | output->layers.shell_overlay = alloc_scene_tree(root->staging, &failed); | ||
115 | output->layers.session_lock = alloc_scene_tree(root->staging, &failed); | ||
116 | |||
117 | if (!failed) { | ||
118 | output->fullscreen_background = wlr_scene_rect_create( | ||
119 | output->layers.fullscreen, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f}); | ||
120 | |||
121 | if (!output->fullscreen_background) { | ||
122 | sway_log(SWAY_ERROR, "Unable to allocate a background rect"); | ||
123 | failed = true; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | if (failed) { | ||
128 | destroy_scene_layers(output); | ||
129 | wlr_scene_output_destroy(output->scene_output); | ||
130 | free(output); | ||
131 | return NULL; | ||
132 | } | ||
133 | |||
93 | output->wlr_output = wlr_output; | 134 | output->wlr_output = wlr_output; |
94 | wlr_output->data = output; | 135 | wlr_output->data = output; |
95 | output->detected_subpixel = wlr_output->subpixel; | 136 | output->detected_subpixel = wlr_output->subpixel; |
96 | output->scale_filter = SCALE_FILTER_NEAREST; | 137 | output->scale_filter = SCALE_FILTER_NEAREST; |
97 | 138 | ||
98 | wl_signal_init(&output->events.destroy); | 139 | wl_signal_init(&output->events.disable); |
99 | 140 | ||
100 | wl_list_insert(&root->all_outputs, &output->link); | 141 | wl_list_insert(&root->all_outputs, &output->link); |
101 | 142 | ||
102 | output->workspaces = create_list(); | 143 | output->workspaces = create_list(); |
103 | output->current.workspaces = create_list(); | 144 | output->current.workspaces = create_list(); |
104 | 145 | ||
105 | size_t len = sizeof(output->layers) / sizeof(output->layers[0]); | ||
106 | for (size_t i = 0; i < len; ++i) { | ||
107 | wl_list_init(&output->layers[i]); | ||
108 | } | ||
109 | |||
110 | return output; | 146 | return output; |
111 | } | 147 | } |
112 | 148 | ||
@@ -146,7 +182,7 @@ void output_enable(struct sway_output *output) { | |||
146 | 182 | ||
147 | input_manager_configure_xcursor(); | 183 | input_manager_configure_xcursor(); |
148 | 184 | ||
149 | wl_signal_emit(&root->events.new_node, &output->node); | 185 | wl_signal_emit_mutable(&root->events.new_node, &output->node); |
150 | 186 | ||
151 | arrange_layers(output); | 187 | arrange_layers(output); |
152 | arrange_root(); | 188 | arrange_root(); |
@@ -192,7 +228,7 @@ static void output_evacuate(struct sway_output *output) { | |||
192 | new_output = fallback_output; | 228 | new_output = fallback_output; |
193 | } | 229 | } |
194 | if (!new_output) { | 230 | if (!new_output) { |
195 | new_output = root->noop_output; | 231 | new_output = root->fallback_output; |
196 | } | 232 | } |
197 | 233 | ||
198 | struct sway_workspace *new_output_ws = | 234 | struct sway_workspace *new_output_ws = |
@@ -238,20 +274,14 @@ void output_destroy(struct sway_output *output) { | |||
238 | "which is still referenced by transactions")) { | 274 | "which is still referenced by transactions")) { |
239 | return; | 275 | return; |
240 | } | 276 | } |
277 | |||
278 | destroy_scene_layers(output); | ||
241 | list_free(output->workspaces); | 279 | list_free(output->workspaces); |
242 | list_free(output->current.workspaces); | 280 | list_free(output->current.workspaces); |
243 | wl_event_source_remove(output->repaint_timer); | 281 | wl_event_source_remove(output->repaint_timer); |
244 | free(output); | 282 | free(output); |
245 | } | 283 | } |
246 | 284 | ||
247 | static void untrack_output(struct sway_container *con, void *data) { | ||
248 | struct sway_output *output = data; | ||
249 | int index = list_find(con->outputs, output); | ||
250 | if (index != -1) { | ||
251 | list_del(con->outputs, index); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | void output_disable(struct sway_output *output) { | 285 | void output_disable(struct sway_output *output) { |
256 | if (!sway_assert(output->enabled, "Expected an enabled output")) { | 286 | if (!sway_assert(output->enabled, "Expected an enabled output")) { |
257 | return; | 287 | return; |
@@ -262,23 +292,20 @@ void output_disable(struct sway_output *output) { | |||
262 | } | 292 | } |
263 | 293 | ||
264 | sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); | 294 | sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); |
265 | wl_signal_emit(&output->events.destroy, output); | 295 | wl_signal_emit_mutable(&output->events.disable, output); |
266 | 296 | ||
267 | output_evacuate(output); | 297 | output_evacuate(output); |
268 | 298 | ||
269 | root_for_each_container(untrack_output, output); | ||
270 | |||
271 | list_del(root->outputs, index); | 299 | list_del(root->outputs, index); |
272 | 300 | ||
273 | output->enabled = false; | 301 | output->enabled = false; |
274 | output->current_mode = NULL; | ||
275 | 302 | ||
276 | arrange_root(); | 303 | arrange_root(); |
277 | 304 | ||
278 | // Reconfigure all devices, since devices with map_to_output directives for | 305 | // Reconfigure all devices, since devices with map_to_output directives for |
279 | // an output that goes offline should stop sending events as long as the | 306 | // an output that goes offline should stop sending events as long as the |
280 | // output remains offline. | 307 | // output remains offline. |
281 | input_manager_configure_all_inputs(); | 308 | input_manager_configure_all_input_mappings(); |
282 | } | 309 | } |
283 | 310 | ||
284 | void output_begin_destroy(struct sway_output *output) { | 311 | void output_begin_destroy(struct sway_output *output) { |
@@ -286,13 +313,10 @@ void output_begin_destroy(struct sway_output *output) { | |||
286 | return; | 313 | return; |
287 | } | 314 | } |
288 | sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); | 315 | sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); |
316 | wl_signal_emit_mutable(&output->node.events.destroy, &output->node); | ||
289 | 317 | ||
290 | output->node.destroying = true; | 318 | output->node.destroying = true; |
291 | node_set_dirty(&output->node); | 319 | node_set_dirty(&output->node); |
292 | |||
293 | wl_list_remove(&output->link); | ||
294 | output->wlr_output->data = NULL; | ||
295 | output->wlr_output = NULL; | ||
296 | } | 320 | } |
297 | 321 | ||
298 | struct sway_output *output_from_wlr_output(struct wlr_output *output) { | 322 | struct sway_output *output_from_wlr_output(struct wlr_output *output) { |
@@ -304,10 +328,10 @@ struct sway_output *output_get_in_direction(struct sway_output *reference, | |||
304 | if (!sway_assert(direction, "got invalid direction: %d", direction)) { | 328 | if (!sway_assert(direction, "got invalid direction: %d", direction)) { |
305 | return NULL; | 329 | return NULL; |
306 | } | 330 | } |
307 | struct wlr_box *output_box = | 331 | struct wlr_box output_box; |
308 | wlr_output_layout_get_box(root->output_layout, reference->wlr_output); | 332 | wlr_output_layout_get_box(root->output_layout, reference->wlr_output, &output_box); |
309 | int lx = output_box->x + output_box->width / 2; | 333 | int lx = output_box.x + output_box.width / 2; |
310 | int ly = output_box->y + output_box->height / 2; | 334 | int ly = output_box.y + output_box.height / 2; |
311 | struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( | 335 | struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( |
312 | root->output_layout, direction, reference->wlr_output, lx, ly); | 336 | root->output_layout, direction, reference->wlr_output, lx, ly); |
313 | if (!wlr_adjacent) { | 337 | if (!wlr_adjacent) { |
@@ -393,6 +417,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) { | |||
393 | box->height = output->height; | 417 | box->height = output->height; |
394 | } | 418 | } |
395 | 419 | ||
420 | static void handle_destroy_non_desktop(struct wl_listener *listener, void *data) { | ||
421 | struct sway_output_non_desktop *output = | ||
422 | wl_container_of(listener, output, destroy); | ||
423 | |||
424 | sway_log(SWAY_DEBUG, "Destroying non-desktop output '%s'", output->wlr_output->name); | ||
425 | |||
426 | int index = list_find(root->non_desktop_outputs, output); | ||
427 | list_del(root->non_desktop_outputs, index); | ||
428 | |||
429 | wl_list_remove(&output->destroy.link); | ||
430 | |||
431 | free(output); | ||
432 | } | ||
433 | |||
434 | struct sway_output_non_desktop *output_non_desktop_create( | ||
435 | struct wlr_output *wlr_output) { | ||
436 | struct sway_output_non_desktop *output = | ||
437 | calloc(1, sizeof(struct sway_output_non_desktop)); | ||
438 | |||
439 | output->wlr_output = wlr_output; | ||
440 | |||
441 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | ||
442 | output->destroy.notify = handle_destroy_non_desktop; | ||
443 | |||
444 | return output; | ||
445 | } | ||
446 | |||
396 | enum sway_container_layout output_get_default_layout( | 447 | enum sway_container_layout output_get_default_layout( |
397 | struct sway_output *output) { | 448 | struct sway_output *output) { |
398 | if (config->default_orientation != L_NONE) { | 449 | if (config->default_orientation != L_NONE) { |
diff --git a/sway/tree/root.c b/sway/tree/root.c index ebd185ec..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,21 +90,34 @@ 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; |
60 | } | 112 | } |
61 | 113 | ||
62 | struct sway_container *parent = con->parent; | 114 | struct sway_container *parent = con->pending.parent; |
63 | struct sway_workspace *workspace = con->workspace; | 115 | struct sway_workspace *workspace = con->pending.workspace; |
116 | |||
117 | set_container_transform(workspace, con); | ||
64 | 118 | ||
65 | // Clear the fullscreen mode when sending to the scratchpad | 119 | // Clear the fullscreen mode when sending to the scratchpad |
66 | if (con->fullscreen_mode != FULLSCREEN_NONE) { | 120 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { |
67 | container_fullscreen_disable(con); | 121 | container_fullscreen_disable(con); |
68 | } | 122 | } |
69 | 123 | ||
@@ -117,7 +171,7 @@ void root_scratchpad_show(struct sway_container *con) { | |||
117 | sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on"); | 171 | sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on"); |
118 | return; | 172 | return; |
119 | } | 173 | } |
120 | struct sway_workspace *old_ws = con->workspace; | 174 | struct sway_workspace *old_ws = con->pending.workspace; |
121 | 175 | ||
122 | // If the current con or any of its parents are in fullscreen mode, we | 176 | // If the current con or any of its parents are in fullscreen mode, we |
123 | // first need to disable it before showing the scratchpad con. | 177 | // first need to disable it before showing the scratchpad con. |
@@ -131,31 +185,34 @@ 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->parent) { | 194 | while (con->pending.parent) { |
138 | con = con->parent; | 195 | con = con->pending.parent; |
139 | } | 196 | } |
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->x + con->width / 2; | 201 | struct wlr_box output_box; |
145 | double center_ly = con->y + con->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) { |
158 | if (con->fullscreen_mode != FULLSCREEN_NONE) { | 215 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { |
159 | container_fullscreen_disable(con); | 216 | container_fullscreen_disable(con); |
160 | } | 217 | } |
161 | } | 218 | } |
@@ -163,14 +220,16 @@ static void disable_fullscreen(struct sway_container *con, void *data) { | |||
163 | void root_scratchpad_hide(struct sway_container *con) { | 220 | void root_scratchpad_hide(struct sway_container *con) { |
164 | struct sway_seat *seat = input_manager_current_seat(); | 221 | struct sway_seat *seat = input_manager_current_seat(); |
165 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); | 222 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); |
166 | struct sway_workspace *ws = con->workspace; | 223 | struct sway_workspace *ws = con->pending.workspace; |
167 | 224 | ||
168 | if (con->fullscreen_mode == FULLSCREEN_GLOBAL && !con->workspace) { | 225 | if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL && !con->pending.workspace) { |
169 | // If the container was made fullscreen global while in the scratchpad, | 226 | // If the container was made fullscreen global while in the scratchpad, |
170 | // it should be shown until fullscreen has been disabled | 227 | // it should be shown until fullscreen has been disabled |
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,163 +242,6 @@ void root_scratchpad_hide(struct sway_container *con) { | |||
183 | ipc_event_window(con, "move"); | 242 | ipc_event_window(con, "move"); |
184 | } | 243 | } |
185 | 244 | ||
186 | struct pid_workspace { | ||
187 | pid_t pid; | ||
188 | char *workspace; | ||
189 | struct timespec time_added; | ||
190 | |||
191 | struct sway_output *output; | ||
192 | struct wl_listener output_destroy; | ||
193 | |||
194 | struct wl_list link; | ||
195 | }; | ||
196 | |||
197 | static struct wl_list pid_workspaces; | ||
198 | |||
199 | /** | ||
200 | * Get the pid of a parent process given the pid of a child process. | ||
201 | * | ||
202 | * Returns the parent pid or NULL if the parent pid cannot be determined. | ||
203 | */ | ||
204 | static pid_t get_parent_pid(pid_t child) { | ||
205 | pid_t parent = -1; | ||
206 | char file_name[100]; | ||
207 | char *buffer = NULL; | ||
208 | const char *sep = " "; | ||
209 | FILE *stat = NULL; | ||
210 | size_t buf_size = 0; | ||
211 | |||
212 | sprintf(file_name, "/proc/%d/stat", child); | ||
213 | |||
214 | if ((stat = fopen(file_name, "r"))) { | ||
215 | if (getline(&buffer, &buf_size, stat) != -1) { | ||
216 | strtok(buffer, sep); // pid | ||
217 | strtok(NULL, sep); // executable name | ||
218 | strtok(NULL, sep); // state | ||
219 | char *token = strtok(NULL, sep); // parent pid | ||
220 | parent = strtol(token, NULL, 10); | ||
221 | } | ||
222 | free(buffer); | ||
223 | fclose(stat); | ||
224 | } | ||
225 | |||
226 | if (parent) { | ||
227 | return (parent == child) ? -1 : parent; | ||
228 | } | ||
229 | |||
230 | return -1; | ||
231 | } | ||
232 | |||
233 | static void pid_workspace_destroy(struct pid_workspace *pw) { | ||
234 | wl_list_remove(&pw->output_destroy.link); | ||
235 | wl_list_remove(&pw->link); | ||
236 | free(pw->workspace); | ||
237 | free(pw); | ||
238 | } | ||
239 | |||
240 | struct sway_workspace *root_workspace_for_pid(pid_t pid) { | ||
241 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
242 | wl_list_init(&pid_workspaces); | ||
243 | return NULL; | ||
244 | } | ||
245 | |||
246 | struct sway_workspace *ws = NULL; | ||
247 | struct pid_workspace *pw = NULL; | ||
248 | |||
249 | sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); | ||
250 | |||
251 | do { | ||
252 | struct pid_workspace *_pw = NULL; | ||
253 | wl_list_for_each(_pw, &pid_workspaces, link) { | ||
254 | if (pid == _pw->pid) { | ||
255 | pw = _pw; | ||
256 | sway_log(SWAY_DEBUG, | ||
257 | "found pid_workspace for pid %d, workspace %s", | ||
258 | pid, pw->workspace); | ||
259 | goto found; | ||
260 | } | ||
261 | } | ||
262 | pid = get_parent_pid(pid); | ||
263 | } while (pid > 1); | ||
264 | found: | ||
265 | |||
266 | if (pw && pw->workspace) { | ||
267 | ws = workspace_by_name(pw->workspace); | ||
268 | |||
269 | if (!ws) { | ||
270 | sway_log(SWAY_DEBUG, | ||
271 | "Creating workspace %s for pid %d because it disappeared", | ||
272 | pw->workspace, pid); | ||
273 | ws = workspace_create(pw->output, pw->workspace); | ||
274 | } | ||
275 | |||
276 | pid_workspace_destroy(pw); | ||
277 | } | ||
278 | |||
279 | return ws; | ||
280 | } | ||
281 | |||
282 | static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { | ||
283 | struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); | ||
284 | pw->output = NULL; | ||
285 | wl_list_remove(&pw->output_destroy.link); | ||
286 | wl_list_init(&pw->output_destroy.link); | ||
287 | } | ||
288 | |||
289 | void root_record_workspace_pid(pid_t pid) { | ||
290 | sway_log(SWAY_DEBUG, "Recording workspace for process %d", pid); | ||
291 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
292 | wl_list_init(&pid_workspaces); | ||
293 | } | ||
294 | |||
295 | struct sway_seat *seat = input_manager_current_seat(); | ||
296 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | ||
297 | if (!ws) { | ||
298 | sway_log(SWAY_DEBUG, "Bailing out, no workspace"); | ||
299 | return; | ||
300 | } | ||
301 | struct sway_output *output = ws->output; | ||
302 | if (!output) { | ||
303 | sway_log(SWAY_DEBUG, "Bailing out, no output"); | ||
304 | return; | ||
305 | } | ||
306 | |||
307 | struct timespec now; | ||
308 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
309 | |||
310 | // Remove expired entries | ||
311 | static const int timeout = 60; | ||
312 | struct pid_workspace *old, *_old; | ||
313 | wl_list_for_each_safe(old, _old, &pid_workspaces, link) { | ||
314 | if (now.tv_sec - old->time_added.tv_sec >= timeout) { | ||
315 | pid_workspace_destroy(old); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); | ||
320 | pw->workspace = strdup(ws->name); | ||
321 | pw->output = output; | ||
322 | pw->pid = pid; | ||
323 | memcpy(&pw->time_added, &now, sizeof(struct timespec)); | ||
324 | pw->output_destroy.notify = pw_handle_output_destroy; | ||
325 | wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy); | ||
326 | wl_list_insert(&pid_workspaces, &pw->link); | ||
327 | } | ||
328 | |||
329 | void root_remove_workspace_pid(pid_t pid) { | ||
330 | if (!pid_workspaces.prev || !pid_workspaces.next) { | ||
331 | return; | ||
332 | } | ||
333 | |||
334 | struct pid_workspace *pw, *tmp; | ||
335 | wl_list_for_each_safe(pw, tmp, &pid_workspaces, link) { | ||
336 | if (pid == pw->pid) { | ||
337 | pid_workspace_destroy(pw); | ||
338 | return; | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | |||
343 | 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), |
344 | void *data) { | 246 | void *data) { |
345 | for (int i = 0; i < root->outputs->length; ++i) { | 247 | for (int i = 0; i < root->outputs->length; ++i) { |
@@ -365,8 +267,8 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data), | |||
365 | } | 267 | } |
366 | 268 | ||
367 | // Saved workspaces | 269 | // Saved workspaces |
368 | for (int i = 0; i < root->noop_output->workspaces->length; ++i) { | 270 | for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { |
369 | struct sway_workspace *ws = root->noop_output->workspaces->items[i]; | 271 | struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; |
370 | workspace_for_each_container(ws, f, data); | 272 | workspace_for_each_container(ws, f, data); |
371 | } | 273 | } |
372 | } | 274 | } |
@@ -418,8 +320,8 @@ struct sway_container *root_find_container( | |||
418 | } | 320 | } |
419 | 321 | ||
420 | // Saved workspaces | 322 | // Saved workspaces |
421 | for (int i = 0; i < root->noop_output->workspaces->length; ++i) { | 323 | for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { |
422 | struct sway_workspace *ws = root->noop_output->workspaces->items[i]; | 324 | struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; |
423 | if ((result = workspace_find_container(ws, test, data))) { | 325 | if ((result = workspace_find_container(ws, test, data))) { |
424 | return result; | 326 | return result; |
425 | } | 327 | } |
@@ -434,17 +336,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) { | |||
434 | box->width = root->width; | 336 | box->width = root->width; |
435 | box->height = root->height; | 337 | box->height = root->height; |
436 | } | 338 | } |
437 | |||
438 | void root_rename_pid_workspaces(const char *old_name, const char *new_name) { | ||
439 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
440 | wl_list_init(&pid_workspaces); | ||
441 | } | ||
442 | |||
443 | struct pid_workspace *pw = NULL; | ||
444 | wl_list_for_each(pw, &pid_workspaces, link) { | ||
445 | if (strcmp(pw->workspace, old_name) == 0) { | ||
446 | free(pw->workspace); | ||
447 | pw->workspace = strdup(new_name); | ||
448 | } | ||
449 | } | ||
450 | } | ||
diff --git a/sway/tree/view.c b/sway/tree/view.c index 7afcdf31..35b4b73f 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -1,11 +1,13 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include <wayland-server-core.h> | 3 | #include <wayland-server-core.h> |
5 | #include <wlr/render/wlr_renderer.h> | 4 | #include <wlr/render/wlr_renderer.h> |
6 | #include <wlr/types/wlr_buffer.h> | 5 | #include <wlr/types/wlr_buffer.h> |
6 | #include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h> | ||
7 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
7 | #include <wlr/types/wlr_output_layout.h> | 8 | #include <wlr/types/wlr_output_layout.h> |
8 | #include <wlr/types/wlr_server_decoration.h> | 9 | #include <wlr/types/wlr_server_decoration.h> |
10 | #include <wlr/types/wlr_subcompositor.h> | ||
9 | #include <wlr/types/wlr_xdg_decoration_v1.h> | 11 | #include <wlr/types/wlr_xdg_decoration_v1.h> |
10 | #include "config.h" | 12 | #include "config.h" |
11 | #if HAVE_XWAYLAND | 13 | #if HAVE_XWAYLAND |
@@ -15,14 +17,16 @@ | |||
15 | #include "log.h" | 17 | #include "log.h" |
16 | #include "sway/criteria.h" | 18 | #include "sway/criteria.h" |
17 | #include "sway/commands.h" | 19 | #include "sway/commands.h" |
18 | #include "sway/desktop.h" | ||
19 | #include "sway/desktop/transaction.h" | 20 | #include "sway/desktop/transaction.h" |
20 | #include "sway/desktop/idle_inhibit_v1.h" | 21 | #include "sway/desktop/idle_inhibit_v1.h" |
22 | #include "sway/desktop/launcher.h" | ||
21 | #include "sway/input/cursor.h" | 23 | #include "sway/input/cursor.h" |
22 | #include "sway/ipc-server.h" | 24 | #include "sway/ipc-server.h" |
23 | #include "sway/output.h" | 25 | #include "sway/output.h" |
24 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/scene_descriptor.h" | ||
25 | #include "sway/server.h" | 28 | #include "sway/server.h" |
29 | #include "sway/sway_text_node.h" | ||
26 | #include "sway/tree/arrange.h" | 30 | #include "sway/tree/arrange.h" |
27 | #include "sway/tree/container.h" | 31 | #include "sway/tree/container.h" |
28 | #include "sway/tree/view.h" | 32 | #include "sway/tree/view.h" |
@@ -32,15 +36,29 @@ | |||
32 | #include "pango.h" | 36 | #include "pango.h" |
33 | #include "stringop.h" | 37 | #include "stringop.h" |
34 | 38 | ||
35 | void view_init(struct sway_view *view, enum sway_view_type type, | 39 | bool view_init(struct sway_view *view, enum sway_view_type type, |
36 | const struct sway_view_impl *impl) { | 40 | const struct sway_view_impl *impl) { |
41 | bool failed = false; | ||
42 | view->scene_tree = alloc_scene_tree(root->staging, &failed); | ||
43 | view->content_tree = alloc_scene_tree(view->scene_tree, &failed); | ||
44 | |||
45 | if (!failed && !scene_descriptor_assign(&view->scene_tree->node, | ||
46 | SWAY_SCENE_DESC_VIEW, view)) { | ||
47 | failed = true; | ||
48 | } | ||
49 | |||
50 | if (failed) { | ||
51 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
52 | return false; | ||
53 | } | ||
54 | |||
37 | view->type = type; | 55 | view->type = type; |
38 | view->impl = impl; | 56 | view->impl = impl; |
39 | view->executed_criteria = create_list(); | 57 | view->executed_criteria = create_list(); |
40 | wl_list_init(&view->saved_buffers); | ||
41 | view->allow_request_urgent = true; | 58 | view->allow_request_urgent = true; |
42 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; | 59 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; |
43 | wl_signal_init(&view->events.unmap); | 60 | wl_signal_init(&view->events.unmap); |
61 | return true; | ||
44 | } | 62 | } |
45 | 63 | ||
46 | void view_destroy(struct sway_view *view) { | 64 | void view_destroy(struct sway_view *view) { |
@@ -56,11 +74,11 @@ void view_destroy(struct sway_view *view) { | |||
56 | "(might have a pending transaction?)")) { | 74 | "(might have a pending transaction?)")) { |
57 | return; | 75 | return; |
58 | } | 76 | } |
59 | if (!wl_list_empty(&view->saved_buffers)) { | 77 | wl_list_remove(&view->events.unmap.listener_list); |
60 | view_remove_saved_buffer(view); | ||
61 | } | ||
62 | list_free(view->executed_criteria); | 78 | list_free(view->executed_criteria); |
63 | 79 | ||
80 | view_assign_ctx(view, NULL); | ||
81 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
64 | free(view->title_format); | 82 | free(view->title_format); |
65 | 83 | ||
66 | if (view->impl->destroy) { | 84 | if (view->impl->destroy) { |
@@ -206,7 +224,7 @@ bool view_ancestor_is_only_visible(struct sway_view *view) { | |||
206 | } else { | 224 | } else { |
207 | only_visible = true; | 225 | only_visible = true; |
208 | } | 226 | } |
209 | con = con->parent; | 227 | con = con->pending.parent; |
210 | } | 228 | } |
211 | return only_visible; | 229 | return only_visible; |
212 | } | 230 | } |
@@ -222,72 +240,73 @@ static bool view_is_only_visible(struct sway_view *view) { | |||
222 | } | 240 | } |
223 | } | 241 | } |
224 | 242 | ||
225 | con = con->parent; | 243 | con = con->pending.parent; |
226 | } | 244 | } |
227 | 245 | ||
228 | return true; | 246 | return true; |
229 | } | 247 | } |
230 | 248 | ||
231 | static bool gaps_to_edge(struct sway_view *view) { | 249 | static bool gaps_to_edge(struct sway_view *view) { |
232 | struct side_gaps gaps = view->container->workspace->current_gaps; | 250 | struct side_gaps gaps = view->container->pending.workspace->current_gaps; |
233 | return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; | 251 | return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; |
234 | } | 252 | } |
235 | 253 | ||
236 | void view_autoconfigure(struct sway_view *view) { | 254 | void view_autoconfigure(struct sway_view *view) { |
237 | struct sway_container *con = view->container; | 255 | struct sway_container *con = view->container; |
238 | struct sway_workspace *ws = con->workspace; | 256 | struct sway_workspace *ws = con->pending.workspace; |
239 | 257 | ||
240 | if (container_is_scratchpad_hidden(con) && | 258 | if (container_is_scratchpad_hidden(con) && |
241 | con->fullscreen_mode != FULLSCREEN_GLOBAL) { | 259 | con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { |
242 | return; | 260 | return; |
243 | } | 261 | } |
244 | struct sway_output *output = ws ? ws->output : NULL; | 262 | struct sway_output *output = ws ? ws->output : NULL; |
245 | 263 | ||
246 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 264 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
247 | con->content_x = output->lx; | 265 | con->pending.content_x = output->lx; |
248 | con->content_y = output->ly; | 266 | con->pending.content_y = output->ly; |
249 | con->content_width = output->width; | 267 | con->pending.content_width = output->width; |
250 | con->content_height = output->height; | 268 | con->pending.content_height = output->height; |
251 | return; | 269 | return; |
252 | } else if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { | 270 | } else if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
253 | con->content_x = root->x; | 271 | con->pending.content_x = root->x; |
254 | con->content_y = root->y; | 272 | con->pending.content_y = root->y; |
255 | con->content_width = root->width; | 273 | con->pending.content_width = root->width; |
256 | con->content_height = root->height; | 274 | con->pending.content_height = root->height; |
257 | return; | 275 | return; |
258 | } | 276 | } |
259 | 277 | ||
260 | con->border_top = con->border_bottom = true; | 278 | con->pending.border_top = con->pending.border_bottom = true; |
261 | con->border_left = con->border_right = true; | 279 | con->pending.border_left = con->pending.border_right = true; |
262 | double y_offset = 0; | 280 | double y_offset = 0; |
263 | 281 | ||
264 | if (!container_is_floating(con) && ws) { | 282 | if (!container_is_floating_or_child(con) && ws) { |
265 | if (config->hide_edge_borders == E_BOTH | 283 | if (config->hide_edge_borders == E_BOTH |
266 | || config->hide_edge_borders == E_VERTICAL) { | 284 | || config->hide_edge_borders == E_VERTICAL) { |
267 | con->border_left = con->x != ws->x; | 285 | con->pending.border_left = con->pending.x != ws->x; |
268 | int right_x = con->x + con->width; | 286 | int right_x = con->pending.x + con->pending.width; |
269 | con->border_right = right_x != ws->x + ws->width; | 287 | con->pending.border_right = right_x != ws->x + ws->width; |
270 | } | 288 | } |
271 | 289 | ||
272 | if (config->hide_edge_borders == E_BOTH | 290 | if (config->hide_edge_borders == E_BOTH |
273 | || config->hide_edge_borders == E_HORIZONTAL) { | 291 | || config->hide_edge_borders == E_HORIZONTAL) { |
274 | con->border_top = con->y != ws->y; | 292 | con->pending.border_top = con->pending.y != ws->y; |
275 | int bottom_y = con->y + con->height; | 293 | int bottom_y = con->pending.y + con->pending.height; |
276 | con->border_bottom = bottom_y != ws->y + ws->height; | 294 | con->pending.border_bottom = bottom_y != ws->y + ws->height; |
277 | } | 295 | } |
278 | 296 | ||
279 | bool smart = config->hide_edge_borders_smart == ESMART_ON || | 297 | bool smart = config->hide_edge_borders_smart == ESMART_ON || |
280 | (config->hide_edge_borders_smart == ESMART_NO_GAPS && | 298 | (config->hide_edge_borders_smart == ESMART_NO_GAPS && |
281 | !gaps_to_edge(view)); | 299 | !gaps_to_edge(view)); |
282 | if (smart) { | 300 | if (smart) { |
283 | bool show_border = container_is_floating_or_child(con) || | 301 | bool show_border = !view_is_only_visible(view); |
284 | !view_is_only_visible(view); | 302 | con->pending.border_left &= show_border; |
285 | con->border_left &= show_border; | 303 | con->pending.border_right &= show_border; |
286 | con->border_right &= show_border; | 304 | con->pending.border_top &= show_border; |
287 | con->border_top &= show_border; | 305 | con->pending.border_bottom &= show_border; |
288 | con->border_bottom &= show_border; | ||
289 | } | 306 | } |
307 | } | ||
290 | 308 | ||
309 | if (!container_is_floating(con)) { | ||
291 | // In a tabbed or stacked container, the container's y is the top of the | 310 | // In a tabbed or stacked container, the container's y is the top of the |
292 | // title area. We have to offset the surface y by the height of the title, | 311 | // title area. We have to offset the surface y by the height of the title, |
293 | // bar, and disable any top border because we'll always have the title bar. | 312 | // bar, and disable any top border because we'll always have the title bar. |
@@ -298,56 +317,56 @@ void view_autoconfigure(struct sway_view *view) { | |||
298 | enum sway_container_layout layout = container_parent_layout(con); | 317 | enum sway_container_layout layout = container_parent_layout(con); |
299 | if (layout == L_TABBED) { | 318 | if (layout == L_TABBED) { |
300 | y_offset = container_titlebar_height(); | 319 | y_offset = container_titlebar_height(); |
301 | con->border_top = false; | 320 | con->pending.border_top = false; |
302 | } else if (layout == L_STACKED) { | 321 | } else if (layout == L_STACKED) { |
303 | y_offset = container_titlebar_height() * siblings->length; | 322 | y_offset = container_titlebar_height() * siblings->length; |
304 | con->border_top = false; | 323 | con->pending.border_top = false; |
305 | } | 324 | } |
306 | } | 325 | } |
307 | } | 326 | } |
308 | 327 | ||
309 | double x, y, width, height; | 328 | double x, y, width, height; |
310 | switch (con->border) { | 329 | switch (con->pending.border) { |
311 | default: | 330 | default: |
312 | case B_CSD: | 331 | case B_CSD: |
313 | case B_NONE: | 332 | case B_NONE: |
314 | x = con->x; | 333 | x = con->pending.x; |
315 | y = con->y + y_offset; | 334 | y = con->pending.y + y_offset; |
316 | width = con->width; | 335 | width = con->pending.width; |
317 | height = con->height - y_offset; | 336 | height = con->pending.height - y_offset; |
318 | break; | 337 | break; |
319 | case B_PIXEL: | 338 | case B_PIXEL: |
320 | x = con->x + con->border_thickness * con->border_left; | 339 | x = con->pending.x + con->pending.border_thickness * con->pending.border_left; |
321 | y = con->y + con->border_thickness * con->border_top + y_offset; | 340 | y = con->pending.y + con->pending.border_thickness * con->pending.border_top + y_offset; |
322 | width = con->width | 341 | width = con->pending.width |
323 | - con->border_thickness * con->border_left | 342 | - con->pending.border_thickness * con->pending.border_left |
324 | - con->border_thickness * con->border_right; | 343 | - con->pending.border_thickness * con->pending.border_right; |
325 | height = con->height - y_offset | 344 | height = con->pending.height - y_offset |
326 | - con->border_thickness * con->border_top | 345 | - con->pending.border_thickness * con->pending.border_top |
327 | - con->border_thickness * con->border_bottom; | 346 | - con->pending.border_thickness * con->pending.border_bottom; |
328 | break; | 347 | break; |
329 | case B_NORMAL: | 348 | case B_NORMAL: |
330 | // Height is: 1px border + 3px pad + title height + 3px pad + 1px border | 349 | // Height is: 1px border + 3px pad + title height + 3px pad + 1px border |
331 | x = con->x + con->border_thickness * con->border_left; | 350 | x = con->pending.x + con->pending.border_thickness * con->pending.border_left; |
332 | width = con->width | 351 | width = con->pending.width |
333 | - con->border_thickness * con->border_left | 352 | - con->pending.border_thickness * con->pending.border_left |
334 | - con->border_thickness * con->border_right; | 353 | - con->pending.border_thickness * con->pending.border_right; |
335 | if (y_offset) { | 354 | if (y_offset) { |
336 | y = con->y + y_offset; | 355 | y = con->pending.y + y_offset; |
337 | height = con->height - y_offset | 356 | height = con->pending.height - y_offset |
338 | - con->border_thickness * con->border_bottom; | 357 | - con->pending.border_thickness * con->pending.border_bottom; |
339 | } else { | 358 | } else { |
340 | y = con->y + container_titlebar_height(); | 359 | y = con->pending.y + container_titlebar_height(); |
341 | height = con->height - container_titlebar_height() | 360 | height = con->pending.height - container_titlebar_height() |
342 | - con->border_thickness * con->border_bottom; | 361 | - con->pending.border_thickness * con->pending.border_bottom; |
343 | } | 362 | } |
344 | break; | 363 | break; |
345 | } | 364 | } |
346 | 365 | ||
347 | con->content_x = x; | 366 | con->pending.content_x = x; |
348 | con->content_y = y; | 367 | con->pending.content_y = y; |
349 | con->content_width = width; | 368 | con->pending.content_width = width; |
350 | con->content_height = height; | 369 | con->pending.content_height = height; |
351 | } | 370 | } |
352 | 371 | ||
353 | void view_set_activated(struct sway_view *view, bool activated) { | 372 | void view_set_activated(struct sway_view *view, bool activated) { |
@@ -360,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
360 | } | 379 | } |
361 | } | 380 | } |
362 | 381 | ||
363 | void view_request_activate(struct sway_view *view) { | 382 | void view_request_activate(struct sway_view *view, struct sway_seat *seat) { |
364 | struct sway_workspace *ws = view->container->workspace; | 383 | struct sway_workspace *ws = view->container->pending.workspace; |
365 | if (!ws) { // hidden scratchpad container | 384 | if (!seat) { |
366 | return; | 385 | seat = input_manager_current_seat(); |
367 | } | 386 | } |
368 | struct sway_seat *seat = input_manager_current_seat(); | ||
369 | 387 | ||
370 | switch (config->focus_on_window_activation) { | 388 | switch (config->focus_on_window_activation) { |
371 | case FOWA_SMART: | 389 | case FOWA_SMART: |
372 | if (workspace_is_visible(ws)) { | 390 | if (ws && workspace_is_visible(ws)) { |
373 | seat_set_focus_container(seat, view->container); | 391 | seat_set_focus_container(seat, view->container); |
392 | container_raise_floating(view->container); | ||
374 | } else { | 393 | } else { |
375 | view_set_urgent(view, true); | 394 | view_set_urgent(view, true); |
376 | } | 395 | } |
@@ -379,11 +398,23 @@ void view_request_activate(struct sway_view *view) { | |||
379 | view_set_urgent(view, true); | 398 | view_set_urgent(view, true); |
380 | break; | 399 | break; |
381 | case FOWA_FOCUS: | 400 | case FOWA_FOCUS: |
382 | 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 | } | ||
383 | break; | 407 | break; |
384 | case FOWA_NONE: | 408 | case FOWA_NONE: |
385 | break; | 409 | break; |
386 | } | 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 | } | ||
387 | } | 418 | } |
388 | 419 | ||
389 | 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) { |
@@ -401,13 +432,13 @@ void view_set_csd_from_server(struct sway_view *view, bool enabled) { | |||
401 | void view_update_csd_from_client(struct sway_view *view, bool enabled) { | 432 | void view_update_csd_from_client(struct sway_view *view, bool enabled) { |
402 | sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled); | 433 | sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled); |
403 | struct sway_container *con = view->container; | 434 | struct sway_container *con = view->container; |
404 | if (enabled && con && con->border != B_CSD) { | 435 | if (enabled && con && con->pending.border != B_CSD) { |
405 | con->saved_border = con->border; | 436 | con->saved_border = con->pending.border; |
406 | if (container_is_floating(con)) { | 437 | if (container_is_floating(con)) { |
407 | con->border = B_CSD; | 438 | con->pending.border = B_CSD; |
408 | } | 439 | } |
409 | } else if (!enabled && con && con->border == B_CSD) { | 440 | } else if (!enabled && con && con->pending.border == B_CSD) { |
410 | con->border = con->saved_border; | 441 | con->pending.border = con->saved_border; |
411 | } | 442 | } |
412 | view->using_csd = enabled; | 443 | view->using_csd = enabled; |
413 | } | 444 | } |
@@ -430,49 +461,6 @@ void view_close_popups(struct sway_view *view) { | |||
430 | } | 461 | } |
431 | } | 462 | } |
432 | 463 | ||
433 | void view_damage_from(struct sway_view *view) { | ||
434 | for (int i = 0; i < root->outputs->length; ++i) { | ||
435 | struct sway_output *output = root->outputs->items[i]; | ||
436 | output_damage_from_view(output, view); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | void view_for_each_surface(struct sway_view *view, | ||
441 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
442 | if (!view->surface) { | ||
443 | return; | ||
444 | } | ||
445 | if (view->impl->for_each_surface) { | ||
446 | view->impl->for_each_surface(view, iterator, user_data); | ||
447 | } else { | ||
448 | wlr_surface_for_each_surface(view->surface, iterator, user_data); | ||
449 | } | ||
450 | } | ||
451 | |||
452 | void view_for_each_popup_surface(struct sway_view *view, | ||
453 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
454 | if (!view->surface) { | ||
455 | return; | ||
456 | } | ||
457 | if (view->impl->for_each_popup_surface) { | ||
458 | view->impl->for_each_popup_surface(view, iterator, user_data); | ||
459 | } | ||
460 | } | ||
461 | |||
462 | static void view_subsurface_create(struct sway_view *view, | ||
463 | struct wlr_subsurface *subsurface); | ||
464 | |||
465 | static void view_init_subsurfaces(struct sway_view *view, | ||
466 | struct wlr_surface *surface); | ||
467 | |||
468 | static void view_handle_surface_new_subsurface(struct wl_listener *listener, | ||
469 | void *data) { | ||
470 | struct sway_view *view = | ||
471 | wl_container_of(listener, view, surface_new_subsurface); | ||
472 | struct wlr_subsurface *subsurface = data; | ||
473 | view_subsurface_create(view, subsurface); | ||
474 | } | ||
475 | |||
476 | static bool view_has_executed_criteria(struct sway_view *view, | 464 | static bool view_has_executed_criteria(struct sway_view *view, |
477 | struct criteria *criteria) { | 465 | struct criteria *criteria) { |
478 | for (int i = 0; i < view->executed_criteria->length; ++i) { | 466 | for (int i = 0; i < view->executed_criteria->length; ++i) { |
@@ -514,7 +502,7 @@ static void view_populate_pid(struct sway_view *view) { | |||
514 | #if HAVE_XWAYLAND | 502 | #if HAVE_XWAYLAND |
515 | case SWAY_VIEW_XWAYLAND:; | 503 | case SWAY_VIEW_XWAYLAND:; |
516 | struct wlr_xwayland_surface *surf = | 504 | struct wlr_xwayland_surface *surf = |
517 | wlr_xwayland_surface_from_wlr_surface(view->surface); | 505 | wlr_xwayland_surface_try_from_wlr_surface(view->surface); |
518 | pid = surf->pid; | 506 | pid = surf->pid; |
519 | break; | 507 | break; |
520 | #endif | 508 | #endif |
@@ -527,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) { | |||
527 | view->pid = pid; | 515 | view->pid = pid; |
528 | } | 516 | } |
529 | 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 | |||
530 | static struct sway_workspace *select_workspace(struct sway_view *view) { | 532 | static struct sway_workspace *select_workspace(struct sway_view *view) { |
531 | struct sway_seat *seat = input_manager_current_seat(); | 533 | struct sway_seat *seat = input_manager_current_seat(); |
532 | 534 | ||
@@ -562,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
562 | } | 564 | } |
563 | list_free(criterias); | 565 | list_free(criterias); |
564 | if (ws) { | 566 | if (ws) { |
565 | root_remove_workspace_pid(view->pid); | 567 | view_assign_ctx(view, NULL); |
566 | return ws; | 568 | return ws; |
567 | } | 569 | } |
568 | 570 | ||
569 | // Check if there's a PID mapping | 571 | // Check if there's a PID mapping |
570 | ws = root_workspace_for_pid(view->pid); | 572 | ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL; |
571 | if (ws) { | 573 | if (ws) { |
574 | view_assign_ctx(view, NULL); | ||
572 | return ws; | 575 | return ws; |
573 | } | 576 | } |
574 | 577 | ||
@@ -577,7 +580,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
577 | if (node && node->type == N_WORKSPACE) { | 580 | if (node && node->type == N_WORKSPACE) { |
578 | return node->sway_workspace; | 581 | return node->sway_workspace; |
579 | } else if (node && node->type == N_CONTAINER) { | 582 | } else if (node && node->type == N_CONTAINER) { |
580 | return node->sway_container->workspace; | 583 | return node->sway_container->pending.workspace; |
581 | } | 584 | } |
582 | 585 | ||
583 | // When there's no outputs connected, the above should match a workspace on | 586 | // When there's no outputs connected, the above should match a workspace on |
@@ -586,16 +589,29 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
586 | return NULL; | 589 | return NULL; |
587 | } | 590 | } |
588 | 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 | |||
589 | static bool should_focus(struct sway_view *view) { | 600 | static bool should_focus(struct sway_view *view) { |
590 | struct sway_seat *seat = input_manager_current_seat(); | 601 | struct sway_seat *seat = input_manager_current_seat(); |
591 | struct sway_container *prev_con = seat_get_focused_container(seat); | 602 | struct sway_container *prev_con = seat_get_focused_container(seat); |
592 | struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); | 603 | struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); |
593 | struct sway_workspace *map_ws = view->container->workspace; | 604 | struct sway_workspace *map_ws = view->container->pending.workspace; |
594 | 605 | ||
595 | if (view->container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 606 | if (view->container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
596 | return true; | 607 | return true; |
597 | } | 608 | } |
598 | 609 | ||
610 | // View opened "under" fullscreen view should not be given focus. | ||
611 | if (root->fullscreen_global || !map_ws || map_ws->fullscreen) { | ||
612 | return false; | ||
613 | } | ||
614 | |||
599 | // Views can only take focus if they are mapped into the active workspace | 615 | // Views can only take focus if they are mapped into the active workspace |
600 | if (prev_ws != map_ws) { | 616 | if (prev_ws != map_ws) { |
601 | return false; | 617 | return false; |
@@ -603,9 +619,9 @@ static bool should_focus(struct sway_view *view) { | |||
603 | 619 | ||
604 | // If the view is the only one in the focused workspace, it'll get focus | 620 | // If the view is the only one in the focused workspace, it'll get focus |
605 | // regardless of any no_focus criteria. | 621 | // regardless of any no_focus criteria. |
606 | if (!view->container->parent && !prev_con) { | 622 | if (!view->container->pending.parent && !prev_con) { |
607 | size_t num_children = view->container->workspace->tiling->length + | 623 | size_t num_children = view->container->pending.workspace->tiling->length + |
608 | view->container->workspace->floating->length; | 624 | view->container->pending.workspace->floating->length; |
609 | if (num_children == 1) { | 625 | if (num_children == 1) { |
610 | return true; | 626 | return true; |
611 | } | 627 | } |
@@ -635,6 +651,7 @@ static void handle_foreign_activate_request( | |||
635 | break; | 651 | break; |
636 | } | 652 | } |
637 | } | 653 | } |
654 | transaction_commit_dirty(); | ||
638 | } | 655 | } |
639 | 656 | ||
640 | static void handle_foreign_fullscreen_request( | 657 | static void handle_foreign_fullscreen_request( |
@@ -645,9 +662,21 @@ static void handle_foreign_fullscreen_request( | |||
645 | 662 | ||
646 | // Match fullscreen command behavior for scratchpad hidden views | 663 | // Match fullscreen command behavior for scratchpad hidden views |
647 | struct sway_container *container = view->container; | 664 | struct sway_container *container = view->container; |
648 | if (!container->workspace) { | 665 | if (!container->pending.workspace) { |
649 | while (container->parent) { | 666 | while (container->pending.parent) { |
650 | container = container->parent; | 667 | container = container->pending.parent; |
668 | } | ||
669 | } | ||
670 | |||
671 | if (event->fullscreen && event->output && event->output->data) { | ||
672 | struct sway_output *output = event->output->data; | ||
673 | struct sway_workspace *ws = output_get_active_workspace(output); | ||
674 | if (ws && !container_is_scratchpad_hidden(view->container)) { | ||
675 | if (container_is_floating(view->container)) { | ||
676 | workspace_add_floating(ws, view->container); | ||
677 | } else { | ||
678 | workspace_add_tiling(ws, view->container); | ||
679 | } | ||
651 | } | 680 | } |
652 | } | 681 | } |
653 | 682 | ||
@@ -656,12 +685,13 @@ static void handle_foreign_fullscreen_request( | |||
656 | if (event->fullscreen) { | 685 | if (event->fullscreen) { |
657 | arrange_root(); | 686 | arrange_root(); |
658 | } else { | 687 | } else { |
659 | if (container->parent) { | 688 | if (container->pending.parent) { |
660 | arrange_container(container->parent); | 689 | arrange_container(container->pending.parent); |
661 | } else if (container->workspace) { | 690 | } else if (container->pending.workspace) { |
662 | arrange_workspace(container->workspace); | 691 | arrange_workspace(container->pending.workspace); |
663 | } | 692 | } |
664 | } | 693 | } |
694 | transaction_commit_dirty(); | ||
665 | } | 695 | } |
666 | 696 | ||
667 | static void handle_foreign_close_request( | 697 | static void handle_foreign_close_request( |
@@ -692,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
692 | view_populate_pid(view); | 722 | view_populate_pid(view); |
693 | view->container = container_create(view); | 723 | view->container = container_create(view); |
694 | 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 | |||
695 | // 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 |
696 | // to honor that request. Otherwise, fallback to assigns, pid mappings, | 733 | // to honor that request. Otherwise, fallback to assigns, pid mappings, |
697 | // focused workspace, etc | 734 | // focused workspace, etc |
@@ -705,10 +742,36 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
705 | } | 742 | } |
706 | 743 | ||
707 | struct sway_seat *seat = input_manager_current_seat(); | 744 | struct sway_seat *seat = input_manager_current_seat(); |
708 | struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) | 745 | struct sway_node *node = |
709 | : seat_get_focus_inactive(seat, &root->node); | 746 | seat_get_focus_inactive(seat, ws ? &ws->node : &root->node); |
710 | struct sway_container *target_sibling = node->type == N_CONTAINER ? | 747 | struct sway_container *target_sibling = NULL; |
711 | node->sway_container : NULL; | 748 | if (node && node->type == N_CONTAINER) { |
749 | if (container_is_floating(node->sway_container)) { | ||
750 | // If we're about to launch the view into the floating container, then | ||
751 | // launch it as a tiled view instead. | ||
752 | if (ws) { | ||
753 | target_sibling = seat_get_focus_inactive_tiling(seat, ws); | ||
754 | if (target_sibling) { | ||
755 | struct sway_container *con = | ||
756 | seat_get_focus_inactive_view(seat, &target_sibling->node); | ||
757 | if (con) { | ||
758 | target_sibling = con; | ||
759 | } | ||
760 | } | ||
761 | } else { | ||
762 | ws = seat_get_last_known_workspace(seat); | ||
763 | } | ||
764 | } else { | ||
765 | target_sibling = node->sway_container; | ||
766 | } | ||
767 | } | ||
768 | |||
769 | struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = { | ||
770 | .app_id = view_get_app_id(view), | ||
771 | .title = view_get_title(view), | ||
772 | }; | ||
773 | view->ext_foreign_toplevel = | ||
774 | wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state); | ||
712 | 775 | ||
713 | view->foreign_toplevel = | 776 | view->foreign_toplevel = |
714 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); | 777 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); |
@@ -725,13 +788,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
725 | wl_signal_add(&view->foreign_toplevel->events.destroy, | 788 | wl_signal_add(&view->foreign_toplevel->events.destroy, |
726 | &view->foreign_destroy); | 789 | &view->foreign_destroy); |
727 | 790 | ||
728 | // If we're about to launch the view into the floating container, then | ||
729 | // launch it as a tiled view in the root of the workspace instead. | ||
730 | if (target_sibling && container_is_floating(target_sibling)) { | ||
731 | target_sibling = NULL; | ||
732 | ws = seat_get_last_known_workspace(seat); | ||
733 | } | ||
734 | |||
735 | struct sway_container *container = view->container; | 791 | struct sway_container *container = view->container; |
736 | if (target_sibling) { | 792 | if (target_sibling) { |
737 | container_add_sibling(target_sibling, container, 1); | 793 | container_add_sibling(target_sibling, container, 1); |
@@ -740,30 +796,25 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
740 | } | 796 | } |
741 | ipc_event_window(view->container, "new"); | 797 | ipc_event_window(view->container, "new"); |
742 | 798 | ||
743 | view_init_subsurfaces(view, wlr_surface); | ||
744 | wl_signal_add(&wlr_surface->events.new_subsurface, | ||
745 | &view->surface_new_subsurface); | ||
746 | view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; | ||
747 | |||
748 | if (decoration) { | 799 | if (decoration) { |
749 | view_update_csd_from_client(view, decoration); | 800 | view_update_csd_from_client(view, decoration); |
750 | } | 801 | } |
751 | 802 | ||
752 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { | 803 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { |
753 | view->container->border = config->floating_border; | 804 | view->container->pending.border = config->floating_border; |
754 | view->container->border_thickness = config->floating_border_thickness; | 805 | view->container->pending.border_thickness = config->floating_border_thickness; |
755 | container_set_floating(view->container, true); | 806 | container_set_floating(view->container, true); |
756 | } else { | 807 | } else { |
757 | view->container->border = config->border; | 808 | view->container->pending.border = config->border; |
758 | view->container->border_thickness = config->border_thickness; | 809 | view->container->pending.border_thickness = config->border_thickness; |
759 | view_set_tiled(view, true); | 810 | view_set_tiled(view, true); |
760 | } | 811 | } |
761 | 812 | ||
762 | if (config->popup_during_fullscreen == POPUP_LEAVE && | 813 | if (config->popup_during_fullscreen == POPUP_LEAVE && |
763 | container->workspace && | 814 | container->pending.workspace && |
764 | container->workspace->fullscreen && | 815 | container->pending.workspace->fullscreen && |
765 | container->workspace->fullscreen->view) { | 816 | container->pending.workspace->fullscreen->view) { |
766 | struct sway_container *fs = container->workspace->fullscreen; | 817 | struct sway_container *fs = container->pending.workspace->fullscreen; |
767 | if (view_is_transient_for(view, fs->view)) { | 818 | if (view_is_transient_for(view, fs->view)) { |
768 | container_set_fullscreen(fs, false); | 819 | container_set_fullscreen(fs, false); |
769 | } | 820 | } |
@@ -774,12 +825,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
774 | 825 | ||
775 | if (fullscreen) { | 826 | if (fullscreen) { |
776 | container_set_fullscreen(view->container, true); | 827 | container_set_fullscreen(view->container, true); |
777 | arrange_workspace(view->container->workspace); | 828 | arrange_workspace(view->container->pending.workspace); |
778 | } else { | 829 | } else { |
779 | if (container->parent) { | 830 | if (container->pending.parent) { |
780 | arrange_container(container->parent); | 831 | arrange_container(container->pending.parent); |
781 | } else if (container->workspace) { | 832 | } else if (container->pending.workspace) { |
782 | arrange_workspace(container->workspace); | 833 | arrange_workspace(container->pending.workspace); |
783 | } | 834 | } |
784 | } | 835 | } |
785 | 836 | ||
@@ -788,11 +839,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
788 | bool set_focus = should_focus(view); | 839 | bool set_focus = should_focus(view); |
789 | 840 | ||
790 | #if HAVE_XWAYLAND | 841 | #if HAVE_XWAYLAND |
791 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 842 | struct wlr_xwayland_surface *xsurface; |
792 | struct wlr_xwayland_surface *xsurface = | 843 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
793 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | 844 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != |
794 | set_focus = (wlr_xwayland_icccm_input_model(xsurface) != | 845 | WLR_ICCCM_INPUT_MODEL_NONE; |
795 | WLR_ICCCM_INPUT_MODEL_NONE) && set_focus; | ||
796 | } | 846 | } |
797 | #endif | 847 | #endif |
798 | 848 | ||
@@ -800,34 +850,41 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
800 | input_manager_set_focus(&view->container->node); | 850 | input_manager_set_focus(&view->container->node); |
801 | } | 851 | } |
802 | 852 | ||
853 | if (view->ext_foreign_toplevel) { | ||
854 | update_ext_foreign_toplevel(view); | ||
855 | } | ||
856 | |||
803 | const char *app_id; | 857 | const char *app_id; |
804 | const char *class; | 858 | const char *class; |
805 | if ((app_id = view_get_app_id(view)) != NULL) { | 859 | if ((app_id = view_get_app_id(view)) != NULL) { |
806 | wlr_foreign_toplevel_handle_v1_set_app_id( | 860 | wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id); |
807 | view->foreign_toplevel, app_id); | ||
808 | } else if ((class = view_get_class(view)) != NULL) { | 861 | } else if ((class = view_get_class(view)) != NULL) { |
809 | wlr_foreign_toplevel_handle_v1_set_app_id( | 862 | wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, class); |
810 | view->foreign_toplevel, class); | ||
811 | } | 863 | } |
812 | } | 864 | } |
813 | 865 | ||
814 | void view_unmap(struct sway_view *view) { | 866 | void view_unmap(struct sway_view *view) { |
815 | wl_signal_emit(&view->events.unmap, view); | 867 | wl_signal_emit_mutable(&view->events.unmap, view); |
816 | 868 | ||
817 | wl_list_remove(&view->surface_new_subsurface.link); | 869 | view->executed_criteria->length = 0; |
818 | 870 | ||
819 | if (view->urgent_timer) { | 871 | if (view->urgent_timer) { |
820 | wl_event_source_remove(view->urgent_timer); | 872 | wl_event_source_remove(view->urgent_timer); |
821 | view->urgent_timer = NULL; | 873 | view->urgent_timer = NULL; |
822 | } | 874 | } |
823 | 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 | |||
824 | if (view->foreign_toplevel) { | 881 | if (view->foreign_toplevel) { |
825 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); | 882 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); |
826 | view->foreign_toplevel = NULL; | 883 | view->foreign_toplevel = NULL; |
827 | } | 884 | } |
828 | 885 | ||
829 | struct sway_container *parent = view->container->parent; | 886 | struct sway_container *parent = view->container->pending.parent; |
830 | struct sway_workspace *ws = view->container->workspace; | 887 | struct sway_workspace *ws = view->container->pending.workspace; |
831 | container_begin_destroy(view->container); | 888 | container_begin_destroy(view->container); |
832 | if (parent) { | 889 | if (parent) { |
833 | container_reap_empty(parent); | 890 | container_reap_empty(parent); |
@@ -860,262 +917,54 @@ void view_unmap(struct sway_view *view) { | |||
860 | view->surface = NULL; | 917 | view->surface = NULL; |
861 | } | 918 | } |
862 | 919 | ||
863 | void view_update_size(struct sway_view *view, int width, int height) { | 920 | void view_update_size(struct sway_view *view) { |
864 | struct sway_container *con = view->container; | 921 | struct sway_container *con = view->container; |
865 | 922 | con->pending.content_width = view->geometry.width; | |
866 | if (container_is_floating(con)) { | 923 | con->pending.content_height = view->geometry.height; |
867 | con->content_width = width; | 924 | container_set_geometry_from_content(con); |
868 | con->content_height = height; | ||
869 | container_set_geometry_from_content(con); | ||
870 | } else { | ||
871 | con->surface_x = con->content_x + (con->content_width - width) / 2; | ||
872 | con->surface_y = con->content_y + (con->content_height - height) / 2; | ||
873 | con->surface_x = fmax(con->surface_x, con->content_x); | ||
874 | con->surface_y = fmax(con->surface_y, con->content_y); | ||
875 | } | ||
876 | } | 925 | } |
877 | 926 | ||
878 | static const struct sway_view_child_impl subsurface_impl; | 927 | void view_center_and_clip_surface(struct sway_view *view) { |
928 | struct sway_container *con = view->container; | ||
879 | 929 | ||
880 | static void subsurface_get_root_coords(struct sway_view_child *child, | 930 | if (container_is_floating(con)) { |
881 | int *root_sx, int *root_sy) { | 931 | // We always center the current coordinates rather than the next, as the |
882 | struct wlr_surface *surface = child->surface; | 932 | // geometry immediately affects the currently active rendering. |
883 | *root_sx = -child->view->geometry.x; | 933 | int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); |
884 | *root_sy = -child->view->geometry.y; | 934 | int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); |
885 | 935 | ||
886 | if (child->parent && child->parent->impl && | 936 | wlr_scene_node_set_position(&view->content_tree->node, x, y); |
887 | child->parent->impl->get_root_coords) { | ||
888 | int sx, sy; | ||
889 | child->parent->impl->get_root_coords(child->parent, &sx, &sy); | ||
890 | *root_sx += sx; | ||
891 | *root_sy += sy; | ||
892 | } else { | 937 | } else { |
893 | while (surface && wlr_surface_is_subsurface(surface)) { | 938 | wlr_scene_node_set_position(&view->content_tree->node, 0, 0); |
894 | struct wlr_subsurface *subsurface = | ||
895 | wlr_subsurface_from_wlr_surface(surface); | ||
896 | if (subsurface == NULL) { | ||
897 | break; | ||
898 | } | ||
899 | *root_sx += subsurface->current.x; | ||
900 | *root_sy += subsurface->current.y; | ||
901 | surface = subsurface->parent; | ||
902 | } | ||
903 | } | 939 | } |
904 | } | ||
905 | 940 | ||
906 | static void subsurface_destroy(struct sway_view_child *child) { | 941 | // only make sure to clip the content if there is content to clip |
907 | if (!sway_assert(child->impl == &subsurface_impl, | 942 | if (!wl_list_empty(&con->view->content_tree->children)) { |
908 | "Expected a subsurface")) { | 943 | wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){ |
909 | return; | 944 | .x = con->view->geometry.x, |
910 | } | 945 | .y = con->view->geometry.y, |
911 | struct sway_subsurface *subsurface = (struct sway_subsurface *)child; | 946 | .width = con->current.content_width, |
912 | wl_list_remove(&subsurface->destroy.link); | 947 | .height = con->current.content_height, |
913 | free(subsurface); | 948 | }); |
914 | } | ||
915 | |||
916 | static const struct sway_view_child_impl subsurface_impl = { | ||
917 | .get_root_coords = subsurface_get_root_coords, | ||
918 | .destroy = subsurface_destroy, | ||
919 | }; | ||
920 | |||
921 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
922 | void *data) { | ||
923 | struct sway_subsurface *subsurface = | ||
924 | wl_container_of(listener, subsurface, destroy); | ||
925 | struct sway_view_child *child = &subsurface->child; | ||
926 | view_child_destroy(child); | ||
927 | } | ||
928 | |||
929 | static void view_child_damage(struct sway_view_child *child, bool whole); | ||
930 | |||
931 | static void view_subsurface_create(struct sway_view *view, | ||
932 | struct wlr_subsurface *wlr_subsurface) { | ||
933 | struct sway_subsurface *subsurface = | ||
934 | calloc(1, sizeof(struct sway_subsurface)); | ||
935 | if (subsurface == NULL) { | ||
936 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
937 | return; | ||
938 | } | ||
939 | view_child_init(&subsurface->child, &subsurface_impl, view, | ||
940 | wlr_subsurface->surface); | ||
941 | |||
942 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
943 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
944 | |||
945 | subsurface->child.mapped = true; | ||
946 | |||
947 | view_child_damage(&subsurface->child, true); | ||
948 | } | ||
949 | |||
950 | static void view_child_subsurface_create(struct sway_view_child *child, | ||
951 | struct wlr_subsurface *wlr_subsurface) { | ||
952 | struct sway_subsurface *subsurface = | ||
953 | calloc(1, sizeof(struct sway_subsurface)); | ||
954 | if (subsurface == NULL) { | ||
955 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
956 | return; | ||
957 | } | ||
958 | subsurface->child.parent = child; | ||
959 | wl_list_insert(&child->children, &subsurface->child.link); | ||
960 | view_child_init(&subsurface->child, &subsurface_impl, child->view, | ||
961 | wlr_subsurface->surface); | ||
962 | |||
963 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
964 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
965 | |||
966 | subsurface->child.mapped = true; | ||
967 | |||
968 | view_child_damage(&subsurface->child, true); | ||
969 | } | ||
970 | |||
971 | static void view_child_damage(struct sway_view_child *child, bool whole) { | ||
972 | if (!child || !child->mapped || !child->view || !child->view->container) { | ||
973 | return; | ||
974 | } | ||
975 | int sx, sy; | ||
976 | child->impl->get_root_coords(child, &sx, &sy); | ||
977 | desktop_damage_surface(child->surface, | ||
978 | child->view->container->content_x + sx, | ||
979 | child->view->container->content_y + sy, whole); | ||
980 | } | ||
981 | |||
982 | static void view_child_handle_surface_commit(struct wl_listener *listener, | ||
983 | void *data) { | ||
984 | struct sway_view_child *child = | ||
985 | wl_container_of(listener, child, surface_commit); | ||
986 | view_child_damage(child, false); | ||
987 | } | ||
988 | |||
989 | static void view_child_handle_surface_new_subsurface( | ||
990 | struct wl_listener *listener, void *data) { | ||
991 | struct sway_view_child *child = | ||
992 | wl_container_of(listener, child, surface_new_subsurface); | ||
993 | struct wlr_subsurface *subsurface = data; | ||
994 | view_child_subsurface_create(child, subsurface); | ||
995 | } | ||
996 | |||
997 | static void view_child_handle_surface_destroy(struct wl_listener *listener, | ||
998 | void *data) { | ||
999 | struct sway_view_child *child = | ||
1000 | wl_container_of(listener, child, surface_destroy); | ||
1001 | view_child_destroy(child); | ||
1002 | } | ||
1003 | |||
1004 | static void view_init_subsurfaces(struct sway_view *view, | ||
1005 | struct wlr_surface *surface) { | ||
1006 | struct wlr_subsurface *subsurface; | ||
1007 | wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) { | ||
1008 | view_subsurface_create(view, subsurface); | ||
1009 | } | ||
1010 | } | ||
1011 | |||
1012 | static void view_child_handle_surface_map(struct wl_listener *listener, | ||
1013 | void *data) { | ||
1014 | struct sway_view_child *child = | ||
1015 | wl_container_of(listener, child, surface_map); | ||
1016 | child->mapped = true; | ||
1017 | view_child_damage(child, true); | ||
1018 | } | ||
1019 | |||
1020 | static void view_child_handle_surface_unmap(struct wl_listener *listener, | ||
1021 | void *data) { | ||
1022 | struct sway_view_child *child = | ||
1023 | wl_container_of(listener, child, surface_unmap); | ||
1024 | view_child_damage(child, true); | ||
1025 | child->mapped = false; | ||
1026 | } | ||
1027 | |||
1028 | static void view_child_handle_view_unmap(struct wl_listener *listener, | ||
1029 | void *data) { | ||
1030 | struct sway_view_child *child = | ||
1031 | wl_container_of(listener, child, view_unmap); | ||
1032 | view_child_damage(child, true); | ||
1033 | child->mapped = false; | ||
1034 | } | ||
1035 | |||
1036 | void view_child_init(struct sway_view_child *child, | ||
1037 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
1038 | struct wlr_surface *surface) { | ||
1039 | child->impl = impl; | ||
1040 | child->view = view; | ||
1041 | child->surface = surface; | ||
1042 | wl_list_init(&child->children); | ||
1043 | |||
1044 | wl_signal_add(&surface->events.commit, &child->surface_commit); | ||
1045 | child->surface_commit.notify = view_child_handle_surface_commit; | ||
1046 | wl_signal_add(&surface->events.new_subsurface, | ||
1047 | &child->surface_new_subsurface); | ||
1048 | child->surface_new_subsurface.notify = | ||
1049 | view_child_handle_surface_new_subsurface; | ||
1050 | wl_signal_add(&surface->events.destroy, &child->surface_destroy); | ||
1051 | child->surface_destroy.notify = view_child_handle_surface_destroy; | ||
1052 | |||
1053 | // Not all child views have a map/unmap event | ||
1054 | child->surface_map.notify = view_child_handle_surface_map; | ||
1055 | wl_list_init(&child->surface_map.link); | ||
1056 | child->surface_unmap.notify = view_child_handle_surface_unmap; | ||
1057 | wl_list_init(&child->surface_unmap.link); | ||
1058 | |||
1059 | wl_signal_add(&view->events.unmap, &child->view_unmap); | ||
1060 | child->view_unmap.notify = view_child_handle_view_unmap; | ||
1061 | |||
1062 | struct sway_workspace *workspace = child->view->container->workspace; | ||
1063 | if (workspace) { | ||
1064 | wlr_surface_send_enter(child->surface, workspace->output->wlr_output); | ||
1065 | } | ||
1066 | |||
1067 | view_init_subsurfaces(child->view, surface); | ||
1068 | } | ||
1069 | |||
1070 | void view_child_destroy(struct sway_view_child *child) { | ||
1071 | if (child->mapped && child->view->container != NULL) { | ||
1072 | view_child_damage(child, true); | ||
1073 | } | ||
1074 | |||
1075 | if (child->parent != NULL) { | ||
1076 | wl_list_remove(&child->link); | ||
1077 | child->parent = NULL; | ||
1078 | } | ||
1079 | |||
1080 | struct sway_view_child *subchild, *tmpchild; | ||
1081 | wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { | ||
1082 | wl_list_remove(&subchild->link); | ||
1083 | subchild->parent = NULL; | ||
1084 | } | ||
1085 | |||
1086 | wl_list_remove(&child->surface_commit.link); | ||
1087 | wl_list_remove(&child->surface_destroy.link); | ||
1088 | wl_list_remove(&child->surface_map.link); | ||
1089 | wl_list_remove(&child->surface_unmap.link); | ||
1090 | wl_list_remove(&child->view_unmap.link); | ||
1091 | wl_list_remove(&child->surface_new_subsurface.link); | ||
1092 | |||
1093 | if (child->impl && child->impl->destroy) { | ||
1094 | child->impl->destroy(child); | ||
1095 | } else { | ||
1096 | free(child); | ||
1097 | } | 949 | } |
1098 | } | 950 | } |
1099 | 951 | ||
1100 | 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) { |
1101 | if (wlr_surface_is_xdg_surface(wlr_surface)) { | 953 | struct wlr_xdg_surface *xdg_surface; |
1102 | struct wlr_xdg_surface *xdg_surface = | 954 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { |
1103 | wlr_xdg_surface_from_wlr_surface(wlr_surface); | ||
1104 | return view_from_wlr_xdg_surface(xdg_surface); | 955 | return view_from_wlr_xdg_surface(xdg_surface); |
1105 | } | 956 | } |
1106 | #if HAVE_XWAYLAND | 957 | #if HAVE_XWAYLAND |
1107 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 958 | struct wlr_xwayland_surface *xsurface; |
1108 | struct wlr_xwayland_surface *xsurface = | 959 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
1109 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
1110 | return view_from_wlr_xwayland_surface(xsurface); | 960 | return view_from_wlr_xwayland_surface(xsurface); |
1111 | } | 961 | } |
1112 | #endif | 962 | #endif |
1113 | if (wlr_surface_is_subsurface(wlr_surface)) { | 963 | struct wlr_subsurface *subsurface; |
1114 | struct wlr_subsurface *subsurface = | 964 | if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) { |
1115 | wlr_subsurface_from_wlr_surface(wlr_surface); | ||
1116 | return view_from_wlr_surface(subsurface->parent); | 965 | return view_from_wlr_surface(subsurface->parent); |
1117 | } | 966 | } |
1118 | if (wlr_surface_is_layer_surface(wlr_surface)) { | 967 | if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { |
1119 | return NULL; | 968 | return NULL; |
1120 | } | 969 | } |
1121 | 970 | ||
@@ -1196,6 +1045,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { | |||
1196 | return len; | 1045 | return len; |
1197 | } | 1046 | } |
1198 | 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 | |||
1199 | void view_update_title(struct sway_view *view, bool force) { | 1060 | void view_update_title(struct sway_view *view, bool force) { |
1200 | const char *title = view_get_title(view); | 1061 | const char *title = view_get_title(view); |
1201 | 1062 | ||
@@ -1211,46 +1072,56 @@ void view_update_title(struct sway_view *view, bool force) { | |||
1211 | 1072 | ||
1212 | free(view->container->title); | 1073 | free(view->container->title); |
1213 | free(view->container->formatted_title); | 1074 | free(view->container->formatted_title); |
1214 | if (title) { | 1075 | |
1215 | size_t len = parse_title_format(view, NULL); | 1076 | size_t len = parse_title_format(view, NULL); |
1077 | |||
1078 | if (len) { | ||
1216 | char *buffer = calloc(len + 1, sizeof(char)); | 1079 | char *buffer = calloc(len + 1, sizeof(char)); |
1217 | if (!sway_assert(buffer, "Unable to allocate title string")) { | 1080 | if (!sway_assert(buffer, "Unable to allocate title string")) { |
1218 | return; | 1081 | return; |
1219 | } | 1082 | } |
1220 | parse_title_format(view, buffer); | ||
1221 | 1083 | ||
1222 | view->container->title = strdup(title); | 1084 | parse_title_format(view, buffer); |
1223 | view->container->formatted_title = buffer; | 1085 | view->container->formatted_title = buffer; |
1224 | } else { | 1086 | } else { |
1225 | view->container->title = NULL; | ||
1226 | view->container->formatted_title = NULL; | 1087 | view->container->formatted_title = NULL; |
1227 | } | 1088 | } |
1228 | container_calculate_title_height(view->container); | 1089 | |
1229 | config_update_font_height(false); | 1090 | view->container->title = title ? strdup(title) : NULL; |
1230 | 1091 | ||
1231 | // Update title after the global font height is updated | 1092 | // Update title after the global font height is updated |
1232 | 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 | } | ||
1233 | 1100 | ||
1234 | ipc_event_window(view->container, "title"); | 1101 | ipc_event_window(view->container, "title"); |
1235 | 1102 | ||
1236 | if (view->foreign_toplevel && title) { | 1103 | if (view->foreign_toplevel && title) { |
1237 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); | 1104 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); |
1238 | } | 1105 | } |
1106 | |||
1107 | if (view->ext_foreign_toplevel) { | ||
1108 | update_ext_foreign_toplevel(view); | ||
1109 | } | ||
1239 | } | 1110 | } |
1240 | 1111 | ||
1241 | bool view_is_visible(struct sway_view *view) { | 1112 | bool view_is_visible(struct sway_view *view) { |
1242 | if (view->container->node.destroying) { | 1113 | if (view->container->node.destroying) { |
1243 | return false; | 1114 | return false; |
1244 | } | 1115 | } |
1245 | struct sway_workspace *workspace = view->container->workspace; | 1116 | struct sway_workspace *workspace = view->container->pending.workspace; |
1246 | if (!workspace && view->container->fullscreen_mode != FULLSCREEN_GLOBAL) { | 1117 | if (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { |
1247 | bool fs_global_descendant = false; | 1118 | bool fs_global_descendant = false; |
1248 | struct sway_container *parent = view->container->parent; | 1119 | struct sway_container *parent = view->container->pending.parent; |
1249 | while (parent) { | 1120 | while (parent) { |
1250 | if (parent->fullscreen_mode == FULLSCREEN_GLOBAL) { | 1121 | if (parent->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
1251 | fs_global_descendant = true; | 1122 | fs_global_descendant = true; |
1252 | } | 1123 | } |
1253 | parent = parent->parent; | 1124 | parent = parent->pending.parent; |
1254 | } | 1125 | } |
1255 | if (!fs_global_descendant) { | 1126 | if (!fs_global_descendant) { |
1256 | return false; | 1127 | return false; |
@@ -1268,13 +1139,13 @@ bool view_is_visible(struct sway_view *view) { | |||
1268 | enum sway_container_layout layout = container_parent_layout(con); | 1139 | enum sway_container_layout layout = container_parent_layout(con); |
1269 | if ((layout == L_TABBED || layout == L_STACKED) | 1140 | if ((layout == L_TABBED || layout == L_STACKED) |
1270 | && !container_is_floating(con)) { | 1141 | && !container_is_floating(con)) { |
1271 | struct sway_node *parent = con->parent ? | 1142 | struct sway_node *parent = con->pending.parent ? |
1272 | &con->parent->node : &con->workspace->node; | 1143 | &con->pending.parent->node : &con->pending.workspace->node; |
1273 | if (seat_get_active_tiling_child(seat, parent) != &con->node) { | 1144 | if (seat_get_active_tiling_child(seat, parent) != &con->node) { |
1274 | return false; | 1145 | return false; |
1275 | } | 1146 | } |
1276 | } | 1147 | } |
1277 | con = con->parent; | 1148 | con = con->pending.parent; |
1278 | } | 1149 | } |
1279 | // Check view isn't hidden by another fullscreen view | 1150 | // Check view isn't hidden by another fullscreen view |
1280 | struct sway_container *fs = root->fullscreen_global ? | 1151 | struct sway_container *fs = root->fullscreen_global ? |
@@ -1296,6 +1167,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1296 | return; | 1167 | return; |
1297 | } | 1168 | } |
1298 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | 1169 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); |
1170 | container_update_itself_and_parents(view->container); | ||
1299 | } else { | 1171 | } else { |
1300 | view->urgent = (struct timespec){ 0 }; | 1172 | view->urgent = (struct timespec){ 0 }; |
1301 | if (view->urgent_timer) { | 1173 | if (view->urgent_timer) { |
@@ -1303,12 +1175,11 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1303 | view->urgent_timer = NULL; | 1175 | view->urgent_timer = NULL; |
1304 | } | 1176 | } |
1305 | } | 1177 | } |
1306 | container_damage_whole(view->container); | ||
1307 | 1178 | ||
1308 | ipc_event_window(view->container, "urgent"); | 1179 | ipc_event_window(view->container, "urgent"); |
1309 | 1180 | ||
1310 | if (!container_is_scratchpad_hidden(view->container)) { | 1181 | if (!container_is_scratchpad_hidden(view->container)) { |
1311 | workspace_detect_urgent(view->container->workspace); | 1182 | workspace_detect_urgent(view->container->pending.workspace); |
1312 | } | 1183 | } |
1313 | } | 1184 | } |
1314 | 1185 | ||
@@ -1317,40 +1188,54 @@ bool view_is_urgent(struct sway_view *view) { | |||
1317 | } | 1188 | } |
1318 | 1189 | ||
1319 | void view_remove_saved_buffer(struct sway_view *view) { | 1190 | void view_remove_saved_buffer(struct sway_view *view) { |
1320 | 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")) { |
1321 | return; | 1192 | return; |
1322 | } | 1193 | } |
1323 | struct sway_saved_buffer *saved_buf, *tmp; | 1194 | |
1324 | wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { | 1195 | wlr_scene_node_destroy(&view->saved_surface_tree->node); |
1325 | wlr_buffer_unlock(&saved_buf->buffer->base); | 1196 | view->saved_surface_tree = NULL; |
1326 | wl_list_remove(&saved_buf->link); | 1197 | wlr_scene_node_set_enabled(&view->content_tree->node, true); |
1327 | free(saved_buf); | ||
1328 | } | ||
1329 | } | 1198 | } |
1330 | 1199 | ||
1331 | static void view_save_buffer_iterator(struct wlr_surface *surface, | 1200 | static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, |
1332 | int sx, int sy, void *data) { | 1201 | int sx, int sy, void *data) { |
1333 | struct sway_view *view = data; | 1202 | struct wlr_scene_tree *tree = data; |
1334 | 1203 | ||
1335 | if (surface && wlr_surface_has_buffer(surface)) { | 1204 | struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); |
1336 | wlr_buffer_lock(&surface->buffer->base); | 1205 | if (!sbuf) { |
1337 | 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"); |
1338 | saved_buffer->buffer = surface->buffer; | 1207 | return; |
1339 | saved_buffer->width = surface->current.width; | ||
1340 | saved_buffer->height = surface->current.height; | ||
1341 | saved_buffer->x = sx; | ||
1342 | saved_buffer->y = sy; | ||
1343 | saved_buffer->transform = surface->current.transform; | ||
1344 | wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); | ||
1345 | wl_list_insert(&view->saved_buffers, &saved_buffer->link); | ||
1346 | } | 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); | ||
1347 | } | 1217 | } |
1348 | 1218 | ||
1349 | void view_save_buffer(struct sway_view *view) { | 1219 | void view_save_buffer(struct sway_view *view) { |
1350 | 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")) { |
1351 | view_remove_saved_buffer(view); | 1221 | view_remove_saved_buffer(view); |
1352 | } | 1222 | } |
1353 | 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); | ||
1354 | } | 1239 | } |
1355 | 1240 | ||
1356 | bool view_is_transient_for(struct sway_view *child, | 1241 | bool view_is_transient_for(struct sway_view *child, |
@@ -1358,3 +1243,19 @@ bool view_is_transient_for(struct sway_view *child, | |||
1358 | return child->impl->is_transient_for && | 1243 | return child->impl->is_transient_for && |
1359 | child->impl->is_transient_for(child, ancestor); | 1244 | child->impl->is_transient_for(child, ancestor); |
1360 | } | 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 921b7d19..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> |
@@ -48,14 +47,16 @@ struct sway_output *workspace_get_initial_output(const char *name) { | |||
48 | if (focus && focus->type == N_WORKSPACE) { | 47 | if (focus && focus->type == N_WORKSPACE) { |
49 | return focus->sway_workspace->output; | 48 | return focus->sway_workspace->output; |
50 | } else if (focus && focus->type == N_CONTAINER) { | 49 | } else if (focus && focus->type == N_CONTAINER) { |
51 | return focus->sway_container->workspace->output; | 50 | return focus->sway_container->pending.workspace->output; |
52 | } | 51 | } |
53 | // Fallback to the first output or noop output for headless | 52 | // Fallback to the first output or the headless output |
54 | return root->outputs->length ? root->outputs->items[0] : root->noop_output; | 53 | return root->outputs->length ? root->outputs->items[0] : root->fallback_output; |
55 | } | 54 | } |
56 | 55 | ||
57 | struct sway_workspace *workspace_create(struct sway_output *output, | 56 | struct sway_workspace *workspace_create(struct sway_output *output, |
58 | const char *name) { | 57 | const char *name) { |
58 | sway_assert(name, "NULL name given to workspace_create"); | ||
59 | |||
59 | if (output == NULL) { | 60 | if (output == NULL) { |
60 | output = workspace_get_initial_output(name); | 61 | output = workspace_get_initial_output(name); |
61 | } | 62 | } |
@@ -69,7 +70,19 @@ struct sway_workspace *workspace_create(struct sway_output *output, | |||
69 | return NULL; | 70 | return NULL; |
70 | } | 71 | } |
71 | node_init(&ws->node, N_WORKSPACE, ws); | 72 | node_init(&ws->node, N_WORKSPACE, ws); |
72 | ws->name = name ? strdup(name) : NULL; | 73 | |
74 | bool failed = false; | ||
75 | ws->layers.tiling = alloc_scene_tree(root->staging, &failed); | ||
76 | ws->layers.fullscreen = alloc_scene_tree(root->staging, &failed); | ||
77 | |||
78 | if (failed) { | ||
79 | wlr_scene_node_destroy(&ws->layers.tiling->node); | ||
80 | wlr_scene_node_destroy(&ws->layers.fullscreen->node); | ||
81 | free(ws); | ||
82 | return NULL; | ||
83 | } | ||
84 | |||
85 | ws->name = strdup(name); | ||
73 | ws->prev_split_layout = L_NONE; | 86 | ws->prev_split_layout = L_NONE; |
74 | ws->layout = output_get_default_layout(output); | 87 | ws->layout = output_get_default_layout(output); |
75 | ws->floating = create_list(); | 88 | ws->floating = create_list(); |
@@ -114,7 +127,7 @@ struct sway_workspace *workspace_create(struct sway_output *output, | |||
114 | output_sort_workspaces(output); | 127 | output_sort_workspaces(output); |
115 | 128 | ||
116 | ipc_event_workspace(NULL, ws, "init"); | 129 | ipc_event_workspace(NULL, ws, "init"); |
117 | wl_signal_emit(&root->events.new_node, &ws->node); | 130 | wl_signal_emit_mutable(&root->events.new_node, &ws->node); |
118 | 131 | ||
119 | return ws; | 132 | return ws; |
120 | } | 133 | } |
@@ -129,6 +142,11 @@ void workspace_destroy(struct sway_workspace *workspace) { | |||
129 | return; | 142 | return; |
130 | } | 143 | } |
131 | 144 | ||
145 | scene_node_disown_children(workspace->layers.tiling); | ||
146 | scene_node_disown_children(workspace->layers.fullscreen); | ||
147 | wlr_scene_node_destroy(&workspace->layers.tiling->node); | ||
148 | wlr_scene_node_destroy(&workspace->layers.fullscreen->node); | ||
149 | |||
132 | free(workspace->name); | 150 | free(workspace->name); |
133 | free(workspace->representation); | 151 | free(workspace->representation); |
134 | list_free_items_and_destroy(workspace->output_priority); | 152 | list_free_items_and_destroy(workspace->output_priority); |
@@ -142,7 +160,7 @@ void workspace_destroy(struct sway_workspace *workspace) { | |||
142 | void workspace_begin_destroy(struct sway_workspace *workspace) { | 160 | void workspace_begin_destroy(struct sway_workspace *workspace) { |
143 | sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); | 161 | sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); |
144 | ipc_event_workspace(NULL, workspace, "empty"); // intentional | 162 | ipc_event_workspace(NULL, workspace, "empty"); // intentional |
145 | wl_signal_emit(&workspace->node.events.destroy, &workspace->node); | 163 | wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); |
146 | 164 | ||
147 | if (workspace->output) { | 165 | if (workspace->output) { |
148 | workspace_detach(workspace); | 166 | workspace_detach(workspace); |
@@ -174,22 +192,16 @@ void workspace_consider_destroy(struct sway_workspace *ws) { | |||
174 | static bool workspace_valid_on_output(const char *output_name, | 192 | static bool workspace_valid_on_output(const char *output_name, |
175 | const char *ws_name) { | 193 | const char *ws_name) { |
176 | struct workspace_config *wsc = workspace_find_config(ws_name); | 194 | struct workspace_config *wsc = workspace_find_config(ws_name); |
177 | char identifier[128]; | ||
178 | struct sway_output *output = output_by_name_or_id(output_name); | 195 | struct sway_output *output = output_by_name_or_id(output_name); |
179 | if (!output) { | 196 | if (!output) { |
180 | return false; | 197 | return false; |
181 | } | 198 | } |
182 | output_name = output->wlr_output->name; | ||
183 | output_get_identifier(identifier, sizeof(identifier), output); | ||
184 | |||
185 | if (!wsc) { | 199 | if (!wsc) { |
186 | return true; | 200 | return true; |
187 | } | 201 | } |
188 | 202 | ||
189 | for (int i = 0; i < wsc->outputs->length; i++) { | 203 | for (int i = 0; i < wsc->outputs->length; i++) { |
190 | if (strcmp(wsc->outputs->items[i], "*") == 0 || | 204 | if (output_match_name_or_id(output, wsc->outputs->items[i])) { |
191 | strcmp(wsc->outputs->items[i], output_name) == 0 || | ||
192 | strcmp(wsc->outputs->items[i], identifier) == 0) { | ||
193 | return true; | 205 | return true; |
194 | } | 206 | } |
195 | } | 207 | } |
@@ -222,10 +234,8 @@ static void workspace_name_from_binding(const struct sway_binding * binding, | |||
222 | // not a command about workspaces | 234 | // not a command about workspaces |
223 | if (strcmp(_target, "next") == 0 || | 235 | if (strcmp(_target, "next") == 0 || |
224 | strcmp(_target, "prev") == 0 || | 236 | strcmp(_target, "prev") == 0 || |
225 | strncmp(_target, "next_on_output", | 237 | strcmp(_target, "next_on_output") == 0 || |
226 | strlen("next_on_output")) == 0 || | 238 | strcmp(_target, "prev_on_output") == 0 || |
227 | strncmp(_target, "prev_on_output", | ||
228 | strlen("next_on_output")) == 0 || | ||
229 | strcmp(_target, "number") == 0 || | 239 | strcmp(_target, "number") == 0 || |
230 | strcmp(_target, "back_and_forth") == 0 || | 240 | strcmp(_target, "back_and_forth") == 0 || |
231 | strcmp(_target, "current") == 0) { | 241 | strcmp(_target, "current") == 0) { |
@@ -286,13 +296,10 @@ char *workspace_next_name(const char *output_name) { | |||
286 | // assignments primarily, falling back to bindings and numbers. | 296 | // assignments primarily, falling back to bindings and numbers. |
287 | struct sway_mode *mode = config->current_mode; | 297 | struct sway_mode *mode = config->current_mode; |
288 | 298 | ||
289 | char identifier[128]; | ||
290 | struct sway_output *output = output_by_name_or_id(output_name); | 299 | struct sway_output *output = output_by_name_or_id(output_name); |
291 | if (!output) { | 300 | if (!output) { |
292 | return NULL; | 301 | return NULL; |
293 | } | 302 | } |
294 | output_name = output->wlr_output->name; | ||
295 | output_get_identifier(identifier, sizeof(identifier), output); | ||
296 | 303 | ||
297 | int order = INT_MAX; | 304 | int order = INT_MAX; |
298 | char *target = NULL; | 305 | char *target = NULL; |
@@ -312,9 +319,7 @@ char *workspace_next_name(const char *output_name) { | |||
312 | } | 319 | } |
313 | bool found = false; | 320 | bool found = false; |
314 | for (int j = 0; j < wsc->outputs->length; ++j) { | 321 | for (int j = 0; j < wsc->outputs->length; ++j) { |
315 | if (strcmp(wsc->outputs->items[j], "*") == 0 || | 322 | if (output_match_name_or_id(output, wsc->outputs->items[j])) { |
316 | strcmp(wsc->outputs->items[j], output_name) == 0 || | ||
317 | strcmp(wsc->outputs->items[j], identifier) == 0) { | ||
318 | found = true; | 323 | found = true; |
319 | free(target); | 324 | free(target); |
320 | target = strdup(wsc->workspace); | 325 | target = strdup(wsc->workspace); |
@@ -363,11 +368,11 @@ struct sway_workspace *workspace_by_name(const char *name) { | |||
363 | if (current && strcmp(name, "prev") == 0) { | 368 | if (current && strcmp(name, "prev") == 0) { |
364 | return workspace_prev(current); | 369 | return workspace_prev(current); |
365 | } else if (current && strcmp(name, "prev_on_output") == 0) { | 370 | } else if (current && strcmp(name, "prev_on_output") == 0) { |
366 | return workspace_output_prev(current, false); | 371 | return workspace_output_prev(current); |
367 | } else if (current && strcmp(name, "next") == 0) { | 372 | } else if (current && strcmp(name, "next") == 0) { |
368 | return workspace_next(current); | 373 | return workspace_next(current); |
369 | } else if (current && strcmp(name, "next_on_output") == 0) { | 374 | } else if (current && strcmp(name, "next_on_output") == 0) { |
370 | return workspace_output_next(current, false); | 375 | return workspace_output_next(current); |
371 | } else if (strcmp(name, "current") == 0) { | 376 | } else if (strcmp(name, "current") == 0) { |
372 | return current; | 377 | return current; |
373 | } else if (strcasecmp(name, "back_and_forth") == 0) { | 378 | } else if (strcasecmp(name, "back_and_forth") == 0) { |
@@ -530,7 +535,7 @@ struct sway_workspace *workspace_next(struct sway_workspace *workspace) { | |||
530 | * otherwise the next one is returned. | 535 | * otherwise the next one is returned. |
531 | */ | 536 | */ |
532 | static struct sway_workspace *workspace_output_prev_next_impl( | 537 | static struct sway_workspace *workspace_output_prev_next_impl( |
533 | struct sway_output *output, int dir, bool create) { | 538 | struct sway_output *output, int dir) { |
534 | struct sway_seat *seat = input_manager_current_seat(); | 539 | struct sway_seat *seat = input_manager_current_seat(); |
535 | struct sway_workspace *workspace = seat_get_focused_workspace(seat); | 540 | struct sway_workspace *workspace = seat_get_focused_workspace(seat); |
536 | if (!workspace) { | 541 | if (!workspace) { |
@@ -540,46 +545,43 @@ static struct sway_workspace *workspace_output_prev_next_impl( | |||
540 | } | 545 | } |
541 | 546 | ||
542 | int index = list_find(output->workspaces, workspace); | 547 | int index = list_find(output->workspaces, workspace); |
543 | if (!workspace_is_empty(workspace) && create && | ||
544 | (index + dir < 0 || index + dir == output->workspaces->length)) { | ||
545 | struct sway_output *output = workspace->output; | ||
546 | char *next = workspace_next_name(output->wlr_output->name); | ||
547 | workspace_create(output, next); | ||
548 | free(next); | ||
549 | } | ||
550 | size_t new_index = wrap(index + dir, output->workspaces->length); | 548 | size_t new_index = wrap(index + dir, output->workspaces->length); |
551 | return output->workspaces->items[new_index]; | 549 | return output->workspaces->items[new_index]; |
552 | } | 550 | } |
553 | 551 | ||
554 | struct sway_workspace *workspace_output_next( | 552 | |
555 | struct sway_workspace *current, bool create) { | 553 | struct sway_workspace *workspace_output_next(struct sway_workspace *current) { |
556 | return workspace_output_prev_next_impl(current->output, 1, create); | 554 | return workspace_output_prev_next_impl(current->output, 1); |
557 | } | 555 | } |
558 | 556 | ||
559 | struct sway_workspace *workspace_output_prev( | 557 | struct sway_workspace *workspace_output_prev(struct sway_workspace *current) { |
560 | struct sway_workspace *current, bool create) { | 558 | return workspace_output_prev_next_impl(current->output, -1); |
561 | return workspace_output_prev_next_impl(current->output, -1, create); | ||
562 | } | 559 | } |
563 | 560 | ||
564 | bool workspace_switch(struct sway_workspace *workspace, | 561 | struct sway_workspace *workspace_auto_back_and_forth( |
565 | bool no_auto_back_and_forth) { | 562 | struct sway_workspace *workspace) { |
566 | struct sway_seat *seat = input_manager_current_seat(); | 563 | struct sway_seat *seat = input_manager_current_seat(); |
567 | struct sway_workspace *active_ws = NULL; | 564 | struct sway_workspace *active_ws = NULL; |
568 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); | 565 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); |
569 | if (focus && focus->type == N_WORKSPACE) { | 566 | if (focus && focus->type == N_WORKSPACE) { |
570 | active_ws = focus->sway_workspace; | 567 | active_ws = focus->sway_workspace; |
571 | } else if (focus && focus->type == N_CONTAINER) { | 568 | } else if (focus && focus->type == N_CONTAINER) { |
572 | active_ws = focus->sway_container->workspace; | 569 | active_ws = focus->sway_container->pending.workspace; |
573 | } | 570 | } |
574 | 571 | ||
575 | if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws | 572 | if (config->auto_back_and_forth && active_ws && active_ws == workspace && |
576 | && active_ws == workspace && seat->prev_workspace_name) { | 573 | seat->prev_workspace_name) { |
577 | struct sway_workspace *new_ws = | 574 | struct sway_workspace *new_ws = |
578 | workspace_by_name(seat->prev_workspace_name); | 575 | workspace_by_name(seat->prev_workspace_name); |
579 | workspace = new_ws ? | 576 | workspace = new_ws ? |
580 | new_ws : | 577 | new_ws : |
581 | workspace_create(NULL, seat->prev_workspace_name); | 578 | workspace_create(NULL, seat->prev_workspace_name); |
582 | } | 579 | } |
580 | return workspace; | ||
581 | } | ||
582 | |||
583 | bool workspace_switch(struct sway_workspace *workspace) { | ||
584 | struct sway_seat *seat = input_manager_current_seat(); | ||
583 | 585 | ||
584 | sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", | 586 | sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", |
585 | workspace, workspace->name); | 587 | workspace, workspace->name); |
@@ -657,15 +659,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace, | |||
657 | 659 | ||
658 | struct sway_output *workspace_output_get_highest_available( | 660 | struct sway_output *workspace_output_get_highest_available( |
659 | struct sway_workspace *ws, struct sway_output *exclude) { | 661 | struct sway_workspace *ws, struct sway_output *exclude) { |
660 | char exclude_id[128] = {'\0'}; | ||
661 | if (exclude) { | ||
662 | output_get_identifier(exclude_id, sizeof(exclude_id), exclude); | ||
663 | } | ||
664 | |||
665 | for (int i = 0; i < ws->output_priority->length; i++) { | 662 | for (int i = 0; i < ws->output_priority->length; i++) { |
666 | char *name = ws->output_priority->items[i]; | 663 | const char *name = ws->output_priority->items[i]; |
667 | if (exclude && (strcmp(name, exclude->wlr_output->name) == 0 | 664 | if (exclude && output_match_name_or_id(exclude, name)) { |
668 | || strcmp(name, exclude_id) == 0)) { | ||
669 | continue; | 665 | continue; |
670 | } | 666 | } |
671 | 667 | ||
@@ -689,7 +685,6 @@ void workspace_detect_urgent(struct sway_workspace *workspace) { | |||
689 | if (workspace->urgent != new_urgent) { | 685 | if (workspace->urgent != new_urgent) { |
690 | workspace->urgent = new_urgent; | 686 | workspace->urgent = new_urgent; |
691 | ipc_event_workspace(NULL, workspace, "urgent"); | 687 | ipc_event_workspace(NULL, workspace, "urgent"); |
692 | output_damage_whole(workspace->output); | ||
693 | } | 688 | } |
694 | } | 689 | } |
695 | 690 | ||
@@ -736,13 +731,13 @@ struct sway_container *workspace_find_container(struct sway_workspace *ws, | |||
736 | } | 731 | } |
737 | 732 | ||
738 | static void set_workspace(struct sway_container *container, void *data) { | 733 | static void set_workspace(struct sway_container *container, void *data) { |
739 | container->workspace = container->parent->workspace; | 734 | container->pending.workspace = container->pending.parent->pending.workspace; |
740 | } | 735 | } |
741 | 736 | ||
742 | static void workspace_attach_tiling(struct sway_workspace *ws, | 737 | static void workspace_attach_tiling(struct sway_workspace *ws, |
743 | struct sway_container *con) { | 738 | struct sway_container *con) { |
744 | list_add(ws->tiling, con); | 739 | list_add(ws->tiling, con); |
745 | con->workspace = ws; | 740 | con->pending.workspace = ws; |
746 | container_for_each_child(con, set_workspace, NULL); | 741 | container_for_each_child(con, set_workspace, NULL); |
747 | container_handle_fullscreen_reparent(con); | 742 | container_handle_fullscreen_reparent(con); |
748 | workspace_update_representation(ws); | 743 | workspace_update_representation(ws); |
@@ -753,7 +748,7 @@ static void workspace_attach_tiling(struct sway_workspace *ws, | |||
753 | struct sway_container *workspace_wrap_children(struct sway_workspace *ws) { | 748 | struct sway_container *workspace_wrap_children(struct sway_workspace *ws) { |
754 | struct sway_container *fs = ws->fullscreen; | 749 | struct sway_container *fs = ws->fullscreen; |
755 | struct sway_container *middle = container_create(NULL); | 750 | struct sway_container *middle = container_create(NULL); |
756 | middle->layout = ws->layout; | 751 | middle->pending.layout = ws->layout; |
757 | while (ws->tiling->length) { | 752 | while (ws->tiling->length) { |
758 | struct sway_container *child = ws->tiling->items[0]; | 753 | struct sway_container *child = ws->tiling->items[0]; |
759 | container_detach(child); | 754 | container_detach(child); |
@@ -771,9 +766,9 @@ void workspace_unwrap_children(struct sway_workspace *ws, | |||
771 | return; | 766 | return; |
772 | } | 767 | } |
773 | 768 | ||
774 | ws->layout = wrap->layout; | 769 | ws->layout = wrap->pending.layout; |
775 | while (wrap->children->length) { | 770 | while (wrap->pending.children->length) { |
776 | struct sway_container *child = wrap->children->items[0]; | 771 | struct sway_container *child = wrap->pending.children->items[0]; |
777 | container_detach(child); | 772 | container_detach(child); |
778 | workspace_add_tiling(ws, child); | 773 | workspace_add_tiling(ws, child); |
779 | } | 774 | } |
@@ -793,14 +788,18 @@ void workspace_detach(struct sway_workspace *workspace) { | |||
793 | 788 | ||
794 | struct sway_container *workspace_add_tiling(struct sway_workspace *workspace, | 789 | struct sway_container *workspace_add_tiling(struct sway_workspace *workspace, |
795 | struct sway_container *con) { | 790 | struct sway_container *con) { |
796 | if (con->workspace) { | 791 | if (con->pending.workspace) { |
792 | struct sway_container *old_parent = con->pending.parent; | ||
797 | container_detach(con); | 793 | container_detach(con); |
794 | if (old_parent) { | ||
795 | container_reap_empty(old_parent); | ||
796 | } | ||
798 | } | 797 | } |
799 | if (config->default_layout != L_NONE) { | 798 | if (config->default_layout != L_NONE) { |
800 | con = container_split(con, config->default_layout); | 799 | con = container_split(con, config->default_layout); |
801 | } | 800 | } |
802 | list_add(workspace->tiling, con); | 801 | list_add(workspace->tiling, con); |
803 | con->workspace = workspace; | 802 | con->pending.workspace = workspace; |
804 | container_for_each_child(con, set_workspace, NULL); | 803 | container_for_each_child(con, set_workspace, NULL); |
805 | container_handle_fullscreen_reparent(con); | 804 | container_handle_fullscreen_reparent(con); |
806 | workspace_update_representation(workspace); | 805 | workspace_update_representation(workspace); |
@@ -811,11 +810,11 @@ struct sway_container *workspace_add_tiling(struct sway_workspace *workspace, | |||
811 | 810 | ||
812 | void workspace_add_floating(struct sway_workspace *workspace, | 811 | void workspace_add_floating(struct sway_workspace *workspace, |
813 | struct sway_container *con) { | 812 | struct sway_container *con) { |
814 | if (con->workspace) { | 813 | if (con->pending.workspace) { |
815 | container_detach(con); | 814 | container_detach(con); |
816 | } | 815 | } |
817 | list_add(workspace->floating, con); | 816 | list_add(workspace->floating, con); |
818 | con->workspace = workspace; | 817 | con->pending.workspace = workspace; |
819 | container_for_each_child(con, set_workspace, NULL); | 818 | container_for_each_child(con, set_workspace, NULL); |
820 | container_handle_fullscreen_reparent(con); | 819 | container_handle_fullscreen_reparent(con); |
821 | node_set_dirty(&workspace->node); | 820 | node_set_dirty(&workspace->node); |
@@ -825,7 +824,7 @@ void workspace_add_floating(struct sway_workspace *workspace, | |||
825 | void workspace_insert_tiling_direct(struct sway_workspace *workspace, | 824 | void workspace_insert_tiling_direct(struct sway_workspace *workspace, |
826 | struct sway_container *con, int index) { | 825 | struct sway_container *con, int index) { |
827 | list_insert(workspace->tiling, index, con); | 826 | list_insert(workspace->tiling, index, con); |
828 | con->workspace = workspace; | 827 | con->pending.workspace = workspace; |
829 | container_for_each_child(con, set_workspace, NULL); | 828 | container_for_each_child(con, set_workspace, NULL); |
830 | container_handle_fullscreen_reparent(con); | 829 | container_handle_fullscreen_reparent(con); |
831 | workspace_update_representation(workspace); | 830 | workspace_update_representation(workspace); |
@@ -835,7 +834,7 @@ void workspace_insert_tiling_direct(struct sway_workspace *workspace, | |||
835 | 834 | ||
836 | struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, | 835 | struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, |
837 | struct sway_container *con, int index) { | 836 | struct sway_container *con, int index) { |
838 | if (con->workspace) { | 837 | if (con->pending.workspace) { |
839 | container_detach(con); | 838 | container_detach(con); |
840 | } | 839 | } |
841 | if (config->default_layout != L_NONE) { | 840 | if (config->default_layout != L_NONE) { |
@@ -845,24 +844,36 @@ struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, | |||
845 | return con; | 844 | return con; |
846 | } | 845 | } |
847 | 846 | ||
847 | bool workspace_has_single_visible_container(struct sway_workspace *ws) { | ||
848 | struct sway_seat *seat = input_manager_get_default_seat(); | ||
849 | struct sway_container *focus = | ||
850 | seat_get_focus_inactive_tiling(seat, ws); | ||
851 | if (focus && !focus->view) { | ||
852 | focus = seat_get_focus_inactive_view(seat, &focus->node); | ||
853 | } | ||
854 | return (focus && focus->view && view_ancestor_is_only_visible(focus->view)); | ||
855 | } | ||
856 | |||
848 | void workspace_add_gaps(struct sway_workspace *ws) { | 857 | void workspace_add_gaps(struct sway_workspace *ws) { |
849 | if (config->smart_gaps) { | 858 | if (config->smart_gaps == SMART_GAPS_ON |
850 | struct sway_seat *seat = input_manager_get_default_seat(); | 859 | && workspace_has_single_visible_container(ws)) { |
851 | struct sway_container *focus = | 860 | ws->current_gaps.top = 0; |
852 | seat_get_focus_inactive_tiling(seat, ws); | 861 | ws->current_gaps.right = 0; |
853 | if (focus && !focus->view) { | 862 | ws->current_gaps.bottom = 0; |
854 | focus = seat_get_focus_inactive_view(seat, &focus->node); | 863 | ws->current_gaps.left = 0; |
855 | } | 864 | return; |
856 | if (focus && focus->view && view_ancestor_is_only_visible(focus->view)) { | 865 | } |
857 | ws->current_gaps.top = 0; | 866 | |
858 | ws->current_gaps.right = 0; | 867 | if (config->smart_gaps == SMART_GAPS_INVERSE_OUTER |
859 | ws->current_gaps.bottom = 0; | 868 | && !workspace_has_single_visible_container(ws)) { |
860 | ws->current_gaps.left = 0; | 869 | ws->current_gaps.top = 0; |
861 | return; | 870 | ws->current_gaps.right = 0; |
862 | } | 871 | ws->current_gaps.bottom = 0; |
872 | ws->current_gaps.left = 0; | ||
873 | } else { | ||
874 | ws->current_gaps = ws->gaps_outer; | ||
863 | } | 875 | } |
864 | 876 | ||
865 | ws->current_gaps = ws->gaps_outer; | ||
866 | // Add inner gaps and make sure we don't turn out negative | 877 | // Add inner gaps and make sure we don't turn out negative |
867 | ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); | 878 | ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); |
868 | ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); | 879 | ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); |
@@ -905,7 +916,7 @@ struct sway_container *workspace_split(struct sway_workspace *workspace, | |||
905 | enum sway_container_layout old_layout = workspace->layout; | 916 | enum sway_container_layout old_layout = workspace->layout; |
906 | struct sway_container *middle = workspace_wrap_children(workspace); | 917 | struct sway_container *middle = workspace_wrap_children(workspace); |
907 | workspace->layout = layout; | 918 | workspace->layout = layout; |
908 | middle->layout = old_layout; | 919 | middle->pending.layout = old_layout; |
909 | 920 | ||
910 | struct sway_seat *seat; | 921 | struct sway_seat *seat; |
911 | wl_list_for_each(seat, &server.input->seats, link) { | 922 | wl_list_for_each(seat, &server.input->seats, link) { |
diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c new file mode 100644 index 00000000..b7c80dd4 --- /dev/null +++ b/sway/xdg_activation_v1.c | |||
@@ -0,0 +1,65 @@ | |||
1 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
2 | #include <wlr/types/wlr_xdg_shell.h> | ||
3 | #include "sway/desktop/launcher.h" | ||
4 | #include "sway/tree/view.h" | ||
5 | #include "sway/tree/workspace.h" | ||
6 | |||
7 | void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, | ||
8 | void *data) { | ||
9 | const struct wlr_xdg_activation_v1_request_activate_event *event = data; | ||
10 | |||
11 | struct wlr_xdg_surface *xdg_surface = | ||
12 | wlr_xdg_surface_try_from_wlr_surface(event->surface); | ||
13 | if (xdg_surface == NULL) { | ||
14 | return; | ||
15 | } | ||
16 | struct sway_view *view = xdg_surface->data; | ||
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 | } | ||
37 | return; | ||
38 | } | ||
39 | |||
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 | } | ||
65 | } | ||
diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c index e7c3ea73..fa8c6279 100644 --- a/sway/xdg_decoration.c +++ b/sway/xdg_decoration.c | |||
@@ -10,7 +10,7 @@ static void xdg_decoration_handle_destroy(struct wl_listener *listener, | |||
10 | void *data) { | 10 | void *data) { |
11 | struct sway_xdg_decoration *deco = | 11 | struct sway_xdg_decoration *deco = |
12 | wl_container_of(listener, deco, destroy); | 12 | wl_container_of(listener, deco, destroy); |
13 | if(deco->view) { | 13 | if (deco->view) { |
14 | deco->view->xdg_decoration = NULL; | 14 | deco->view->xdg_decoration = NULL; |
15 | } | 15 | } |
16 | wl_list_remove(&deco->destroy.link); | 16 | wl_list_remove(&deco->destroy.link); |
@@ -23,13 +23,12 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener, | |||
23 | void *data) { | 23 | void *data) { |
24 | struct sway_xdg_decoration *deco = | 24 | struct sway_xdg_decoration *deco = |
25 | wl_container_of(listener, deco, request_mode); | 25 | wl_container_of(listener, deco, request_mode); |
26 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | 26 | set_xdg_decoration_mode(deco); |
27 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); | ||
28 | } | 27 | } |
29 | 28 | ||
30 | void handle_xdg_decoration(struct wl_listener *listener, void *data) { | 29 | void handle_xdg_decoration(struct wl_listener *listener, void *data) { |
31 | struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; | 30 | struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; |
32 | struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->surface->data; | 31 | struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->toplevel->base->data; |
33 | 32 | ||
34 | struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); | 33 | struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); |
35 | if (deco == NULL) { | 34 | if (deco == NULL) { |
@@ -48,16 +47,46 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data) { | |||
48 | 47 | ||
49 | wl_list_insert(&server.xdg_decorations, &deco->link); | 48 | wl_list_insert(&server.xdg_decorations, &deco->link); |
50 | 49 | ||
51 | xdg_decoration_handle_request_mode(&deco->request_mode, wlr_deco); | 50 | set_xdg_decoration_mode(deco); |
52 | } | 51 | } |
53 | 52 | ||
54 | struct sway_xdg_decoration *xdg_decoration_from_surface( | 53 | struct sway_xdg_decoration *xdg_decoration_from_surface( |
55 | struct wlr_surface *surface) { | 54 | struct wlr_surface *surface) { |
56 | struct sway_xdg_decoration *deco; | 55 | struct sway_xdg_decoration *deco; |
57 | wl_list_for_each(deco, &server.xdg_decorations, link) { | 56 | wl_list_for_each(deco, &server.xdg_decorations, link) { |
58 | if (deco->wlr_xdg_decoration->surface->surface == surface) { | 57 | if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) { |
59 | return deco; | 58 | return deco; |
60 | } | 59 | } |
61 | } | 60 | } |
62 | return NULL; | 61 | return NULL; |
63 | } | 62 | } |
63 | |||
64 | void set_xdg_decoration_mode(struct sway_xdg_decoration *deco) { | ||
65 | struct sway_view *view = deco->view; | ||
66 | enum wlr_xdg_toplevel_decoration_v1_mode mode = | ||
67 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; | ||
68 | enum wlr_xdg_toplevel_decoration_v1_mode client_mode = | ||
69 | deco->wlr_xdg_decoration->requested_mode; | ||
70 | |||
71 | bool floating; | ||
72 | if (view->container) { | ||
73 | floating = container_is_floating(view->container); | ||
74 | bool csd = false; | ||
75 | csd = client_mode == | ||
76 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; | ||
77 | view_update_csd_from_client(view, csd); | ||
78 | arrange_container(view->container); | ||
79 | transaction_commit_dirty(); | ||
80 | } else { | ||
81 | floating = view->impl->wants_floating && | ||
82 | view->impl->wants_floating(view); | ||
83 | } | ||
84 | |||
85 | if (floating && client_mode) { | ||
86 | mode = client_mode; | ||
87 | } | ||
88 | |||
89 | if (view->wlr_xdg_toplevel->base->initialized) { | ||
90 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, mode); | ||
91 | } | ||
92 | } | ||
diff --git a/swaybar/bar.c b/swaybar/bar.c index 231c1ad7..5b1213a8 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <errno.h> | 2 | #include <errno.h> |
4 | #include <fcntl.h> | 3 | #include <fcntl.h> |
@@ -51,10 +50,6 @@ static void swaybar_output_free(struct swaybar_output *output) { | |||
51 | if (output->surface != NULL) { | 50 | if (output->surface != NULL) { |
52 | wl_surface_destroy(output->surface); | 51 | wl_surface_destroy(output->surface); |
53 | } | 52 | } |
54 | if (output->input_region != NULL) { | ||
55 | wl_region_destroy(output->input_region); | ||
56 | } | ||
57 | zxdg_output_v1_destroy(output->xdg_output); | ||
58 | wl_output_destroy(output->output); | 53 | wl_output_destroy(output->output); |
59 | destroy_buffer(&output->buffers[0]); | 54 | destroy_buffer(&output->buffers[0]); |
60 | destroy_buffer(&output->buffers[1]); | 55 | destroy_buffer(&output->buffers[1]); |
@@ -90,7 +85,7 @@ static void layer_surface_closed(void *_output, | |||
90 | swaybar_output_free(output); | 85 | swaybar_output_free(output); |
91 | } | 86 | } |
92 | 87 | ||
93 | struct zwlr_layer_surface_v1_listener layer_surface_listener = { | 88 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { |
94 | .configure = layer_surface_configure, | 89 | .configure = layer_surface_configure, |
95 | .closed = layer_surface_closed, | 90 | .closed = layer_surface_closed, |
96 | }; | 91 | }; |
@@ -114,10 +109,9 @@ static void add_layer_surface(struct swaybar_output *output) { | |||
114 | 109 | ||
115 | if (overlay) { | 110 | if (overlay) { |
116 | // Empty input region | 111 | // Empty input region |
117 | output->input_region = wl_compositor_create_region(bar->compositor); | 112 | struct wl_region *region = wl_compositor_create_region(bar->compositor); |
118 | assert(output->input_region); | 113 | wl_surface_set_input_region(output->surface, region); |
119 | 114 | wl_region_destroy(region); | |
120 | wl_surface_set_input_region(output->surface, output->input_region); | ||
121 | } | 115 | } |
122 | 116 | ||
123 | zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position); | 117 | zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position); |
@@ -172,7 +166,7 @@ bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) { | |||
172 | if (bar->status) { | 166 | if (bar->status) { |
173 | sway_log(SWAY_DEBUG, "Sending %s signal to status command", | 167 | sway_log(SWAY_DEBUG, "Sending %s signal to status command", |
174 | visible ? "cont" : "stop"); | 168 | visible ? "cont" : "stop"); |
175 | kill(bar->status->pid, visible ? | 169 | kill(-bar->status->pid, visible ? |
176 | bar->status->cont_signal : bar->status->stop_signal); | 170 | bar->status->cont_signal : bar->status->stop_signal); |
177 | } | 171 | } |
178 | } | 172 | } |
@@ -230,7 +224,7 @@ static void output_scale(void *data, struct wl_output *wl_output, | |||
230 | } | 224 | } |
231 | } | 225 | } |
232 | 226 | ||
233 | struct wl_output_listener output_listener = { | 227 | static const struct wl_output_listener output_listener = { |
234 | .geometry = output_geometry, | 228 | .geometry = output_geometry, |
235 | .mode = output_mode, | 229 | .mode = output_mode, |
236 | .done = output_done, | 230 | .done = output_done, |
@@ -307,7 +301,7 @@ static void xdg_output_handle_description(void *data, | |||
307 | } | 301 | } |
308 | } | 302 | } |
309 | 303 | ||
310 | struct zxdg_output_v1_listener xdg_output_listener = { | 304 | static const struct zxdg_output_v1_listener xdg_output_listener = { |
311 | .logical_position = xdg_output_handle_logical_position, | 305 | .logical_position = xdg_output_handle_logical_position, |
312 | .logical_size = xdg_output_handle_logical_size, | 306 | .logical_size = xdg_output_handle_logical_size, |
313 | .done = xdg_output_handle_done, | 307 | .done = xdg_output_handle_done, |
@@ -367,6 +361,9 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
367 | } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { | 361 | } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { |
368 | bar->xdg_output_manager = wl_registry_bind(registry, name, | 362 | bar->xdg_output_manager = wl_registry_bind(registry, name, |
369 | &zxdg_output_manager_v1_interface, 2); | 363 | &zxdg_output_manager_v1_interface, 2); |
364 | } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { | ||
365 | bar->cursor_shape_manager = wl_registry_bind(registry, name, | ||
366 | &wp_cursor_shape_manager_v1_interface, 1); | ||
370 | } | 367 | } |
371 | } | 368 | } |
372 | 369 | ||
@@ -430,15 +427,17 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) { | |||
430 | // Second roundtrip for xdg-output | 427 | // Second roundtrip for xdg-output |
431 | wl_display_roundtrip(bar->display); | 428 | wl_display_roundtrip(bar->display); |
432 | 429 | ||
433 | struct swaybar_seat *seat; | 430 | if (!bar->cursor_shape_manager) { |
434 | wl_list_for_each(seat, &bar->seats, link) { | 431 | struct swaybar_seat *seat; |
435 | struct swaybar_pointer *pointer = &seat->pointer; | 432 | wl_list_for_each(seat, &bar->seats, link) { |
436 | if (!pointer) { | 433 | struct swaybar_pointer *pointer = &seat->pointer; |
437 | continue; | 434 | if (!pointer) { |
435 | continue; | ||
436 | } | ||
437 | pointer->cursor_surface = | ||
438 | wl_compositor_create_surface(bar->compositor); | ||
439 | assert(pointer->cursor_surface); | ||
438 | } | 440 | } |
439 | pointer->cursor_surface = | ||
440 | wl_compositor_create_surface(bar->compositor); | ||
441 | assert(pointer->cursor_surface); | ||
442 | } | 441 | } |
443 | 442 | ||
444 | if (bar->config->status_command) { | 443 | if (bar->config->status_command) { |
@@ -461,13 +460,28 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) { | |||
461 | 460 | ||
462 | static void display_in(int fd, short mask, void *data) { | 461 | static void display_in(int fd, short mask, void *data) { |
463 | struct swaybar *bar = data; | 462 | struct swaybar *bar = data; |
463 | if (mask & (POLLHUP | POLLERR)) { | ||
464 | if (mask & POLLERR) { | ||
465 | sway_log(SWAY_ERROR, "Wayland display poll error"); | ||
466 | } | ||
467 | bar->running = false; | ||
468 | return; | ||
469 | } | ||
464 | if (wl_display_dispatch(bar->display) == -1) { | 470 | if (wl_display_dispatch(bar->display) == -1) { |
471 | sway_log(SWAY_ERROR, "wl_display_dispatch failed"); | ||
465 | bar->running = false; | 472 | bar->running = false; |
466 | } | 473 | } |
467 | } | 474 | } |
468 | 475 | ||
469 | static void ipc_in(int fd, short mask, void *data) { | 476 | static void ipc_in(int fd, short mask, void *data) { |
470 | struct swaybar *bar = data; | 477 | struct swaybar *bar = data; |
478 | if (mask & (POLLHUP | POLLERR)) { | ||
479 | if (mask & POLLERR) { | ||
480 | sway_log(SWAY_ERROR, "IPC poll error"); | ||
481 | } | ||
482 | bar->running = false; | ||
483 | return; | ||
484 | } | ||
471 | if (handle_ipc_readable(bar)) { | 485 | if (handle_ipc_readable(bar)) { |
472 | set_bar_dirty(bar); | 486 | set_bar_dirty(bar); |
473 | } | 487 | } |
diff --git a/swaybar/config.c b/swaybar/config.c index abedaec0..55bfcb72 100644 --- a/swaybar/config.c +++ b/swaybar/config.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include "swaybar/config.h" | 3 | #include "swaybar/config.h" |
@@ -26,7 +25,7 @@ struct swaybar_config *init_config(void) { | |||
26 | config->status_command = NULL; | 25 | config->status_command = NULL; |
27 | config->pango_markup = false; | 26 | config->pango_markup = false; |
28 | config->position = parse_position("bottom"); | 27 | config->position = parse_position("bottom"); |
29 | config->font = strdup("monospace 10"); | 28 | config->font_description = pango_font_description_from_string("monospace 10"); |
30 | config->mode = strdup("dock"); | 29 | config->mode = strdup("dock"); |
31 | config->hidden_state = strdup("hide"); | 30 | config->hidden_state = strdup("hide"); |
32 | config->sep_symbol = NULL; | 31 | config->sep_symbol = NULL; |
@@ -105,7 +104,7 @@ void free_tray_binding(struct tray_binding *binding) { | |||
105 | 104 | ||
106 | void free_config(struct swaybar_config *config) { | 105 | void free_config(struct swaybar_config *config) { |
107 | free(config->status_command); | 106 | free(config->status_command); |
108 | free(config->font); | 107 | pango_font_description_free(config->font_description); |
109 | free(config->mode); | 108 | free(config->mode); |
110 | free(config->hidden_state); | 109 | free(config->hidden_state); |
111 | free(config->sep_symbol); | 110 | free(config->sep_symbol); |
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 4bcd5843..62c22d43 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <json.h> | 1 | #include <json.h> |
3 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
4 | #include <ctype.h> | 3 | #include <ctype.h> |
@@ -28,6 +27,19 @@ void i3bar_block_unref(struct i3bar_block *block) { | |||
28 | } | 27 | } |
29 | } | 28 | } |
30 | 29 | ||
30 | static bool i3bar_parse_json_color(json_object *json, uint32_t *color) { | ||
31 | if (!json) { | ||
32 | return false; | ||
33 | } | ||
34 | |||
35 | const char *hexstring = json_object_get_string(json); | ||
36 | bool color_set = parse_color(hexstring, color); | ||
37 | if (!color_set) { | ||
38 | sway_log(SWAY_ERROR, "Ignoring invalid block hexadecimal color string: %s", hexstring); | ||
39 | } | ||
40 | return color_set; | ||
41 | } | ||
42 | |||
31 | static void i3bar_parse_json(struct status_line *status, | 43 | static void i3bar_parse_json(struct status_line *status, |
32 | struct json_object *json_array) { | 44 | struct json_object *json_array) { |
33 | struct i3bar_block *block, *tmp; | 45 | struct i3bar_block *block, *tmp; |
@@ -68,13 +80,7 @@ static void i3bar_parse_json(struct status_line *status, | |||
68 | strdup(json_object_get_string(full_text)) : NULL; | 80 | strdup(json_object_get_string(full_text)) : NULL; |
69 | block->short_text = short_text ? | 81 | block->short_text = short_text ? |
70 | strdup(json_object_get_string(short_text)) : NULL; | 82 | strdup(json_object_get_string(short_text)) : NULL; |
71 | if (color) { | 83 | block->color_set = i3bar_parse_json_color(color, &block->color); |
72 | const char *hexstring = json_object_get_string(color); | ||
73 | block->color_set = parse_color(hexstring, &block->color); | ||
74 | if (!block->color_set) { | ||
75 | sway_log(SWAY_ERROR, "Invalid block color: %s", hexstring); | ||
76 | } | ||
77 | } | ||
78 | if (min_width) { | 84 | if (min_width) { |
79 | json_type type = json_object_get_type(min_width); | 85 | json_type type = json_object_get_type(min_width); |
80 | if (type == json_type_int) { | 86 | if (type == json_type_int) { |
@@ -100,14 +106,8 @@ static void i3bar_parse_json(struct status_line *status, | |||
100 | block->separator_block_width = separator_block_width ? | 106 | block->separator_block_width = separator_block_width ? |
101 | json_object_get_int(separator_block_width) : 9; | 107 | json_object_get_int(separator_block_width) : 9; |
102 | // Airblader features | 108 | // Airblader features |
103 | const char *hex = background ? json_object_get_string(background) : NULL; | 109 | i3bar_parse_json_color(background, &block->background); |
104 | if (hex && !parse_color(hex, &block->background)) { | 110 | block->border_set = i3bar_parse_json_color(border, &block->border); |
105 | sway_log(SWAY_ERROR, "Ignoring invalid block background: %s", hex); | ||
106 | } | ||
107 | hex = border ? json_object_get_string(border) : NULL; | ||
108 | if (hex && !parse_color(hex, &block->border)) { | ||
109 | sway_log(SWAY_ERROR, "Ignoring invalid block border: %s", hex); | ||
110 | } | ||
111 | block->border_top = border_top ? json_object_get_int(border_top) : 1; | 111 | block->border_top = border_top ? json_object_get_int(border_top) : 1; |
112 | block->border_bottom = border_bottom ? | 112 | block->border_bottom = border_bottom ? |
113 | json_object_get_int(border_bottom) : 1; | 113 | json_object_get_int(border_bottom) : 1; |
@@ -268,11 +268,16 @@ bool i3bar_handle_readable(struct status_line *status) { | |||
268 | 268 | ||
269 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, | 269 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, |
270 | struct i3bar_block *block, double x, double y, double rx, double ry, | 270 | struct i3bar_block *block, double x, double y, double rx, double ry, |
271 | double w, double h, int scale, uint32_t button) { | 271 | double w, double h, int scale, uint32_t button, bool released) { |
272 | sway_log(SWAY_DEBUG, "block %s clicked", block->name); | 272 | sway_log(SWAY_DEBUG, "block %s clicked", block->name); |
273 | if (!block->name || !status->click_events) { | 273 | if (!block->name || !status->click_events) { |
274 | return HOTSPOT_PROCESS; | 274 | return HOTSPOT_PROCESS; |
275 | } | 275 | } |
276 | if (released) { | ||
277 | // Since we handle the pressed event, also handle the released event | ||
278 | // to block it from falling through to a binding in the bar | ||
279 | return HOTSPOT_IGNORE; | ||
280 | } | ||
276 | 281 | ||
277 | struct json_object *event_json = json_object_new_object(); | 282 | struct json_object *event_json = json_object_new_object(); |
278 | json_object_object_add(event_json, "name", | 283 | json_object_object_add(event_json, "name", |
diff --git a/common/background-image.c b/swaybar/image.c index de42e8e9..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.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 4fe6dd93..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, |
@@ -101,7 +109,9 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | |||
101 | wl_fixed_t surface_x, wl_fixed_t surface_y) { | 109 | wl_fixed_t surface_x, wl_fixed_t surface_y) { |
102 | struct swaybar_seat *seat = data; | 110 | struct swaybar_seat *seat = data; |
103 | struct swaybar_pointer *pointer = &seat->pointer; | 111 | struct swaybar_pointer *pointer = &seat->pointer; |
104 | pointer->serial = serial; | 112 | seat->pointer.x = wl_fixed_to_double(surface_x); |
113 | seat->pointer.y = wl_fixed_to_double(surface_y); | ||
114 | |||
105 | struct swaybar_output *output; | 115 | struct swaybar_output *output; |
106 | wl_list_for_each(output, &seat->bar->outputs, link) { | 116 | wl_list_for_each(output, &seat->bar->outputs, link) { |
107 | if (output->surface == surface) { | 117 | if (output->surface == surface) { |
@@ -109,7 +119,18 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | |||
109 | break; | 119 | break; |
110 | } | 120 | } |
111 | } | 121 | } |
112 | 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 | } | ||
113 | } | 134 | } |
114 | 135 | ||
115 | 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, |
@@ -139,16 +160,15 @@ static bool check_bindings(struct swaybar *bar, uint32_t button, | |||
139 | } | 160 | } |
140 | 161 | ||
141 | static bool process_hotspots(struct swaybar_output *output, | 162 | static bool process_hotspots(struct swaybar_output *output, |
142 | double x, double y, uint32_t button) { | 163 | double x, double y, uint32_t button, uint32_t state) { |
143 | double px = x * output->scale; | 164 | bool released = state == WL_POINTER_BUTTON_STATE_RELEASED; |
144 | double py = y * output->scale; | ||
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 (px >= hotspot->x && py >= hotspot->y | 167 | if (x >= hotspot->x && y >= hotspot->y |
148 | && px < hotspot->x + hotspot->width | 168 | && x < hotspot->x + hotspot->width |
149 | && py < 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 | ||
@@ -339,7 +356,7 @@ static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, | |||
339 | seat->axis[axis].discrete_steps += abs(discrete); | 356 | seat->axis[axis].discrete_steps += abs(discrete); |
340 | } | 357 | } |
341 | 358 | ||
342 | static struct wl_pointer_listener pointer_listener = { | 359 | static const struct wl_pointer_listener pointer_listener = { |
343 | .enter = wl_pointer_enter, | 360 | .enter = wl_pointer_enter, |
344 | .leave = wl_pointer_leave, | 361 | .leave = wl_pointer_leave, |
345 | .motion = wl_pointer_motion, | 362 | .motion = wl_pointer_motion, |
@@ -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 6bbe9408..03500bdf 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809 | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <poll.h> | 2 | #include <poll.h> |
4 | #include <stdio.h> | 3 | #include <stdio.h> |
@@ -147,8 +146,10 @@ static bool ipc_parse_config( | |||
147 | 146 | ||
148 | json_object *font = json_object_object_get(bar_config, "font"); | 147 | json_object *font = json_object_object_get(bar_config, "font"); |
149 | if (font) { | 148 | if (font) { |
150 | free(config->font); | 149 | pango_font_description_free(config->font_description); |
151 | config->font = parse_font(json_object_get_string(font)); | 150 | char *font_value = parse_font(json_object_get_string(font)); |
151 | config->font_description = pango_font_description_from_string(font_value); | ||
152 | free(font_value); | ||
152 | } | 153 | } |
153 | 154 | ||
154 | json_object *gaps = json_object_object_get(bar_config, "gaps"); | 155 | json_object *gaps = json_object_object_get(bar_config, "gaps"); |
@@ -424,12 +425,9 @@ bool ipc_initialize(struct swaybar *bar) { | |||
424 | } | 425 | } |
425 | free(res); | 426 | free(res); |
426 | 427 | ||
427 | struct swaybar_config *config = bar->config; | 428 | char *subscribe = |
428 | char subscribe[128]; // suitably large buffer | 429 | "[ \"barconfig_update\", \"bar_state_update\", \"mode\", \"workspace\" ]"; |
429 | len = snprintf(subscribe, 128, | 430 | len = strlen(subscribe); |
430 | "[ \"barconfig_update\" , \"bar_state_update\" %s %s ]", | ||
431 | config->binding_mode_indicator ? ", \"mode\"" : "", | ||
432 | config->workspace_buttons ? ", \"workspace\"" : ""); | ||
433 | free(ipc_single_command(bar->ipc_event_socketfd, | 431 | free(ipc_single_command(bar->ipc_event_socketfd, |
434 | IPC_SUBSCRIBE, subscribe, &len)); | 432 | IPC_SUBSCRIBE, subscribe, &len)); |
435 | return true; | 433 | return true; |
@@ -485,8 +483,7 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload, | |||
485 | destroy_layer_surface(output); | 483 | destroy_layer_surface(output); |
486 | wl_list_remove(&output->link); | 484 | wl_list_remove(&output->link); |
487 | wl_list_insert(&bar->unused_outputs, &output->link); | 485 | wl_list_insert(&bar->unused_outputs, &output->link); |
488 | } else if (!oldcfg->font || !newcfg->font || | 486 | } else if (!pango_font_description_equal(oldcfg->font_description, newcfg->font_description)) { |
489 | strcmp(oldcfg->font, newcfg->font) != 0) { | ||
490 | output->height = 0; // force update height | 487 | output->height = 0; // force update height |
491 | } | 488 | } |
492 | } | 489 | } |
@@ -547,9 +544,23 @@ bool handle_ipc_readable(struct swaybar *bar) { | |||
547 | return false; | 544 | return false; |
548 | } | 545 | } |
549 | 546 | ||
550 | json_object *result = json_tokener_parse(resp->payload); | 547 | // The default depth of 32 is too small to represent some nested layouts, but |
551 | if (!result) { | 548 | // we can't pass INT_MAX here because json-c (as of this writing) prefaults |
552 | sway_log(SWAY_ERROR, "failed to parse payload as json"); | 549 | // all the memory for its stack. |
550 | json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH); | ||
551 | if (!tok) { | ||
552 | sway_log_errno(SWAY_ERROR, "failed to create tokener"); | ||
553 | free_ipc_response(resp); | ||
554 | return false; | ||
555 | } | ||
556 | |||
557 | json_object *result = json_tokener_parse_ex(tok, resp->payload, -1); | ||
558 | enum json_tokener_error err = json_tokener_get_error(tok); | ||
559 | json_tokener_free(tok); | ||
560 | |||
561 | if (err != json_tokener_success) { | ||
562 | sway_log(SWAY_ERROR, "failed to parse payload as json: %s", | ||
563 | json_tokener_error_desc(err)); | ||
553 | free_ipc_response(resp); | 564 | free_ipc_response(resp); |
554 | return false; | 565 | return false; |
555 | } | 566 | } |
diff --git a/swaybar/main.c b/swaybar/main.c index 5c36d66b..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> |
@@ -18,7 +17,7 @@ int main(int argc, char **argv) { | |||
18 | char *socket_path = NULL; | 17 | char *socket_path = NULL; |
19 | bool debug = false; | 18 | bool debug = false; |
20 | 19 | ||
21 | static struct option long_options[] = { | 20 | static const struct option long_options[] = { |
22 | {"help", no_argument, NULL, 'h'}, | 21 | {"help", no_argument, NULL, 'h'}, |
23 | {"version", no_argument, NULL, 'v'}, | 22 | {"version", no_argument, NULL, 'v'}, |
24 | {"socket", required_argument, NULL, 's'}, | 23 | {"socket", required_argument, NULL, 's'}, |
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 df066622..879a4e42 100644 --- a/swaybar/render.c +++ b/swaybar/render.c | |||
@@ -1,11 +1,10 @@ | |||
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> |
5 | #include <stdlib.h> | 4 | #include <stdlib.h> |
6 | #include <stdint.h> | 5 | #include <stdint.h> |
7 | #include <string.h> | 6 | #include <string.h> |
8 | #include "cairo.h" | 7 | #include "cairo_util.h" |
9 | #include "pango.h" | 8 | #include "pango.h" |
10 | #include "pool-buffer.h" | 9 | #include "pool-buffer.h" |
11 | #include "swaybar/bar.h" | 10 | #include "swaybar/bar.h" |
@@ -14,6 +13,7 @@ | |||
14 | #include "swaybar/ipc.h" | 13 | #include "swaybar/ipc.h" |
15 | #include "swaybar/render.h" | 14 | #include "swaybar/render.h" |
16 | #include "swaybar/status_line.h" | 15 | #include "swaybar/status_line.h" |
16 | #include "log.h" | ||
17 | #if HAVE_TRAY | 17 | #if HAVE_TRAY |
18 | #include "swaybar/tray/tray.h" | 18 | #include "swaybar/tray/tray.h" |
19 | #endif | 19 | #endif |
@@ -23,28 +23,51 @@ static const int WS_HORIZONTAL_PADDING = 5; | |||
23 | static const double WS_VERTICAL_PADDING = 1.5; | 23 | static const double WS_VERTICAL_PADDING = 1.5; |
24 | static const double BORDER_WIDTH = 1; | 24 | static const double BORDER_WIDTH = 1; |
25 | 25 | ||
26 | static uint32_t render_status_line_error(cairo_t *cairo, | 26 | struct render_context { |
27 | struct swaybar_output *output, double *x) { | 27 | cairo_t *cairo; |
28 | struct swaybar_output *output; | ||
29 | cairo_font_options_t *textaa_sharp; | ||
30 | cairo_font_options_t *textaa_safe; | ||
31 | uint32_t background_color; | ||
32 | }; | ||
33 | |||
34 | static void choose_text_aa_mode(struct render_context *ctx, uint32_t fontcolor) { | ||
35 | uint32_t salpha = fontcolor & 0xFF; | ||
36 | uint32_t balpha = ctx->background_color & 0xFF; | ||
37 | |||
38 | // Subpixel antialiasing requires blend be done in cairo, not compositor | ||
39 | cairo_font_options_t *fo = salpha == balpha ? | ||
40 | ctx->textaa_sharp : ctx->textaa_safe; | ||
41 | cairo_set_font_options(ctx->cairo, fo); | ||
42 | |||
43 | // Color emojis, being semitransparent bitmaps, are leaky with 'SOURCE' | ||
44 | cairo_operator_t op = salpha == 0xFF ? | ||
45 | CAIRO_OPERATOR_OVER : CAIRO_OPERATOR_SOURCE; | ||
46 | cairo_set_operator(ctx->cairo, op); | ||
47 | } | ||
48 | |||
49 | static uint32_t render_status_line_error(struct render_context *ctx, double *x) { | ||
50 | struct swaybar_output *output = ctx->output; | ||
28 | const char *error = output->bar->status->text; | 51 | const char *error = output->bar->status->text; |
29 | if (!error) { | 52 | if (!error) { |
30 | return 0; | 53 | return 0; |
31 | } | 54 | } |
32 | 55 | ||
33 | uint32_t height = output->height * output->scale; | 56 | uint32_t height = output->height; |
34 | 57 | ||
58 | cairo_t *cairo = ctx->cairo; | ||
35 | cairo_set_source_u32(cairo, 0xFF0000FF); | 59 | cairo_set_source_u32(cairo, 0xFF0000FF); |
36 | 60 | ||
37 | int margin = 3 * output->scale; | 61 | int margin = 3; |
38 | double ws_vertical_padding = | 62 | double ws_vertical_padding = output->bar->config->status_padding; |
39 | output->bar->config->status_padding * output->scale; | ||
40 | 63 | ||
41 | char *font = output->bar->config->font; | 64 | PangoFontDescription *font = output->bar->config->font_description; |
42 | int text_width, text_height; | 65 | int text_width, text_height; |
43 | get_text_size(cairo, font, &text_width, &text_height, NULL, | 66 | get_text_size(cairo, font, &text_width, &text_height, NULL, |
44 | output->scale, false, "%s", error); | 67 | 1, false, "%s", error); |
45 | 68 | ||
46 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | 69 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; |
47 | uint32_t ideal_surface_height = ideal_height / output->scale; | 70 | uint32_t ideal_surface_height = ideal_height; |
48 | if (!output->bar->config->height && | 71 | if (!output->bar->config->height && |
49 | output->height < ideal_surface_height) { | 72 | output->height < ideal_surface_height) { |
50 | return ideal_surface_height; | 73 | return ideal_surface_height; |
@@ -53,42 +76,45 @@ static uint32_t render_status_line_error(cairo_t *cairo, | |||
53 | 76 | ||
54 | double text_y = height / 2.0 - text_height / 2.0; | 77 | double text_y = height / 2.0 - text_height / 2.0; |
55 | cairo_move_to(cairo, *x, (int)floor(text_y)); | 78 | cairo_move_to(cairo, *x, (int)floor(text_y)); |
56 | pango_printf(cairo, font, output->scale, false, "%s", error); | 79 | choose_text_aa_mode(ctx, 0xFF0000FF); |
80 | render_text(cairo, font, 1, false, "%s", error); | ||
57 | *x -= margin; | 81 | *x -= margin; |
58 | return output->height; | 82 | return output->height; |
59 | } | 83 | } |
60 | 84 | ||
61 | static uint32_t render_status_line_text(cairo_t *cairo, | 85 | static uint32_t render_status_line_text(struct render_context *ctx, double *x) { |
62 | struct swaybar_output *output, double *x) { | 86 | struct swaybar_output *output = ctx->output; |
63 | const char *text = output->bar->status->text; | 87 | const char *text = output->bar->status->text; |
64 | if (!text) { | 88 | if (!text) { |
65 | return 0; | 89 | return 0; |
66 | } | 90 | } |
67 | 91 | ||
92 | cairo_t *cairo = ctx->cairo; | ||
68 | struct swaybar_config *config = output->bar->config; | 93 | struct swaybar_config *config = output->bar->config; |
69 | cairo_set_source_u32(cairo, output->focused ? | 94 | uint32_t fontcolor = output->focused ? |
70 | config->colors.focused_statusline : config->colors.statusline); | 95 | config->colors.focused_statusline : config->colors.statusline; |
96 | cairo_set_source_u32(cairo, fontcolor); | ||
71 | 97 | ||
72 | int text_width, text_height; | 98 | int text_width, text_height; |
73 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 99 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, |
74 | output->scale, config->pango_markup, "%s", text); | 100 | 1, config->pango_markup, "%s", text); |
75 | 101 | ||
76 | double ws_vertical_padding = config->status_padding * output->scale; | 102 | double ws_vertical_padding = config->status_padding; |
77 | int margin = 3 * output->scale; | 103 | int margin = 3; |
78 | 104 | ||
79 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | 105 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; |
80 | uint32_t ideal_surface_height = ideal_height / output->scale; | 106 | uint32_t ideal_surface_height = ideal_height; |
81 | if (!output->bar->config->height && | 107 | if (!output->bar->config->height && |
82 | output->height < ideal_surface_height) { | 108 | output->height < ideal_surface_height) { |
83 | return ideal_surface_height; | 109 | return ideal_surface_height; |
84 | } | 110 | } |
85 | 111 | ||
86 | *x -= text_width + margin; | 112 | *x -= text_width + margin; |
87 | uint32_t height = output->height * output->scale; | 113 | uint32_t height = output->height; |
88 | double text_y = height / 2.0 - text_height / 2.0; | 114 | double text_y = height / 2.0 - text_height / 2.0; |
89 | cairo_move_to(cairo, *x, (int)floor(text_y)); | 115 | cairo_move_to(cairo, *x, (int)floor(text_y)); |
90 | pango_printf(cairo, config->font, output->scale, | 116 | choose_text_aa_mode(ctx, fontcolor); |
91 | config->pango_markup, "%s", text); | 117 | render_text(cairo, config->font_description, 1, config->pango_markup, "%s", text); |
92 | *x -= margin; | 118 | *x -= margin; |
93 | return output->height; | 119 | return output->height; |
94 | } | 120 | } |
@@ -96,6 +122,7 @@ static uint32_t render_status_line_text(cairo_t *cairo, | |||
96 | static void render_sharp_rectangle(cairo_t *cairo, uint32_t color, | 122 | static void render_sharp_rectangle(cairo_t *cairo, uint32_t color, |
97 | double x, double y, double width, double height) { | 123 | double x, double y, double width, double height) { |
98 | cairo_save(cairo); | 124 | cairo_save(cairo); |
125 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | ||
99 | cairo_set_source_u32(cairo, color); | 126 | cairo_set_source_u32(cairo, color); |
100 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); | 127 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); |
101 | cairo_rectangle(cairo, x, y, width, height); | 128 | cairo_rectangle(cairo, x, y, width, height); |
@@ -109,6 +136,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, | |||
109 | render_sharp_rectangle(cairo, color, x, y, width, height); | 136 | render_sharp_rectangle(cairo, color, x, y, width, height); |
110 | } else { | 137 | } else { |
111 | cairo_save(cairo); | 138 | cairo_save(cairo); |
139 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | ||
112 | cairo_set_source_u32(cairo, color); | 140 | cairo_set_source_u32(cairo, color); |
113 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); | 141 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); |
114 | if (width == 1) { | 142 | if (width == 1) { |
@@ -131,24 +159,23 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, | |||
131 | 159 | ||
132 | static enum hotspot_event_handling block_hotspot_callback( | 160 | static enum hotspot_event_handling block_hotspot_callback( |
133 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, | 161 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, |
134 | double x, double y, uint32_t button, void *data) { | 162 | double x, double y, uint32_t button, bool released, void *data) { |
135 | struct i3bar_block *block = data; | 163 | struct i3bar_block *block = data; |
136 | struct status_line *status = output->bar->status; | 164 | struct status_line *status = output->bar->status; |
137 | return i3bar_block_send_click(status, block, x, y, | 165 | return i3bar_block_send_click(status, block, x, y, |
138 | x - (double)hotspot->x / output->scale, | 166 | x - (double)hotspot->x, |
139 | y - (double)hotspot->y / output->scale, | 167 | y - (double)hotspot->y, |
140 | (double)hotspot->width / output->scale, | 168 | (double)hotspot->width, |
141 | (double)hotspot->height / output->scale, | 169 | (double)hotspot->height, |
142 | output->scale, button); | 170 | output->scale, button, released); |
143 | } | 171 | } |
144 | 172 | ||
145 | static void i3bar_block_unref_callback(void *data) { | 173 | static void i3bar_block_unref_callback(void *data) { |
146 | i3bar_block_unref(data); | 174 | i3bar_block_unref(data); |
147 | } | 175 | } |
148 | 176 | ||
149 | static uint32_t render_status_block(cairo_t *cairo, | 177 | static uint32_t render_status_block(struct render_context *ctx, |
150 | struct swaybar_output *output, struct i3bar_block *block, double *x, | 178 | struct i3bar_block *block, double *x, bool edge, bool use_short_text) { |
151 | bool edge, bool use_short_text) { | ||
152 | if (!block->full_text || !*block->full_text) { | 179 | if (!block->full_text || !*block->full_text) { |
153 | return 0; | 180 | return 0; |
154 | } | 181 | } |
@@ -158,20 +185,21 @@ static uint32_t render_status_block(cairo_t *cairo, | |||
158 | text = block->short_text; | 185 | text = block->short_text; |
159 | } | 186 | } |
160 | 187 | ||
188 | cairo_t *cairo = ctx->cairo; | ||
189 | struct swaybar_output *output = ctx->output; | ||
161 | struct swaybar_config *config = output->bar->config; | 190 | struct swaybar_config *config = output->bar->config; |
162 | |||
163 | int text_width, text_height; | 191 | int text_width, text_height; |
164 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 192 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, |
165 | output->scale, block->markup, "%s", text); | 193 | block->markup, "%s", text); |
166 | 194 | ||
167 | int margin = 3 * output->scale; | 195 | int margin = 3; |
168 | double ws_vertical_padding = config->status_padding * output->scale; | 196 | double ws_vertical_padding = config->status_padding; |
169 | 197 | ||
170 | int width = text_width; | 198 | int width = text_width; |
171 | if (block->min_width_str) { | 199 | if (block->min_width_str) { |
172 | int w; | 200 | int w; |
173 | get_text_size(cairo, config->font, &w, NULL, NULL, | 201 | get_text_size(cairo, config->font_description, &w, NULL, NULL, 1, block->markup, |
174 | output->scale, block->markup, "%s", block->min_width_str); | 202 | "%s", block->min_width_str); |
175 | block->min_width = w; | 203 | block->min_width = w; |
176 | } | 204 | } |
177 | if (width < block->min_width) { | 205 | if (width < block->min_width) { |
@@ -180,30 +208,30 @@ static uint32_t render_status_block(cairo_t *cairo, | |||
180 | 208 | ||
181 | double block_width = width; | 209 | double block_width = width; |
182 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | 210 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; |
183 | uint32_t ideal_surface_height = ideal_height / output->scale; | 211 | uint32_t ideal_surface_height = ideal_height; |
184 | if (!output->bar->config->height && | 212 | if (!output->bar->config->height && |
185 | output->height < ideal_surface_height) { | 213 | output->height < ideal_surface_height) { |
186 | return ideal_surface_height; | 214 | return ideal_surface_height; |
187 | } | 215 | } |
188 | 216 | ||
189 | *x -= width; | 217 | *x -= width; |
190 | if ((block->border || block->urgent) && block->border_left > 0) { | 218 | if ((block->border_set || block->urgent) && block->border_left > 0) { |
191 | *x -= (block->border_left * output->scale + margin); | 219 | *x -= (block->border_left + margin); |
192 | block_width += block->border_left * output->scale + margin; | 220 | block_width += block->border_left + margin; |
193 | } | 221 | } |
194 | if ((block->border || block->urgent) && block->border_right > 0) { | 222 | if ((block->border_set || block->urgent) && block->border_right > 0) { |
195 | *x -= (block->border_right * output->scale + margin); | 223 | *x -= (block->border_right + margin); |
196 | block_width += block->border_right * output->scale + margin; | 224 | block_width += block->border_right + margin; |
197 | } | 225 | } |
198 | 226 | ||
199 | int sep_width, sep_height; | 227 | int sep_width, sep_height; |
200 | int sep_block_width = block->separator_block_width; | 228 | int sep_block_width = block->separator_block_width; |
201 | if (!edge) { | 229 | if (!edge) { |
202 | if (config->sep_symbol) { | 230 | if (config->sep_symbol) { |
203 | get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, | 231 | get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL, |
204 | output->scale, false, "%s", config->sep_symbol); | 232 | 1, false, "%s", config->sep_symbol); |
205 | uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; | 233 | uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; |
206 | uint32_t _ideal_surface_height = _ideal_height / output->scale; | 234 | uint32_t _ideal_surface_height = _ideal_height; |
207 | if (!output->bar->config->height && | 235 | if (!output->bar->config->height && |
208 | output->height < _ideal_surface_height) { | 236 | output->height < _ideal_surface_height) { |
209 | return _ideal_surface_height; | 237 | return _ideal_surface_height; |
@@ -214,10 +242,10 @@ static uint32_t render_status_block(cairo_t *cairo, | |||
214 | } | 242 | } |
215 | *x -= sep_block_width; | 243 | *x -= sep_block_width; |
216 | } else if (config->status_edge_padding) { | 244 | } else if (config->status_edge_padding) { |
217 | *x -= config->status_edge_padding * output->scale; | 245 | *x -= config->status_edge_padding; |
218 | } | 246 | } |
219 | 247 | ||
220 | uint32_t height = output->height * output->scale; | 248 | uint32_t height = output->height; |
221 | if (output->bar->status->click_events) { | 249 | if (output->bar->status->click_events) { |
222 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); | 250 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); |
223 | hotspot->x = *x; | 251 | hotspot->x = *x; |
@@ -240,27 +268,30 @@ static uint32_t render_status_block(cairo_t *cairo, | |||
240 | if (bg_color) { | 268 | if (bg_color) { |
241 | render_sharp_rectangle(cairo, bg_color, x_pos, y_pos, | 269 | render_sharp_rectangle(cairo, bg_color, x_pos, y_pos, |
242 | block_width, render_height); | 270 | block_width, render_height); |
271 | ctx->background_color = bg_color; | ||
243 | } | 272 | } |
244 | 273 | ||
245 | uint32_t border_color = block->urgent | 274 | uint32_t border_color = block->urgent |
246 | ? config->colors.urgent_workspace.border : block->border; | 275 | ? config->colors.urgent_workspace.border : block->border; |
247 | if (border_color && block->border_top > 0) { | 276 | if (block->border_set || block->urgent) { |
248 | render_sharp_line(cairo, border_color, x_pos, y_pos, | 277 | if (block->border_top > 0) { |
249 | block_width, block->border_top * output->scale); | 278 | render_sharp_line(cairo, border_color, x_pos, y_pos, |
250 | } | 279 | block_width, block->border_top); |
251 | if (border_color && block->border_bottom > 0) { | 280 | } |
252 | render_sharp_line(cairo, border_color, x_pos, | 281 | if (block->border_bottom > 0) { |
253 | y_pos + render_height - block->border_bottom * output->scale, | 282 | render_sharp_line(cairo, border_color, x_pos, |
254 | block_width, block->border_bottom * output->scale); | 283 | y_pos + render_height - block->border_bottom, |
255 | } | 284 | block_width, block->border_bottom); |
256 | if (border_color && block->border_left > 0) { | 285 | } |
257 | render_sharp_line(cairo, border_color, x_pos, y_pos, | 286 | if (block->border_left > 0) { |
258 | block->border_left * output->scale, render_height); | 287 | render_sharp_line(cairo, border_color, x_pos, y_pos, |
259 | x_pos += block->border_left * output->scale + margin; | 288 | block->border_left, render_height); |
289 | } | ||
290 | x_pos += block->border_left + margin; | ||
260 | } | 291 | } |
261 | 292 | ||
262 | double offset = 0; | 293 | double offset = 0; |
263 | if (strncmp(block->align, "left", 5) == 0) { | 294 | if (strncmp(block->align, "left", 4) == 0) { |
264 | offset = x_pos; | 295 | offset = x_pos; |
265 | } else if (strncmp(block->align, "right", 5) == 0) { | 296 | } else if (strncmp(block->align, "right", 5) == 0) { |
266 | offset = x_pos + width - text_width; | 297 | offset = x_pos + width - text_width; |
@@ -274,30 +305,35 @@ static uint32_t render_status_block(cairo_t *cairo, | |||
274 | color = block->color_set ? block->color : color; | 305 | color = block->color_set ? block->color : color; |
275 | color = block->urgent ? config->colors.urgent_workspace.text : color; | 306 | color = block->urgent ? config->colors.urgent_workspace.text : color; |
276 | cairo_set_source_u32(cairo, color); | 307 | cairo_set_source_u32(cairo, color); |
277 | pango_printf(cairo, config->font, output->scale, | 308 | choose_text_aa_mode(ctx, color); |
278 | block->markup, "%s", text); | 309 | render_text(cairo, config->font_description, 1, block->markup, "%s", text); |
279 | x_pos += width; | 310 | x_pos += width; |
280 | 311 | ||
281 | if (block->border && block->border_right > 0) { | 312 | if (block->border_set || block->urgent) { |
282 | x_pos += margin; | 313 | x_pos += margin; |
283 | render_sharp_line(cairo, border_color, x_pos, y_pos, | 314 | if (block->border_right > 0) { |
284 | block->border_right * output->scale, render_height); | 315 | render_sharp_line(cairo, border_color, x_pos, y_pos, |
285 | x_pos += block->border_right * output->scale; | 316 | block->border_right, render_height); |
317 | } | ||
318 | x_pos += block->border_right; | ||
286 | } | 319 | } |
287 | 320 | ||
288 | if (!edge && block->separator) { | 321 | if (!edge && block->separator) { |
289 | if (output->focused) { | 322 | if (output->focused) { |
290 | cairo_set_source_u32(cairo, config->colors.focused_separator); | 323 | color = config->colors.focused_separator; |
291 | } else { | 324 | } else { |
292 | cairo_set_source_u32(cairo, config->colors.separator); | 325 | color = config->colors.separator; |
293 | } | 326 | } |
327 | cairo_set_source_u32(cairo, color); | ||
294 | if (config->sep_symbol) { | 328 | if (config->sep_symbol) { |
295 | offset = x_pos + (sep_block_width - sep_width) / 2; | 329 | offset = x_pos + (sep_block_width - sep_width) / 2; |
296 | double sep_y = height / 2.0 - sep_height / 2.0; | 330 | double sep_y = height / 2.0 - sep_height / 2.0; |
297 | cairo_move_to(cairo, offset, (int)floor(sep_y)); | 331 | cairo_move_to(cairo, offset, (int)floor(sep_y)); |
298 | pango_printf(cairo, config->font, output->scale, false, | 332 | choose_text_aa_mode(ctx, color); |
333 | render_text(cairo, config->font_description, 1, false, | ||
299 | "%s", config->sep_symbol); | 334 | "%s", config->sep_symbol); |
300 | } else { | 335 | } else { |
336 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | ||
301 | cairo_set_line_width(cairo, 1); | 337 | cairo_set_line_width(cairo, 1); |
302 | cairo_move_to(cairo, x_pos + sep_block_width / 2, margin); | 338 | cairo_move_to(cairo, x_pos + sep_block_width / 2, margin); |
303 | cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin); | 339 | cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin); |
@@ -317,18 +353,18 @@ static void predict_status_block_pos(cairo_t *cairo, | |||
317 | struct swaybar_config *config = output->bar->config; | 353 | struct swaybar_config *config = output->bar->config; |
318 | 354 | ||
319 | int text_width, text_height; | 355 | int text_width, text_height; |
320 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 356 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, |
321 | output->scale, block->markup, "%s", block->full_text); | 357 | block->markup, "%s", block->full_text); |
322 | 358 | ||
323 | int margin = 3 * output->scale; | 359 | int margin = 3; |
324 | double ws_vertical_padding = config->status_padding * output->scale; | 360 | double ws_vertical_padding = config->status_padding; |
325 | 361 | ||
326 | int width = text_width; | 362 | int width = text_width; |
327 | 363 | ||
328 | if (block->min_width_str) { | 364 | if (block->min_width_str) { |
329 | int w; | 365 | int w; |
330 | get_text_size(cairo, config->font, &w, NULL, NULL, | 366 | get_text_size(cairo, config->font_description, &w, NULL, NULL, |
331 | output->scale, block->markup, "%s", block->min_width_str); | 367 | 1, block->markup, "%s", block->min_width_str); |
332 | block->min_width = w; | 368 | block->min_width = w; |
333 | } | 369 | } |
334 | if (width < block->min_width) { | 370 | if (width < block->min_width) { |
@@ -336,28 +372,28 @@ static void predict_status_block_pos(cairo_t *cairo, | |||
336 | } | 372 | } |
337 | 373 | ||
338 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | 374 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; |
339 | uint32_t ideal_surface_height = ideal_height / output->scale; | 375 | uint32_t ideal_surface_height = ideal_height; |
340 | if (!output->bar->config->height && | 376 | if (!output->bar->config->height && |
341 | output->height < ideal_surface_height) { | 377 | output->height < ideal_surface_height) { |
342 | return; | 378 | return; |
343 | } | 379 | } |
344 | 380 | ||
345 | *x -= width; | 381 | *x -= width; |
346 | if ((block->border || block->urgent) && block->border_left > 0) { | 382 | if ((block->border_set || block->urgent) && block->border_left > 0) { |
347 | *x -= (block->border_left * output->scale + margin); | 383 | *x -= (block->border_left + margin); |
348 | } | 384 | } |
349 | if ((block->border || block->urgent) && block->border_right > 0) { | 385 | if ((block->border_set || block->urgent) && block->border_right > 0) { |
350 | *x -= (block->border_right * output->scale + margin); | 386 | *x -= (block->border_right + margin); |
351 | } | 387 | } |
352 | 388 | ||
353 | int sep_width, sep_height; | 389 | int sep_width, sep_height; |
354 | int sep_block_width = block->separator_block_width; | 390 | int sep_block_width = block->separator_block_width; |
355 | if (!edge) { | 391 | if (!edge) { |
356 | if (config->sep_symbol) { | 392 | if (config->sep_symbol) { |
357 | get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, | 393 | get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL, |
358 | output->scale, false, "%s", config->sep_symbol); | 394 | 1, false, "%s", config->sep_symbol); |
359 | uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; | 395 | uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; |
360 | uint32_t _ideal_surface_height = _ideal_height / output->scale; | 396 | uint32_t _ideal_surface_height = _ideal_height; |
361 | if (!output->bar->config->height && | 397 | if (!output->bar->config->height && |
362 | output->height < _ideal_surface_height) { | 398 | output->height < _ideal_surface_height) { |
363 | return; | 399 | return; |
@@ -368,13 +404,13 @@ static void predict_status_block_pos(cairo_t *cairo, | |||
368 | } | 404 | } |
369 | *x -= sep_block_width; | 405 | *x -= sep_block_width; |
370 | } else if (config->status_edge_padding) { | 406 | } else if (config->status_edge_padding) { |
371 | *x -= config->status_edge_padding * output->scale; | 407 | *x -= config->status_edge_padding; |
372 | } | 408 | } |
373 | } | 409 | } |
374 | 410 | ||
375 | static double predict_status_line_pos(cairo_t *cairo, | 411 | static double predict_status_line_pos(cairo_t *cairo, |
376 | struct swaybar_output *output, double x) { | 412 | struct swaybar_output *output, double x) { |
377 | bool edge = x == output->width * output->scale; | 413 | bool edge = x == output->width; |
378 | struct i3bar_block *block; | 414 | struct i3bar_block *block; |
379 | wl_list_for_each(block, &output->bar->status->blocks, link) { | 415 | wl_list_for_each(block, &output->bar->status->blocks, link) { |
380 | predict_status_block_pos(cairo, output, block, &x, edge); | 416 | predict_status_block_pos(cairo, output, block, &x, edge); |
@@ -389,24 +425,24 @@ static uint32_t predict_workspace_button_length(cairo_t *cairo, | |||
389 | struct swaybar_config *config = output->bar->config; | 425 | struct swaybar_config *config = output->bar->config; |
390 | 426 | ||
391 | int text_width, text_height; | 427 | int text_width, text_height; |
392 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 428 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, |
393 | output->scale, config->pango_markup, "%s", ws->label); | 429 | config->pango_markup, "%s", ws->label); |
394 | 430 | ||
395 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | 431 | int ws_vertical_padding = WS_VERTICAL_PADDING; |
396 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; | 432 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING; |
397 | int border_width = BORDER_WIDTH * output->scale; | 433 | int border_width = BORDER_WIDTH; |
398 | 434 | ||
399 | uint32_t ideal_height = ws_vertical_padding * 2 + text_height | 435 | uint32_t ideal_height = ws_vertical_padding * 2 + text_height |
400 | + border_width * 2; | 436 | + border_width * 2; |
401 | uint32_t ideal_surface_height = ideal_height / output->scale; | 437 | uint32_t ideal_surface_height = ideal_height; |
402 | if (!output->bar->config->height && | 438 | if (!output->bar->config->height && |
403 | output->height < ideal_surface_height) { | 439 | output->height < ideal_surface_height) { |
404 | return 0; | 440 | return 0; |
405 | } | 441 | } |
406 | 442 | ||
407 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; | 443 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; |
408 | if (width < config->workspace_min_width * output->scale) { | 444 | if (width < config->workspace_min_width) { |
409 | width = config->workspace_min_width * output->scale; | 445 | width = config->workspace_min_width; |
410 | } | 446 | } |
411 | return width; | 447 | return width; |
412 | } | 448 | } |
@@ -437,39 +473,40 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo, | |||
437 | } | 473 | } |
438 | 474 | ||
439 | int text_width, text_height; | 475 | int text_width, text_height; |
440 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 476 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, |
441 | output->scale, output->bar->mode_pango_markup, | 477 | 1, output->bar->mode_pango_markup, |
442 | "%s", mode); | 478 | "%s", mode); |
443 | 479 | ||
444 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | 480 | int ws_vertical_padding = WS_VERTICAL_PADDING; |
445 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; | 481 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING; |
446 | int border_width = BORDER_WIDTH * output->scale; | 482 | int border_width = BORDER_WIDTH; |
447 | 483 | ||
448 | uint32_t ideal_height = text_height + ws_vertical_padding * 2 | 484 | uint32_t ideal_height = text_height + ws_vertical_padding * 2 |
449 | + border_width * 2; | 485 | + border_width * 2; |
450 | uint32_t ideal_surface_height = ideal_height / output->scale; | 486 | uint32_t ideal_surface_height = ideal_height; |
451 | if (!output->bar->config->height && | 487 | if (!output->bar->config->height && |
452 | output->height < ideal_surface_height) { | 488 | output->height < ideal_surface_height) { |
453 | return 0; | 489 | return 0; |
454 | } | 490 | } |
455 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; | 491 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; |
456 | if (width < config->workspace_min_width * output->scale) { | 492 | if (width < config->workspace_min_width) { |
457 | width = config->workspace_min_width * output->scale; | 493 | width = config->workspace_min_width; |
458 | } | 494 | } |
459 | return width; | 495 | return width; |
460 | } | 496 | } |
461 | 497 | ||
462 | static uint32_t render_status_line_i3bar(cairo_t *cairo, | 498 | static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) { |
463 | struct swaybar_output *output, double *x) { | 499 | struct swaybar_output *output = ctx->output; |
464 | uint32_t max_height = 0; | 500 | uint32_t max_height = 0; |
465 | bool edge = *x == output->width * output->scale; | 501 | bool edge = *x == output->width; |
466 | struct i3bar_block *block; | 502 | struct i3bar_block *block; |
467 | bool use_short_text = false; | 503 | bool use_short_text = false; |
468 | 504 | ||
505 | cairo_t *cairo = ctx->cairo; | ||
469 | double reserved_width = | 506 | double reserved_width = |
470 | predict_workspace_buttons_length(cairo, output) + | 507 | predict_workspace_buttons_length(cairo, output) + |
471 | predict_binding_mode_indicator_length(cairo, output) + | 508 | predict_binding_mode_indicator_length(cairo, output) + |
472 | 3 * output->scale; // require a bit of space for margin | 509 | 3; // require a bit of space for margin |
473 | 510 | ||
474 | double predicted_full_pos = | 511 | double predicted_full_pos = |
475 | predict_status_line_pos(cairo, output, *x); | 512 | predict_status_line_pos(cairo, output, *x); |
@@ -479,7 +516,7 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo, | |||
479 | } | 516 | } |
480 | 517 | ||
481 | wl_list_for_each(block, &output->bar->status->blocks, link) { | 518 | wl_list_for_each(block, &output->bar->status->blocks, link) { |
482 | uint32_t h = render_status_block(cairo, output, block, x, edge, | 519 | uint32_t h = render_status_block(ctx, block, x, edge, |
483 | use_short_text); | 520 | use_short_text); |
484 | max_height = h > max_height ? h : max_height; | 521 | max_height = h > max_height ? h : max_height; |
485 | edge = false; | 522 | edge = false; |
@@ -487,53 +524,56 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo, | |||
487 | return max_height; | 524 | return max_height; |
488 | } | 525 | } |
489 | 526 | ||
490 | static uint32_t render_status_line(cairo_t *cairo, | 527 | static uint32_t render_status_line(struct render_context *ctx, double *x) { |
491 | struct swaybar_output *output, double *x) { | 528 | struct status_line *status = ctx->output->bar->status; |
492 | struct status_line *status = output->bar->status; | ||
493 | switch (status->protocol) { | 529 | switch (status->protocol) { |
494 | case PROTOCOL_ERROR: | 530 | case PROTOCOL_ERROR: |
495 | return render_status_line_error(cairo, output, x); | 531 | return render_status_line_error(ctx, x); |
496 | case PROTOCOL_TEXT: | 532 | case PROTOCOL_TEXT: |
497 | return render_status_line_text(cairo, output, x); | 533 | return render_status_line_text(ctx, x); |
498 | case PROTOCOL_I3BAR: | 534 | case PROTOCOL_I3BAR: |
499 | return render_status_line_i3bar(cairo, output, x); | 535 | return render_status_line_i3bar(ctx, x); |
500 | case PROTOCOL_UNDEF: | 536 | case PROTOCOL_UNDEF: |
501 | return 0; | 537 | return 0; |
502 | } | 538 | } |
503 | return 0; | 539 | return 0; |
504 | } | 540 | } |
505 | 541 | ||
506 | static uint32_t render_binding_mode_indicator(cairo_t *cairo, | 542 | static uint32_t render_binding_mode_indicator(struct render_context *ctx, |
507 | struct swaybar_output *output, double x) { | 543 | double x) { |
544 | struct swaybar_output *output = ctx->output; | ||
508 | const char *mode = output->bar->mode; | 545 | const char *mode = output->bar->mode; |
509 | if (!mode) { | 546 | if (!mode) { |
510 | return 0; | 547 | return 0; |
511 | } | 548 | } |
512 | 549 | ||
550 | cairo_t *cairo = ctx->cairo; | ||
513 | struct swaybar_config *config = output->bar->config; | 551 | struct swaybar_config *config = output->bar->config; |
514 | int text_width, text_height; | 552 | int text_width, text_height; |
515 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 553 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, |
516 | output->scale, output->bar->mode_pango_markup, | 554 | 1, output->bar->mode_pango_markup, |
517 | "%s", mode); | 555 | "%s", mode); |
518 | 556 | ||
519 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | 557 | int ws_vertical_padding = WS_VERTICAL_PADDING; |
520 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; | 558 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING; |
521 | int border_width = BORDER_WIDTH * output->scale; | 559 | int border_width = BORDER_WIDTH; |
522 | 560 | ||
523 | uint32_t ideal_height = text_height + ws_vertical_padding * 2 | 561 | uint32_t ideal_height = text_height + ws_vertical_padding * 2 |
524 | + border_width * 2; | 562 | + border_width * 2; |
525 | uint32_t ideal_surface_height = ideal_height / output->scale; | 563 | uint32_t ideal_surface_height = ideal_height; |
526 | if (!output->bar->config->height && | 564 | if (!output->bar->config->height && |
527 | output->height < ideal_surface_height) { | 565 | output->height < ideal_surface_height) { |
528 | return ideal_surface_height; | 566 | return ideal_surface_height; |
529 | } | 567 | } |
530 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; | 568 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; |
531 | if (width < config->workspace_min_width * output->scale) { | 569 | if (width < config->workspace_min_width) { |
532 | width = config->workspace_min_width * output->scale; | 570 | width = config->workspace_min_width; |
533 | } | 571 | } |
534 | 572 | ||
535 | uint32_t height = output->height * output->scale; | 573 | uint32_t height = output->height; |
574 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | ||
536 | cairo_set_source_u32(cairo, config->colors.binding_mode.background); | 575 | cairo_set_source_u32(cairo, config->colors.binding_mode.background); |
576 | ctx->background_color = config->colors.binding_mode.background; | ||
537 | cairo_rectangle(cairo, x, 0, width, height); | 577 | cairo_rectangle(cairo, x, 0, width, height); |
538 | cairo_fill(cairo); | 578 | cairo_fill(cairo); |
539 | 579 | ||
@@ -550,24 +590,30 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, | |||
550 | double text_y = height / 2.0 - text_height / 2.0; | 590 | double text_y = height / 2.0 - text_height / 2.0; |
551 | cairo_set_source_u32(cairo, config->colors.binding_mode.text); | 591 | cairo_set_source_u32(cairo, config->colors.binding_mode.text); |
552 | cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); | 592 | cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); |
553 | pango_printf(cairo, config->font, output->scale, | 593 | choose_text_aa_mode(ctx, config->colors.binding_mode.text); |
554 | output->bar->mode_pango_markup, "%s", mode); | 594 | render_text(cairo, config->font_description, 1, output->bar->mode_pango_markup, |
595 | "%s", mode); | ||
555 | return output->height; | 596 | return output->height; |
556 | } | 597 | } |
557 | 598 | ||
558 | static enum hotspot_event_handling workspace_hotspot_callback( | 599 | static enum hotspot_event_handling workspace_hotspot_callback( |
559 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, | 600 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, |
560 | double x, double y, uint32_t button, void *data) { | 601 | double x, double y, uint32_t button, bool released, void *data) { |
561 | if (button != BTN_LEFT) { | 602 | if (button != BTN_LEFT) { |
562 | return HOTSPOT_PROCESS; | 603 | return HOTSPOT_PROCESS; |
563 | } | 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 | } | ||
564 | ipc_send_workspace_command(output->bar, (const char *)data); | 610 | ipc_send_workspace_command(output->bar, (const char *)data); |
565 | return HOTSPOT_IGNORE; | 611 | return HOTSPOT_IGNORE; |
566 | } | 612 | } |
567 | 613 | ||
568 | static uint32_t render_workspace_button(cairo_t *cairo, | 614 | static uint32_t render_workspace_button(struct render_context *ctx, |
569 | struct swaybar_output *output, | ||
570 | struct swaybar_workspace *ws, double *x) { | 615 | struct swaybar_workspace *ws, double *x) { |
616 | struct swaybar_output *output = ctx->output; | ||
571 | struct swaybar_config *config = output->bar->config; | 617 | struct swaybar_config *config = output->bar->config; |
572 | struct box_colors box_colors; | 618 | struct box_colors box_colors; |
573 | if (ws->urgent) { | 619 | if (ws->urgent) { |
@@ -580,30 +626,33 @@ static uint32_t render_workspace_button(cairo_t *cairo, | |||
580 | box_colors = config->colors.inactive_workspace; | 626 | box_colors = config->colors.inactive_workspace; |
581 | } | 627 | } |
582 | 628 | ||
583 | uint32_t height = output->height * output->scale; | 629 | uint32_t height = output->height; |
584 | 630 | ||
631 | cairo_t *cairo = ctx->cairo; | ||
585 | int text_width, text_height; | 632 | int text_width, text_height; |
586 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 633 | get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, |
587 | output->scale, config->pango_markup, "%s", ws->label); | 634 | 1, config->pango_markup, "%s", ws->label); |
588 | 635 | ||
589 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | 636 | int ws_vertical_padding = WS_VERTICAL_PADDING; |
590 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; | 637 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING; |
591 | int border_width = BORDER_WIDTH * output->scale; | 638 | int border_width = BORDER_WIDTH; |
592 | 639 | ||
593 | uint32_t ideal_height = ws_vertical_padding * 2 + text_height | 640 | uint32_t ideal_height = ws_vertical_padding * 2 + text_height |
594 | + border_width * 2; | 641 | + border_width * 2; |
595 | uint32_t ideal_surface_height = ideal_height / output->scale; | 642 | uint32_t ideal_surface_height = ideal_height; |
596 | if (!output->bar->config->height && | 643 | if (!output->bar->config->height && |
597 | output->height < ideal_surface_height) { | 644 | output->height < ideal_surface_height) { |
598 | return ideal_surface_height; | 645 | return ideal_surface_height; |
599 | } | 646 | } |
600 | 647 | ||
601 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; | 648 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; |
602 | if (width < config->workspace_min_width * output->scale) { | 649 | if (width < config->workspace_min_width) { |
603 | width = config->workspace_min_width * output->scale; | 650 | width = config->workspace_min_width; |
604 | } | 651 | } |
605 | 652 | ||
653 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | ||
606 | cairo_set_source_u32(cairo, box_colors.background); | 654 | cairo_set_source_u32(cairo, box_colors.background); |
655 | ctx->background_color = box_colors.background; | ||
607 | cairo_rectangle(cairo, *x, 0, width, height); | 656 | cairo_rectangle(cairo, *x, 0, width, height); |
608 | cairo_fill(cairo); | 657 | cairo_fill(cairo); |
609 | 658 | ||
@@ -620,7 +669,8 @@ static uint32_t render_workspace_button(cairo_t *cairo, | |||
620 | double text_y = height / 2.0 - text_height / 2.0; | 669 | double text_y = height / 2.0 - text_height / 2.0; |
621 | cairo_set_source_u32(cairo, box_colors.text); | 670 | cairo_set_source_u32(cairo, box_colors.text); |
622 | cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); | 671 | cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); |
623 | pango_printf(cairo, config->font, output->scale, config->pango_markup, | 672 | choose_text_aa_mode(ctx, box_colors.text); |
673 | render_text(cairo, config->font_description, 1, config->pango_markup, | ||
624 | "%s", ws->label); | 674 | "%s", ws->label); |
625 | 675 | ||
626 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); | 676 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); |
@@ -637,20 +687,15 @@ static uint32_t render_workspace_button(cairo_t *cairo, | |||
637 | return output->height; | 687 | return output->height; |
638 | } | 688 | } |
639 | 689 | ||
640 | static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { | 690 | static uint32_t render_to_cairo(struct render_context *ctx) { |
691 | cairo_t *cairo = ctx->cairo; | ||
692 | struct swaybar_output *output = ctx->output; | ||
641 | struct swaybar *bar = output->bar; | 693 | struct swaybar *bar = output->bar; |
642 | struct swaybar_config *config = bar->config; | 694 | struct swaybar_config *config = bar->config; |
643 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | ||
644 | if (output->focused) { | ||
645 | cairo_set_source_u32(cairo, config->colors.focused_background); | ||
646 | } else { | ||
647 | cairo_set_source_u32(cairo, config->colors.background); | ||
648 | } | ||
649 | cairo_paint(cairo); | ||
650 | 695 | ||
651 | int th; | 696 | int th; |
652 | get_text_size(cairo, config->font, NULL, &th, NULL, output->scale, false, ""); | 697 | get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, ""); |
653 | uint32_t max_height = (th + WS_VERTICAL_PADDING * 4) / output->scale; | 698 | uint32_t max_height = (th + WS_VERTICAL_PADDING * 4); |
654 | /* | 699 | /* |
655 | * Each render_* function takes the actual height of the bar, and returns | 700 | * Each render_* function takes the actual height of the bar, and returns |
656 | * the ideal height. If the actual height is too short, the render function | 701 | * the ideal height. If the actual height is too short, the render function |
@@ -658,7 +703,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { | |||
658 | * height is too tall, the render function should adapt its drawing to | 703 | * height is too tall, the render function should adapt its drawing to |
659 | * utilize the available space. | 704 | * utilize the available space. |
660 | */ | 705 | */ |
661 | double x = output->width * output->scale; | 706 | double x = output->width; |
662 | #if HAVE_TRAY | 707 | #if HAVE_TRAY |
663 | if (bar->tray) { | 708 | if (bar->tray) { |
664 | uint32_t h = render_tray(cairo, output, &x); | 709 | uint32_t h = render_tray(cairo, output, &x); |
@@ -666,19 +711,19 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { | |||
666 | } | 711 | } |
667 | #endif | 712 | #endif |
668 | if (bar->status) { | 713 | if (bar->status) { |
669 | uint32_t h = render_status_line(cairo, output, &x); | 714 | uint32_t h = render_status_line(ctx, &x); |
670 | max_height = h > max_height ? h : max_height; | 715 | max_height = h > max_height ? h : max_height; |
671 | } | 716 | } |
672 | x = 0; | 717 | x = 0; |
673 | if (config->workspace_buttons) { | 718 | if (config->workspace_buttons) { |
674 | struct swaybar_workspace *ws; | 719 | struct swaybar_workspace *ws; |
675 | wl_list_for_each(ws, &output->workspaces, link) { | 720 | wl_list_for_each(ws, &output->workspaces, link) { |
676 | uint32_t h = render_workspace_button(cairo, output, ws, &x); | 721 | uint32_t h = render_workspace_button(ctx, ws, &x); |
677 | max_height = h > max_height ? h : max_height; | 722 | max_height = h > max_height ? h : max_height; |
678 | } | 723 | } |
679 | } | 724 | } |
680 | if (config->binding_mode_indicator) { | 725 | if (config->binding_mode_indicator) { |
681 | uint32_t h = render_binding_mode_indicator(cairo, output, x); | 726 | uint32_t h = render_binding_mode_indicator(ctx, x); |
682 | max_height = h > max_height ? h : max_height; | 727 | max_height = h > max_height ? h : max_height; |
683 | } | 728 | } |
684 | 729 | ||
@@ -708,26 +753,44 @@ void render_frame(struct swaybar_output *output) { | |||
708 | 753 | ||
709 | free_hotspots(&output->hotspots); | 754 | free_hotspots(&output->hotspots); |
710 | 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 | |||
763 | struct render_context ctx = { 0 }; | ||
764 | ctx.output = output; | ||
765 | // initial background color used for deciding the best way to antialias text | ||
766 | ctx.background_color = background_color; | ||
767 | |||
711 | cairo_surface_t *recorder = cairo_recording_surface_create( | 768 | cairo_surface_t *recorder = cairo_recording_surface_create( |
712 | CAIRO_CONTENT_COLOR_ALPHA, NULL); | 769 | CAIRO_CONTENT_COLOR_ALPHA, NULL); |
713 | cairo_t *cairo = cairo_create(recorder); | 770 | cairo_t *cairo = cairo_create(recorder); |
771 | cairo_scale(cairo, output->scale, output->scale); | ||
714 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | 772 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); |
773 | ctx.cairo = cairo; | ||
774 | |||
715 | cairo_font_options_t *fo = cairo_font_options_create(); | 775 | cairo_font_options_t *fo = cairo_font_options_create(); |
716 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | 776 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); |
777 | ctx.textaa_safe = fo; | ||
717 | if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { | 778 | if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { |
718 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); | 779 | ctx.textaa_sharp = ctx.textaa_safe; |
719 | } else { | 780 | } else { |
781 | fo = cairo_font_options_create(); | ||
720 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | 782 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); |
721 | cairo_font_options_set_subpixel_order(fo, | 783 | cairo_font_options_set_subpixel_order(fo, |
722 | to_cairo_subpixel_order(output->subpixel)); | 784 | to_cairo_subpixel_order(output->subpixel)); |
785 | ctx.textaa_sharp = fo; | ||
723 | } | 786 | } |
724 | cairo_set_font_options(cairo, fo); | 787 | |
725 | cairo_font_options_destroy(fo); | 788 | |
726 | cairo_save(cairo); | 789 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); |
727 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | 790 | cairo_set_source_u32(cairo, background_color); |
728 | cairo_paint(cairo); | 791 | cairo_paint(cairo); |
729 | cairo_restore(cairo); | 792 | |
730 | uint32_t height = render_to_cairo(cairo, output); | 793 | uint32_t height = render_to_cairo(&ctx); |
731 | int config_height = output->bar->config->height; | 794 | int config_height = output->bar->config->height; |
732 | if (config_height > 0) { | 795 | if (config_height > 0) { |
733 | height = config_height; | 796 | height = config_height; |
@@ -753,9 +816,7 @@ void render_frame(struct swaybar_output *output) { | |||
753 | output->width * output->scale, | 816 | output->width * output->scale, |
754 | output->height * output->scale); | 817 | output->height * output->scale); |
755 | if (!output->current_buffer) { | 818 | if (!output->current_buffer) { |
756 | cairo_surface_destroy(recorder); | 819 | goto cleanup; |
757 | cairo_destroy(cairo); | ||
758 | return; | ||
759 | } | 820 | } |
760 | cairo_t *shm = output->current_buffer->cairo; | 821 | cairo_t *shm = output->current_buffer->cairo; |
761 | 822 | ||
@@ -773,12 +834,29 @@ void render_frame(struct swaybar_output *output) { | |||
773 | wl_surface_damage(output->surface, 0, 0, | 834 | wl_surface_damage(output->surface, 0, 0, |
774 | output->width, output->height); | 835 | output->width, output->height); |
775 | 836 | ||
837 | uint32_t bg_alpha = background_color & 0xFF; | ||
838 | if (bg_alpha == 0xFF) { | ||
839 | struct wl_region *region = | ||
840 | wl_compositor_create_region(output->bar->compositor); | ||
841 | wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); | ||
842 | wl_surface_set_opaque_region(output->surface, region); | ||
843 | wl_region_destroy(region); | ||
844 | } else { | ||
845 | wl_surface_set_opaque_region(output->surface, NULL); | ||
846 | } | ||
847 | |||
776 | struct wl_callback *frame_callback = wl_surface_frame(output->surface); | 848 | struct wl_callback *frame_callback = wl_surface_frame(output->surface); |
777 | wl_callback_add_listener(frame_callback, &output_frame_listener, output); | 849 | wl_callback_add_listener(frame_callback, &output_frame_listener, output); |
778 | output->frame_scheduled = true; | 850 | output->frame_scheduled = true; |
779 | 851 | ||
780 | wl_surface_commit(output->surface); | 852 | wl_surface_commit(output->surface); |
781 | } | 853 | } |
854 | |||
855 | cleanup: | ||
856 | if (ctx.textaa_sharp != ctx.textaa_safe) { | ||
857 | cairo_font_options_destroy(ctx.textaa_sharp); | ||
858 | } | ||
859 | cairo_font_options_destroy(ctx.textaa_safe); | ||
782 | cairo_surface_destroy(recorder); | 860 | cairo_surface_destroy(recorder); |
783 | cairo_destroy(cairo); | 861 | cairo_destroy(cairo); |
784 | } | 862 | } |
diff --git a/swaybar/status_line.c b/swaybar/status_line.c index ecd91032..e542e606 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <fcntl.h> | 2 | #include <fcntl.h> |
4 | #include <sys/ioctl.h> | 3 | #include <sys/ioctl.h> |
@@ -117,11 +116,11 @@ bool status_handle_readable(struct status_line *status) { | |||
117 | status->text = status->buffer; | 116 | status->text = status->buffer; |
118 | // intentional fall-through | 117 | // intentional fall-through |
119 | case PROTOCOL_TEXT: | 118 | case PROTOCOL_TEXT: |
120 | errno = 0; | ||
121 | while (true) { | 119 | while (true) { |
122 | if (status->buffer[read_bytes - 1] == '\n') { | 120 | if (status->buffer[read_bytes - 1] == '\n') { |
123 | status->buffer[read_bytes - 1] = '\0'; | 121 | status->buffer[read_bytes - 1] = '\0'; |
124 | } | 122 | } |
123 | errno = 0; | ||
125 | read_bytes = getline(&status->buffer, | 124 | read_bytes = getline(&status->buffer, |
126 | &status->buffer_size, status->read); | 125 | &status->buffer_size, status->read); |
127 | if (errno == EAGAIN) { | 126 | if (errno == EAGAIN) { |
@@ -157,7 +156,12 @@ struct status_line *status_line_init(char *cmd) { | |||
157 | assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before " | 156 | assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before " |
158 | " starting `status-command`; WAYLAND_SOCKET should not be set"); | 157 | " starting `status-command`; WAYLAND_SOCKET should not be set"); |
159 | status->pid = fork(); | 158 | status->pid = fork(); |
160 | if (status->pid == 0) { | 159 | if (status->pid < 0) { |
160 | sway_log_errno(SWAY_ERROR, "fork failed"); | ||
161 | exit(1); | ||
162 | } else if (status->pid == 0) { | ||
163 | setpgid(0, 0); | ||
164 | |||
161 | dup2(pipe_read_fd[1], STDOUT_FILENO); | 165 | dup2(pipe_read_fd[1], STDOUT_FILENO); |
162 | close(pipe_read_fd[0]); | 166 | close(pipe_read_fd[0]); |
163 | close(pipe_read_fd[1]); | 167 | close(pipe_read_fd[1]); |
@@ -185,8 +189,8 @@ struct status_line *status_line_init(char *cmd) { | |||
185 | 189 | ||
186 | void status_line_free(struct status_line *status) { | 190 | void status_line_free(struct status_line *status) { |
187 | status_line_close_fds(status); | 191 | status_line_close_fds(status); |
188 | kill(status->pid, status->cont_signal); | 192 | kill(-status->pid, status->cont_signal); |
189 | kill(status->pid, SIGTERM); | 193 | kill(-status->pid, SIGTERM); |
190 | waitpid(status->pid, NULL, 0); | 194 | waitpid(status->pid, NULL, 0); |
191 | if (status->protocol == PROTOCOL_I3BAR) { | 195 | if (status->protocol == PROTOCOL_I3BAR) { |
192 | struct i3bar_block *block, *tmp; | 196 | struct i3bar_block *block, *tmp; |
diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index ddf2416d..79b54606 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -10,6 +9,7 @@ | |||
10 | #include "swaybar/tray/tray.h" | 9 | #include "swaybar/tray/tray.h" |
11 | #include "list.h" | 10 | #include "list.h" |
12 | #include "log.h" | 11 | #include "log.h" |
12 | #include "stringop.h" | ||
13 | 13 | ||
14 | static const char *watcher_path = "/StatusNotifierWatcher"; | 14 | static const char *watcher_path = "/StatusNotifierWatcher"; |
15 | 15 | ||
@@ -138,12 +138,10 @@ static int handle_new_watcher(sd_bus_message *msg, | |||
138 | 138 | ||
139 | bool init_host(struct swaybar_host *host, char *protocol, | 139 | bool init_host(struct swaybar_host *host, char *protocol, |
140 | struct swaybar_tray *tray) { | 140 | struct swaybar_tray *tray) { |
141 | size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; | 141 | host->watcher_interface = format_str("org.%s.StatusNotifierWatcher", protocol); |
142 | host->watcher_interface = malloc(len); | ||
143 | if (!host->watcher_interface) { | 142 | if (!host->watcher_interface) { |
144 | return false; | 143 | return false; |
145 | } | 144 | } |
146 | snprintf(host->watcher_interface, len, "org.%s.StatusNotifierWatcher", protocol); | ||
147 | 145 | ||
148 | sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL; | 146 | sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL; |
149 | int ret = sd_bus_match_signal(tray->bus, ®_slot, host->watcher_interface, | 147 | int ret = sd_bus_match_signal(tray->bus, ®_slot, host->watcher_interface, |
@@ -173,13 +171,10 @@ bool init_host(struct swaybar_host *host, char *protocol, | |||
173 | } | 171 | } |
174 | 172 | ||
175 | pid_t pid = getpid(); | 173 | pid_t pid = getpid(); |
176 | size_t service_len = snprintf(NULL, 0, "org.%s.StatusNotifierHost-%d", | 174 | host->service = format_str("org.%s.StatusNotifierHost-%d", protocol, pid); |
177 | protocol, pid) + 1; | ||
178 | host->service = malloc(service_len); | ||
179 | if (!host->service) { | 175 | if (!host->service) { |
180 | goto error; | 176 | goto error; |
181 | } | 177 | } |
182 | snprintf(host->service, service_len, "org.%s.StatusNotifierHost-%d", protocol, pid); | ||
183 | ret = sd_bus_request_name(tray->bus, host->service, 0); | 178 | ret = sd_bus_request_name(tray->bus, host->service, 0); |
184 | if (ret < 0) { | 179 | if (ret < 0) { |
185 | sway_log(SWAY_DEBUG, "Failed to acquire service name: %s", strerror(-ret)); | 180 | sway_log(SWAY_DEBUG, "Failed to acquire service name: %s", strerror(-ret)); |
diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index c426c3d4..659edd86 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <dirent.h> | 2 | #include <dirent.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
@@ -40,9 +39,7 @@ static list_t *get_basedirs(void) { | |||
40 | data_dirs = strdup(data_dirs); | 39 | data_dirs = strdup(data_dirs); |
41 | char *dir = strtok(data_dirs, ":"); | 40 | char *dir = strtok(data_dirs, ":"); |
42 | do { | 41 | do { |
43 | size_t path_len = snprintf(NULL, 0, "%s/icons", dir) + 1; | 42 | char *path = format_str("%s/icons", dir); |
44 | char *path = malloc(path_len); | ||
45 | snprintf(path, path_len, "%s/icons", dir); | ||
46 | list_add(basedirs, path); | 43 | list_add(basedirs, path); |
47 | } while ((dir = strtok(NULL, ":"))); | 44 | } while ((dir = strtok(NULL, ":"))); |
48 | free(data_dirs); | 45 | free(data_dirs); |
@@ -206,13 +203,7 @@ static const char *entry_handler(char *group, char *key, char *value, | |||
206 | */ | 203 | */ |
207 | static struct icon_theme *read_theme_file(char *basedir, char *theme_name) { | 204 | static struct icon_theme *read_theme_file(char *basedir, char *theme_name) { |
208 | // look for index.theme file | 205 | // look for index.theme file |
209 | size_t path_len = snprintf(NULL, 0, "%s/%s/index.theme", basedir, | 206 | char *path = format_str("%s/%s/index.theme", basedir, theme_name); |
210 | theme_name) + 1; | ||
211 | char *path = malloc(path_len); | ||
212 | if (!path) { | ||
213 | return NULL; | ||
214 | } | ||
215 | snprintf(path, path_len, "%s/%s/index.theme", basedir, theme_name); | ||
216 | FILE *theme_file = fopen(path, "r"); | 207 | FILE *theme_file = fopen(path, "r"); |
217 | free(path); | 208 | free(path); |
218 | if (!theme_file) { | 209 | if (!theme_file) { |
@@ -416,26 +407,20 @@ static char *find_icon_in_subdir(char *name, char *basedir, char *theme, | |||
416 | #endif | 407 | #endif |
417 | }; | 408 | }; |
418 | 409 | ||
419 | size_t path_len = snprintf(NULL, 0, "%s/%s/%s/%s.EXT", basedir, theme, | ||
420 | subdir, name) + 1; | ||
421 | char *path = malloc(path_len); | ||
422 | |||
423 | for (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) { | 410 | for (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) { |
424 | snprintf(path, path_len, "%s/%s/%s/%s.%s", basedir, theme, subdir, | 411 | char *path = format_str("%s/%s/%s/%s.%s", |
425 | name, extensions[i]); | 412 | basedir, theme, subdir, name, extensions[i]); |
426 | if (access(path, R_OK) == 0) { | 413 | if (access(path, R_OK) == 0) { |
427 | return path; | 414 | return path; |
428 | } | 415 | } |
416 | free(path); | ||
429 | } | 417 | } |
430 | 418 | ||
431 | free(path); | ||
432 | return NULL; | 419 | return NULL; |
433 | } | 420 | } |
434 | 421 | ||
435 | static bool theme_exists_in_basedir(char *theme, char *basedir) { | 422 | static bool theme_exists_in_basedir(char *theme, char *basedir) { |
436 | size_t path_len = snprintf(NULL, 0, "%s/%s", basedir, theme) + 1; | 423 | char *path = format_str("%s/%s", basedir, theme); |
437 | char *path = malloc(path_len); | ||
438 | snprintf(path, path_len, "%s/%s", basedir, theme); | ||
439 | bool ret = dir_exists(path); | 424 | bool ret = dir_exists(path); |
440 | free(path); | 425 | free(path); |
441 | return ret; | 426 | return ret; |
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index a5660f62..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,13 +6,13 @@ | |||
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" | 15 | #include "cairo_util.h" |
16 | #include "cairo.h" | ||
17 | #include "list.h" | 16 | #include "list.h" |
18 | #include "log.h" | 17 | #include "log.h" |
19 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 18 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
@@ -118,8 +117,13 @@ static int get_property_callback(sd_bus_message *msg, void *data, | |||
118 | 117 | ||
119 | int ret; | 118 | int ret; |
120 | if (sd_bus_message_is_method_error(msg, NULL)) { | 119 | if (sd_bus_message_is_method_error(msg, NULL)) { |
121 | sway_log(SWAY_ERROR, "%s %s: %s", sni->watcher_id, prop, | 120 | const sd_bus_error *err = sd_bus_message_get_error(msg); |
122 | sd_bus_message_get_error(msg)->message); | 121 | sway_log_importance_t log_lv = SWAY_ERROR; |
122 | if ((!strcmp(prop, "IconThemePath")) && | ||
123 | (!strcmp(err->name, SD_BUS_ERROR_UNKNOWN_PROPERTY))) { | ||
124 | log_lv = SWAY_DEBUG; | ||
125 | } | ||
126 | sway_log(log_lv, "%s %s: %s", sni->watcher_id, prop, err->message); | ||
123 | ret = sd_bus_message_get_errno(msg); | 127 | ret = sd_bus_message_get_errno(msg); |
124 | goto cleanup; | 128 | goto cleanup; |
125 | } | 129 | } |
@@ -380,13 +384,18 @@ static int cmp_sni_id(const void *item, const void *cmp_to) { | |||
380 | 384 | ||
381 | static enum hotspot_event_handling icon_hotspot_callback( | 385 | static enum hotspot_event_handling icon_hotspot_callback( |
382 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, | 386 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, |
383 | double x, double y, uint32_t button, void *data) { | 387 | double x, double y, uint32_t button, bool released, void *data) { |
384 | sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data); | 388 | sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data); |
385 | 389 | ||
386 | struct swaybar_tray *tray = output->bar->tray; | 390 | struct swaybar_tray *tray = output->bar->tray; |
387 | int idx = list_seq_find(tray->items, cmp_sni_id, data); | 391 | int idx = list_seq_find(tray->items, cmp_sni_id, data); |
388 | 392 | ||
389 | 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 | } | ||
390 | struct swaybar_sni *sni = tray->items->items[idx]; | 399 | struct swaybar_sni *sni = tray->items->items[idx]; |
391 | // guess global position since wayland doesn't expose it | 400 | // guess global position since wayland doesn't expose it |
392 | struct swaybar_config *config = tray->bar->config; | 401 | struct swaybar_config *config = tray->bar->config; |
@@ -421,7 +430,7 @@ static void reload_sni(struct swaybar_sni *sni, char *icon_theme, | |||
421 | list_free(icon_search_paths); | 430 | list_free(icon_search_paths); |
422 | if (icon_path) { | 431 | if (icon_path) { |
423 | cairo_surface_destroy(sni->icon); | 432 | cairo_surface_destroy(sni->icon); |
424 | sni->icon = load_background_image(icon_path); | 433 | sni->icon = load_image(icon_path); |
425 | free(icon_path); | 434 | free(icon_path); |
426 | return; | 435 | return; |
427 | } | 436 | } |
@@ -461,6 +470,11 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, | |||
461 | sni->target_size = target_size; | 470 | sni->target_size = target_size; |
462 | } | 471 | } |
463 | 472 | ||
473 | // Passive | ||
474 | if (sni->status && sni->status[0] == 'P') { | ||
475 | return 0; | ||
476 | } | ||
477 | |||
464 | int icon_size; | 478 | int icon_size; |
465 | cairo_surface_t *icon; | 479 | cairo_surface_t *icon; |
466 | if (sni->icon) { | 480 | if (sni->icon) { |
@@ -488,24 +502,36 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, | |||
488 | cairo_destroy(cairo_icon); | 502 | cairo_destroy(cairo_icon); |
489 | } | 503 | } |
490 | 504 | ||
491 | int padded_size = icon_size + 2*padding; | 505 | double descaled_padding = (double)padding / output->scale; |
492 | *x -= padded_size; | 506 | double descaled_icon_size = (double)icon_size / output->scale; |
493 | int y = floor((height - padded_size) / 2.0); | 507 | |
508 | int size = descaled_icon_size + 2 * descaled_padding; | ||
509 | *x -= size; | ||
510 | int icon_y = floor((output->height - size) / 2.0); | ||
494 | 511 | ||
495 | cairo_operator_t op = cairo_get_operator(cairo); | 512 | cairo_operator_t op = cairo_get_operator(cairo); |
496 | cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); | 513 | cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); |
497 | cairo_set_source_surface(cairo, icon, *x + padding, y + padding); | 514 | |
498 | cairo_rectangle(cairo, *x, y, padded_size, padded_size); | 515 | cairo_matrix_t scale_matrix; |
516 | cairo_pattern_t *icon_pattern = cairo_pattern_create_for_surface(icon); | ||
517 | // TODO: check cairo_pattern_status for "ENOMEM" | ||
518 | cairo_matrix_init_scale(&scale_matrix, output->scale, output->scale); | ||
519 | cairo_matrix_translate(&scale_matrix, -(*x + descaled_padding), -(icon_y + descaled_padding)); | ||
520 | cairo_pattern_set_matrix(icon_pattern, &scale_matrix); | ||
521 | cairo_set_source(cairo, icon_pattern); | ||
522 | cairo_rectangle(cairo, *x, icon_y, size, size); | ||
499 | cairo_fill(cairo); | 523 | cairo_fill(cairo); |
524 | |||
500 | cairo_set_operator(cairo, op); | 525 | cairo_set_operator(cairo, op); |
501 | 526 | ||
527 | cairo_pattern_destroy(icon_pattern); | ||
502 | cairo_surface_destroy(icon); | 528 | cairo_surface_destroy(icon); |
503 | 529 | ||
504 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); | 530 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); |
505 | hotspot->x = *x; | 531 | hotspot->x = *x; |
506 | hotspot->y = 0; | 532 | hotspot->y = 0; |
507 | hotspot->width = height; | 533 | hotspot->width = size; |
508 | hotspot->height = height; | 534 | hotspot->height = output->height; |
509 | hotspot->callback = icon_hotspot_callback; | 535 | hotspot->callback = icon_hotspot_callback; |
510 | hotspot->destroy = free; | 536 | hotspot->destroy = free; |
511 | hotspot->data = strdup(sni->watcher_id); | 537 | hotspot->data = strdup(sni->watcher_id); |
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index 5fe6f9c3..b0545f4a 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c | |||
@@ -116,8 +116,8 @@ uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) { | |||
116 | } | 116 | } |
117 | } // else display on all | 117 | } // else display on all |
118 | 118 | ||
119 | if ((int) output->height*output->scale <= 2*config->tray_padding) { | 119 | if ((int)(output->height * output->scale) <= 2 * config->tray_padding) { |
120 | return 2*config->tray_padding + 1; | 120 | return (2 * config->tray_padding + 1) / output->scale; |
121 | } | 121 | } |
122 | 122 | ||
123 | uint32_t max_height = 0; | 123 | uint32_t max_height = 0; |
diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 16afc27c..3cfea8d8 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <stddef.h> | 2 | #include <stddef.h> |
4 | #include <stdio.h> | 3 | #include <stdio.h> |
@@ -6,6 +5,7 @@ | |||
6 | #include <string.h> | 5 | #include <string.h> |
7 | #include "list.h" | 6 | #include "list.h" |
8 | #include "log.h" | 7 | #include "log.h" |
8 | #include "stringop.h" | ||
9 | #include "swaybar/tray/watcher.h" | 9 | #include "swaybar/tray/watcher.h" |
10 | 10 | ||
11 | static const char *obj_path = "/StatusNotifierWatcher"; | 11 | static const char *obj_path = "/StatusNotifierWatcher"; |
@@ -76,9 +76,7 @@ static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) { | |||
76 | service = service_or_path; | 76 | service = service_or_path; |
77 | path = "/StatusNotifierItem"; | 77 | path = "/StatusNotifierItem"; |
78 | } | 78 | } |
79 | size_t id_len = snprintf(NULL, 0, "%s%s", service, path) + 1; | 79 | id = format_str("%s%s", service, path); |
80 | id = malloc(id_len); | ||
81 | snprintf(id, id_len, "%s%s", service, path); | ||
82 | } | 80 | } |
83 | 81 | ||
84 | if (list_seq_find(watcher->items, cmp_id, id) == -1) { | 82 | if (list_seq_find(watcher->items, cmp_id, id) == -1) { |
@@ -107,7 +105,7 @@ static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) { | |||
107 | sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); | 105 | sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); |
108 | list_add(watcher->hosts, strdup(service)); | 106 | list_add(watcher->hosts, strdup(service)); |
109 | sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, | 107 | sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, |
110 | "StatusNotifierHostRegistered", "s", service); | 108 | "StatusNotifierHostRegistered", ""); |
111 | } else { | 109 | } else { |
112 | sway_log(SWAY_DEBUG, "Status Notifier Host '%s' already registered", service); | 110 | sway_log(SWAY_DEBUG, "Status Notifier Host '%s' already registered", service); |
113 | } | 111 | } |
@@ -159,9 +157,7 @@ struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) { | |||
159 | return NULL; | 157 | return NULL; |
160 | } | 158 | } |
161 | 159 | ||
162 | size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; | 160 | watcher->interface = format_str("org.%s.StatusNotifierWatcher", protocol); |
163 | watcher->interface = malloc(len); | ||
164 | snprintf(watcher->interface, len, "org.%s.StatusNotifierWatcher", protocol); | ||
165 | 161 | ||
166 | sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL; | 162 | sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL; |
167 | int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path, | 163 | int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path, |
diff --git a/swaymsg/main.c b/swaymsg/main.c index 60536e48..573a7b16 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | |
2 | #include <limits.h> | ||
2 | #include <stdio.h> | 3 | #include <stdio.h> |
3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
4 | #include <string.h> | 5 | #include <string.h> |
@@ -58,7 +59,7 @@ static void pretty_print_cmd(json_object *r) { | |||
58 | if (!success_object(r)) { | 59 | if (!success_object(r)) { |
59 | json_object *error; | 60 | json_object *error; |
60 | if (!json_object_object_get_ex(r, "error", &error)) { | 61 | if (!json_object_object_get_ex(r, "error", &error)) { |
61 | printf("An unknkown error occurred"); | 62 | printf("An unknown error occurred"); |
62 | } else { | 63 | } else { |
63 | printf("Error: %s\n", json_object_get_string(error)); | 64 | printf("Error: %s\n", json_object_get_string(error)); |
64 | } | 65 | } |
@@ -183,12 +184,14 @@ static void pretty_print_seat(json_object *i) { | |||
183 | } | 184 | } |
184 | 185 | ||
185 | static void pretty_print_output(json_object *o) { | 186 | static void pretty_print_output(json_object *o) { |
186 | json_object *name, *rect, *focused, *active, *ws, *current_mode; | 187 | json_object *name, *rect, *focused, *active, *power, *ws, *current_mode, *non_desktop; |
187 | json_object_object_get_ex(o, "name", &name); | 188 | json_object_object_get_ex(o, "name", &name); |
188 | json_object_object_get_ex(o, "rect", &rect); | 189 | json_object_object_get_ex(o, "rect", &rect); |
189 | json_object_object_get_ex(o, "focused", &focused); | 190 | json_object_object_get_ex(o, "focused", &focused); |
190 | json_object_object_get_ex(o, "active", &active); | 191 | json_object_object_get_ex(o, "active", &active); |
192 | json_object_object_get_ex(o, "power", &power); | ||
191 | json_object_object_get_ex(o, "current_workspace", &ws); | 193 | json_object_object_get_ex(o, "current_workspace", &ws); |
194 | json_object_object_get_ex(o, "non_desktop", &non_desktop); | ||
192 | json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, | 195 | json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, |
193 | *transform, *max_render_time, *adaptive_sync_status; | 196 | *transform, *max_render_time, *adaptive_sync_status; |
194 | json_object_object_get_ex(o, "make", &make); | 197 | json_object_object_get_ex(o, "make", &make); |
@@ -211,10 +214,19 @@ static void pretty_print_output(json_object *o) { | |||
211 | json_object_object_get_ex(current_mode, "height", &height); | 214 | json_object_object_get_ex(current_mode, "height", &height); |
212 | json_object_object_get_ex(current_mode, "refresh", &refresh); | 215 | json_object_object_get_ex(current_mode, "refresh", &refresh); |
213 | 216 | ||
214 | if (json_object_get_boolean(active)) { | 217 | if (json_object_get_boolean(non_desktop)) { |
218 | printf( | ||
219 | "Output %s '%s %s %s' (non-desktop)\n", | ||
220 | json_object_get_string(name), | ||
221 | json_object_get_string(make), | ||
222 | json_object_get_string(model), | ||
223 | json_object_get_string(serial) | ||
224 | ); | ||
225 | } else if (json_object_get_boolean(active)) { | ||
215 | printf( | 226 | printf( |
216 | "Output %s '%s %s %s'%s\n" | 227 | "Output %s '%s %s %s'%s\n" |
217 | " Current mode: %dx%d @ %.3f Hz\n" | 228 | " Current mode: %dx%d @ %.3f Hz\n" |
229 | " Power: %s\n" | ||
218 | " Position: %d,%d\n" | 230 | " Position: %d,%d\n" |
219 | " Scale factor: %f\n" | 231 | " Scale factor: %f\n" |
220 | " Scale filter: %s\n" | 232 | " Scale filter: %s\n" |
@@ -229,6 +241,7 @@ static void pretty_print_output(json_object *o) { | |||
229 | json_object_get_int(width), | 241 | json_object_get_int(width), |
230 | json_object_get_int(height), | 242 | json_object_get_int(height), |
231 | (double)json_object_get_int(refresh) / 1000, | 243 | (double)json_object_get_int(refresh) / 1000, |
244 | json_object_get_boolean(power) ? "on" : "off", | ||
232 | json_object_get_int(x), json_object_get_int(y), | 245 | json_object_get_int(x), json_object_get_int(y), |
233 | json_object_get_double(scale), | 246 | json_object_get_double(scale), |
234 | json_object_get_string(scale_filter), | 247 | json_object_get_string(scale_filter), |
@@ -245,7 +258,7 @@ static void pretty_print_output(json_object *o) { | |||
245 | json_object_get_string(adaptive_sync_status)); | 258 | json_object_get_string(adaptive_sync_status)); |
246 | } else { | 259 | } else { |
247 | printf( | 260 | printf( |
248 | "Output %s '%s %s %s' (inactive)\n", | 261 | "Output %s '%s %s %s' (disabled)\n", |
249 | json_object_get_string(name), | 262 | json_object_get_string(name), |
250 | json_object_get_string(make), | 263 | json_object_get_string(make), |
251 | json_object_get_string(model), | 264 | json_object_get_string(model), |
@@ -260,14 +273,22 @@ static void pretty_print_output(json_object *o) { | |||
260 | for (size_t i = 0; i < modes_len; ++i) { | 273 | for (size_t i = 0; i < modes_len; ++i) { |
261 | json_object *mode = json_object_array_get_idx(modes, i); | 274 | json_object *mode = json_object_array_get_idx(modes, i); |
262 | 275 | ||
263 | json_object *mode_width, *mode_height, *mode_refresh; | 276 | json_object *mode_width, *mode_height, *mode_refresh, |
277 | *mode_picture_aspect_ratio; | ||
264 | json_object_object_get_ex(mode, "width", &mode_width); | 278 | json_object_object_get_ex(mode, "width", &mode_width); |
265 | json_object_object_get_ex(mode, "height", &mode_height); | 279 | json_object_object_get_ex(mode, "height", &mode_height); |
266 | json_object_object_get_ex(mode, "refresh", &mode_refresh); | 280 | json_object_object_get_ex(mode, "refresh", &mode_refresh); |
281 | json_object_object_get_ex(mode, "picture_aspect_ratio", | ||
282 | &mode_picture_aspect_ratio); | ||
267 | 283 | ||
268 | printf(" %dx%d @ %.3f Hz\n", json_object_get_int(mode_width), | 284 | printf(" %dx%d @ %.3f Hz", json_object_get_int(mode_width), |
269 | json_object_get_int(mode_height), | 285 | json_object_get_int(mode_height), |
270 | (double)json_object_get_int(mode_refresh) / 1000); | 286 | (double)json_object_get_int(mode_refresh) / 1000); |
287 | if (mode_picture_aspect_ratio && | ||
288 | strcmp("none", json_object_get_string(mode_picture_aspect_ratio)) != 0) { | ||
289 | printf(" (%s)", json_object_get_string(mode_picture_aspect_ratio)); | ||
290 | } | ||
291 | printf("\n"); | ||
271 | } | 292 | } |
272 | } | 293 | } |
273 | 294 | ||
@@ -286,28 +307,83 @@ static void pretty_print_config(json_object *c) { | |||
286 | printf("%s\n", json_object_get_string(config)); | 307 | printf("%s\n", json_object_get_string(config)); |
287 | } | 308 | } |
288 | 309 | ||
289 | static void pretty_print(int type, json_object *resp) { | 310 | static void pretty_print_tree(json_object *obj, int indent) { |
290 | if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && | 311 | for (int i = 0; i < indent; i++) { |
291 | type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && | 312 | printf(" "); |
292 | type != IPC_GET_VERSION && type != IPC_GET_SEATS && | ||
293 | type != IPC_GET_CONFIG && type != IPC_SEND_TICK) { | ||
294 | printf("%s\n", json_object_to_json_string_ext(resp, | ||
295 | JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); | ||
296 | return; | ||
297 | } | 313 | } |
298 | 314 | ||
299 | if (type == IPC_SEND_TICK) { | 315 | int id = json_object_get_int(json_object_object_get(obj, "id")); |
300 | return; | 316 | const char *name = json_object_get_string(json_object_object_get(obj, "name")); |
317 | const char *type = json_object_get_string(json_object_object_get(obj, "type")); | ||
318 | const char *shell = json_object_get_string(json_object_object_get(obj, "shell")); | ||
319 | |||
320 | printf("#%d: %s \"%s\"", id, type, name); | ||
321 | |||
322 | if (shell != NULL) { | ||
323 | int pid = json_object_get_int(json_object_object_get(obj, "pid")); | ||
324 | const char *app_id = json_object_get_string(json_object_object_get(obj, "app_id")); | ||
325 | json_object *window_props_obj = json_object_object_get(obj, "window_properties"); | ||
326 | const char *instance = json_object_get_string(json_object_object_get(window_props_obj, "instance")); | ||
327 | const char *class = json_object_get_string(json_object_object_get(window_props_obj, "class")); | ||
328 | int x11_id = json_object_get_int(json_object_object_get(obj, "window")); | ||
329 | |||
330 | printf(" (%s, pid: %d", shell, pid); | ||
331 | if (app_id != NULL) { | ||
332 | printf(", app_id: \"%s\"", app_id); | ||
333 | } | ||
334 | if (instance != NULL) { | ||
335 | printf(", instance: \"%s\"", instance); | ||
336 | } | ||
337 | if (class != NULL) { | ||
338 | printf(", class: \"%s\"", class); | ||
339 | } | ||
340 | if (x11_id != 0) { | ||
341 | printf(", X11 window: 0x%X", x11_id); | ||
342 | } | ||
343 | printf(")"); | ||
301 | } | 344 | } |
302 | 345 | ||
303 | if (type == IPC_GET_VERSION) { | 346 | printf("\n"); |
304 | pretty_print_version(resp); | 347 | |
305 | return; | 348 | json_object *nodes_obj = json_object_object_get(obj, "nodes"); |
349 | size_t len = json_object_array_length(nodes_obj); | ||
350 | for (size_t i = 0; i < len; i++) { | ||
351 | pretty_print_tree(json_object_array_get_idx(nodes_obj, i), indent + 1); | ||
306 | } | 352 | } |
307 | 353 | ||
308 | if (type == IPC_GET_CONFIG) { | 354 | json_object *floating_nodes_obj; |
355 | json_bool floating_nodes = json_object_object_get_ex(obj, "floating_nodes", &floating_nodes_obj); | ||
356 | if (floating_nodes) { | ||
357 | size_t len = json_object_array_length(floating_nodes_obj); | ||
358 | for (size_t i = 0; i < len; i++) { | ||
359 | pretty_print_tree(json_object_array_get_idx(floating_nodes_obj, i), indent + 1); | ||
360 | } | ||
361 | } | ||
362 | } | ||
363 | |||
364 | static void pretty_print(int type, json_object *resp) { | ||
365 | switch (type) { | ||
366 | case IPC_SEND_TICK: | ||
367 | return; | ||
368 | case IPC_GET_VERSION: | ||
369 | pretty_print_version(resp); | ||
370 | return; | ||
371 | case IPC_GET_CONFIG: | ||
309 | pretty_print_config(resp); | 372 | pretty_print_config(resp); |
310 | return; | 373 | return; |
374 | case IPC_GET_TREE: | ||
375 | pretty_print_tree(resp, 0); | ||
376 | return; | ||
377 | case IPC_COMMAND: | ||
378 | case IPC_GET_WORKSPACES: | ||
379 | case IPC_GET_INPUTS: | ||
380 | case IPC_GET_OUTPUTS: | ||
381 | case IPC_GET_SEATS: | ||
382 | break; | ||
383 | default: | ||
384 | printf("%s\n", json_object_to_json_string_ext(resp, | ||
385 | JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); | ||
386 | return; | ||
311 | } | 387 | } |
312 | 388 | ||
313 | json_object *obj; | 389 | json_object *obj; |
@@ -343,7 +419,7 @@ int main(int argc, char **argv) { | |||
343 | 419 | ||
344 | sway_log_init(SWAY_INFO, NULL); | 420 | sway_log_init(SWAY_INFO, NULL); |
345 | 421 | ||
346 | static struct option long_options[] = { | 422 | static const struct option long_options[] = { |
347 | {"help", no_argument, NULL, 'h'}, | 423 | {"help", no_argument, NULL, 'h'}, |
348 | {"monitor", no_argument, NULL, 'm'}, | 424 | {"monitor", no_argument, NULL, 'm'}, |
349 | {"pretty", no_argument, NULL, 'p'}, | 425 | {"pretty", no_argument, NULL, 'p'}, |
@@ -480,12 +556,20 @@ int main(int argc, char **argv) { | |||
480 | char *resp = ipc_single_command(socketfd, type, command, &len); | 556 | char *resp = ipc_single_command(socketfd, type, command, &len); |
481 | 557 | ||
482 | // pretty print the json | 558 | // pretty print the json |
483 | json_object *obj = json_tokener_parse(resp); | 559 | json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH); |
484 | if (obj == NULL) { | 560 | if (tok == NULL) { |
561 | if (quiet) { | ||
562 | exit(EXIT_FAILURE); | ||
563 | } | ||
564 | sway_abort("failed allocating json_tokener"); | ||
565 | } | ||
566 | json_object *obj = json_tokener_parse_ex(tok, resp, -1); | ||
567 | enum json_tokener_error err = json_tokener_get_error(tok); | ||
568 | json_tokener_free(tok); | ||
569 | if (obj == NULL || err != json_tokener_success) { | ||
485 | if (!quiet) { | 570 | if (!quiet) { |
486 | fprintf(stderr, "ERROR: Could not parse json response from ipc. " | 571 | sway_log(SWAY_ERROR, "failed to parse payload as json: %s", |
487 | "This is a bug in sway."); | 572 | json_tokener_error_desc(err)); |
488 | printf("%s\n", resp); | ||
489 | } | 573 | } |
490 | ret = 1; | 574 | ret = 1; |
491 | } else { | 575 | } else { |
@@ -517,13 +601,22 @@ int main(int argc, char **argv) { | |||
517 | break; | 601 | break; |
518 | } | 602 | } |
519 | 603 | ||
520 | json_object *obj = json_tokener_parse(reply->payload); | 604 | json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH); |
521 | if (obj == NULL) { | 605 | if (tok == NULL) { |
606 | if (quiet) { | ||
607 | exit(EXIT_FAILURE); | ||
608 | } | ||
609 | sway_abort("failed allocating json_tokener"); | ||
610 | } | ||
611 | json_object *obj = json_tokener_parse_ex(tok, reply->payload, -1); | ||
612 | enum json_tokener_error err = json_tokener_get_error(tok); | ||
613 | json_tokener_free(tok); | ||
614 | if (obj == NULL || err != json_tokener_success) { | ||
522 | if (!quiet) { | 615 | if (!quiet) { |
523 | fprintf(stderr, "ERROR: Could not parse json response from" | 616 | sway_log(SWAY_ERROR, "failed to parse payload as json: %s", |
524 | " ipc. This is a bug in sway."); | 617 | json_tokener_error_desc(err)); |
525 | ret = 1; | ||
526 | } | 618 | } |
619 | ret = 1; | ||
527 | break; | 620 | break; |
528 | } else if (quiet) { | 621 | } else if (quiet) { |
529 | json_object_put(obj); | 622 | json_object_put(obj); |
diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index b69013b5..abee1bb9 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd | |||
@@ -21,12 +21,13 @@ _swaymsg_ [options...] [message] | |||
21 | 21 | ||
22 | *-p, --pretty* | 22 | *-p, --pretty* |
23 | Use pretty output even when not using a tty. | 23 | Use pretty output even when not using a tty. |
24 | Not available for all message types. | ||
24 | 25 | ||
25 | *-q, --quiet* | 26 | *-q, --quiet* |
26 | Sends the IPC message but does not print the response from sway. | 27 | Sends the IPC message but does not print the response from sway. |
27 | 28 | ||
28 | *-r, --raw* | 29 | *-r, --raw* |
29 | Use raw output even if using a tty. | 30 | Use raw JSON output even if using a tty. |
30 | 31 | ||
31 | *-s, --socket* <path> | 32 | *-s, --socket* <path> |
32 | Use the specified socket path. Otherwise, swaymsg will ask sway where the | 33 | Use the specified socket path. Otherwise, swaymsg will ask sway where the |
@@ -46,6 +47,11 @@ _swaymsg_ [options...] [message] | |||
46 | 47 | ||
47 | See *sway*(5) for a list of commands. | 48 | See *sway*(5) for a list of commands. |
48 | 49 | ||
50 | _swaymsg_ can return pretty printed (standalone-default) or JSON-formatted | ||
51 | (*--raw*) output. For detailed documentation on the returned JSON-data of | ||
52 | each message type listed below, refer to *sway-ipc*(7). The JSON-format can | ||
53 | contain more information than the pretty print. | ||
54 | |||
49 | Tips: | 55 | Tips: |
50 | - Command expansion is performed twice: once by swaymsg, and again by sway. | 56 | - Command expansion is performed twice: once by swaymsg, and again by sway. |
51 | If you have quoted multi-word strings in your command, enclose the entire | 57 | If you have quoted multi-word strings in your command, enclose the entire |
@@ -60,20 +66,20 @@ _swaymsg_ [options...] [message] | |||
60 | _swaymsg -- mark --add test_ instead of _swaymsg mark --add test_. | 66 | _swaymsg -- mark --add test_ instead of _swaymsg mark --add test_. |
61 | 67 | ||
62 | *get\_workspaces* | 68 | *get\_workspaces* |
63 | Gets a JSON-encoded list of workspaces and their status. | 69 | Gets a list of workspaces and their status. |
64 | 70 | ||
65 | *get\_inputs* | 71 | *get\_inputs* |
66 | Gets a JSON-encoded list of current inputs. | 72 | Gets a list of current inputs. |
67 | 73 | ||
68 | *get\_outputs* | 74 | *get\_outputs* |
69 | Gets a JSON-encoded list of current outputs. | 75 | Gets a list of current outputs. |
70 | 76 | ||
71 | *get\_tree* | 77 | *get\_tree* |
72 | Gets a JSON-encoded layout tree of all open windows, containers, outputs, | 78 | Gets a JSON-encoded layout tree of all open windows, containers, outputs, |
73 | workspaces, and so on. | 79 | workspaces, and so on. |
74 | 80 | ||
75 | *get\_seats* | 81 | *get\_seats* |
76 | Gets a JSON-encoded list of all seats, | 82 | Gets a list of all seats, |
77 | its properties and all assigned devices. | 83 | its properties and all assigned devices. |
78 | 84 | ||
79 | *get\_marks* | 85 | *get\_marks* |
@@ -83,7 +89,7 @@ _swaymsg_ [options...] [message] | |||
83 | Get a JSON-encoded configuration for swaybar. | 89 | Get a JSON-encoded configuration for swaybar. |
84 | 90 | ||
85 | *get\_version* | 91 | *get\_version* |
86 | Get JSON-encoded version information for the running instance of sway. | 92 | Get version information for the running instance of sway. |
87 | 93 | ||
88 | *get\_binding\_modes* | 94 | *get\_binding\_modes* |
89 | Gets a JSON-encoded list of currently configured binding modes. | 95 | Gets a JSON-encoded list of currently configured binding modes. |
@@ -92,7 +98,7 @@ _swaymsg_ [options...] [message] | |||
92 | Gets JSON-encoded info about the current binding state. | 98 | Gets JSON-encoded info about the current binding state. |
93 | 99 | ||
94 | *get\_config* | 100 | *get\_config* |
95 | Gets a JSON-encoded copy of the current configuration. | 101 | Gets a copy of the current configuration. Doesn't expand includes. |
96 | 102 | ||
97 | *send\_tick* | 103 | *send\_tick* |
98 | Sends a tick event to all subscribed clients. | 104 | Sends a tick event to all subscribed clients. |
@@ -101,6 +107,8 @@ _swaymsg_ [options...] [message] | |||
101 | Subscribe to a list of event types. The argument for this type should be | 107 | Subscribe to a list of event types. The argument for this type should be |
102 | provided in the form of a valid JSON array. If any of the types are invalid | 108 | provided in the form of a valid JSON array. If any of the types are invalid |
103 | or if a valid JSON array is not provided, this will result in a failure. | 109 | or if a valid JSON array is not provided, this will result in a failure. |
110 | For a list of valid event types and the data returned with them refer to | ||
111 | *sway-ipc*(7). | ||
104 | 112 | ||
105 | # RETURN CODES | 113 | # RETURN CODES |
106 | 114 | ||
diff --git a/swaynag/config.c b/swaynag/config.c index ca7f4eb2..efd71ce7 100644 --- a/swaynag/config.c +++ b/swaynag/config.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <getopt.h> | 1 | #include <getopt.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -11,24 +10,40 @@ | |||
11 | #include "util.h" | 10 | #include "util.h" |
12 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 11 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
13 | 12 | ||
14 | static char *read_from_stdin(void) { | 13 | static char *read_and_trim_stdin(void) { |
15 | char *buffer = NULL; | 14 | char *buffer = NULL, *line = NULL; |
16 | size_t buffer_len = 0; | 15 | size_t buffer_len = 0, line_size = 0; |
17 | char *line = NULL; | 16 | while (1) { |
18 | size_t line_size = 0; | 17 | ssize_t nread = getline(&line, &line_size, stdin); |
19 | ssize_t nread; | 18 | if (nread == -1) { |
20 | while ((nread = getline(&line, &line_size, stdin)) != -1) { | 19 | if (feof(stdin)) { |
20 | break; | ||
21 | } else { | ||
22 | perror("getline"); | ||
23 | goto freeline; | ||
24 | } | ||
25 | } | ||
21 | buffer = realloc(buffer, buffer_len + nread + 1); | 26 | buffer = realloc(buffer, buffer_len + nread + 1); |
22 | snprintf(&buffer[buffer_len], nread + 1, "%s", line); | 27 | if (!buffer) { |
28 | perror("realloc"); | ||
29 | goto freebuf; | ||
30 | } | ||
31 | memcpy(&buffer[buffer_len], line, nread + 1); | ||
23 | buffer_len += nread; | 32 | buffer_len += nread; |
24 | } | 33 | } |
25 | free(line); | 34 | free(line); |
26 | 35 | ||
27 | while (buffer && buffer[buffer_len - 1] == '\n') { | 36 | while (buffer_len && buffer[buffer_len - 1] == '\n') { |
28 | buffer[--buffer_len] = '\0'; | 37 | buffer[--buffer_len] = '\0'; |
29 | } | 38 | } |
30 | 39 | ||
31 | return buffer; | 40 | return buffer; |
41 | |||
42 | freeline: | ||
43 | free(line); | ||
44 | freebuf: | ||
45 | free(buffer); | ||
46 | return NULL; | ||
32 | } | 47 | } |
33 | 48 | ||
34 | int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | 49 | int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, |
@@ -51,7 +66,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
51 | TO_PADDING_BTN, | 66 | TO_PADDING_BTN, |
52 | }; | 67 | }; |
53 | 68 | ||
54 | static struct option opts[] = { | 69 | static const struct option opts[] = { |
55 | {"button", required_argument, NULL, 'b'}, | 70 | {"button", required_argument, NULL, 'b'}, |
56 | {"button-no-terminal", required_argument, NULL, 'B'}, | 71 | {"button-no-terminal", required_argument, NULL, 'B'}, |
57 | {"button-dismiss", required_argument, NULL, 'z'}, | 72 | {"button-dismiss", required_argument, NULL, 'z'}, |
@@ -59,6 +74,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
59 | {"config", required_argument, NULL, 'c'}, | 74 | {"config", required_argument, NULL, 'c'}, |
60 | {"debug", no_argument, NULL, 'd'}, | 75 | {"debug", no_argument, NULL, 'd'}, |
61 | {"edge", required_argument, NULL, 'e'}, | 76 | {"edge", required_argument, NULL, 'e'}, |
77 | {"layer", required_argument, NULL, 'y'}, | ||
62 | {"font", required_argument, NULL, 'f'}, | 78 | {"font", required_argument, NULL, 'f'}, |
63 | {"help", no_argument, NULL, 'h'}, | 79 | {"help", no_argument, NULL, 'h'}, |
64 | {"detailed-message", no_argument, NULL, 'l'}, | 80 | {"detailed-message", no_argument, NULL, 'l'}, |
@@ -104,6 +120,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
104 | " -c, --config <path> Path to config file.\n" | 120 | " -c, --config <path> Path to config file.\n" |
105 | " -d, --debug Enable debugging.\n" | 121 | " -d, --debug Enable debugging.\n" |
106 | " -e, --edge top|bottom Set the edge to use.\n" | 122 | " -e, --edge top|bottom Set the edge to use.\n" |
123 | " -y, --layer overlay|top|bottom|background\n" | ||
124 | " Set the layer to use.\n" | ||
107 | " -f, --font <font> Set the font to use.\n" | 125 | " -f, --font <font> Set the font to use.\n" |
108 | " -h, --help Show help message and quit.\n" | 126 | " -h, --help Show help message and quit.\n" |
109 | " -l, --detailed-message Read a detailed message from stdin.\n" | 127 | " -l, --detailed-message Read a detailed message from stdin.\n" |
@@ -133,7 +151,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
133 | 151 | ||
134 | optind = 1; | 152 | optind = 1; |
135 | while (1) { | 153 | while (1) { |
136 | int c = getopt_long(argc, argv, "b:B:z:Z:c:de:f:hlL:m:o:s:t:v", opts, NULL); | 154 | int c = getopt_long(argc, argv, "b:B:z:Z:c:de:y:f:hlL:m:o:s:t:v", opts, NULL); |
137 | if (c == -1) { | 155 | if (c == -1) { |
138 | break; | 156 | break; |
139 | } | 157 | } |
@@ -147,8 +165,11 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
147 | fprintf(stderr, "Missing action for button %s\n", optarg); | 165 | fprintf(stderr, "Missing action for button %s\n", optarg); |
148 | return EXIT_FAILURE; | 166 | return EXIT_FAILURE; |
149 | } | 167 | } |
150 | struct swaynag_button *button; | 168 | struct swaynag_button *button = calloc(1, sizeof(struct swaynag_button)); |
151 | button = calloc(sizeof(struct swaynag_button), 1); | 169 | if (!button) { |
170 | perror("calloc"); | ||
171 | return EXIT_FAILURE; | ||
172 | } | ||
152 | button->text = strdup(optarg); | 173 | button->text = strdup(optarg); |
153 | button->type = SWAYNAG_ACTION_COMMAND; | 174 | button->type = SWAYNAG_ACTION_COMMAND; |
154 | button->action = strdup(argv[optind]); | 175 | button->action = strdup(argv[optind]); |
@@ -184,24 +205,45 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
184 | } | 205 | } |
185 | } | 206 | } |
186 | break; | 207 | break; |
208 | case 'y': // Layer | ||
209 | if (type) { | ||
210 | if (strcmp(optarg, "background") == 0) { | ||
211 | type->layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; | ||
212 | } else if (strcmp(optarg, "bottom") == 0) { | ||
213 | type->layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; | ||
214 | } else if (strcmp(optarg, "top") == 0) { | ||
215 | type->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; | ||
216 | } else if (strcmp(optarg, "overlay") == 0) { | ||
217 | type->layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; | ||
218 | } else { | ||
219 | fprintf(stderr, "Invalid layer: %s\n" | ||
220 | "Usage: --layer overlay|top|bottom|background\n", | ||
221 | optarg); | ||
222 | return EXIT_FAILURE; | ||
223 | } | ||
224 | } | ||
225 | break; | ||
187 | case 'f': // Font | 226 | case 'f': // Font |
188 | if (type) { | 227 | if (type) { |
189 | free(type->font); | 228 | pango_font_description_free(type->font_description); |
190 | type->font = strdup(optarg); | 229 | type->font_description = pango_font_description_from_string(optarg); |
191 | } | 230 | } |
192 | break; | 231 | break; |
193 | case 'l': // Detailed Message | 232 | case 'l': // Detailed Message |
194 | if (swaynag) { | 233 | if (swaynag) { |
195 | free(swaynag->details.message); | 234 | free(swaynag->details.message); |
196 | swaynag->details.message = read_from_stdin(); | 235 | swaynag->details.message = read_and_trim_stdin(); |
236 | if (!swaynag->details.message) { | ||
237 | return EXIT_FAILURE; | ||
238 | } | ||
197 | swaynag->details.button_up.text = strdup("â–²"); | 239 | swaynag->details.button_up.text = strdup("â–²"); |
198 | swaynag->details.button_down.text = strdup("â–¼"); | 240 | swaynag->details.button_down.text = strdup("â–¼"); |
199 | } | 241 | } |
200 | break; | 242 | break; |
201 | case 'L': // Detailed Button Text | 243 | case 'L': // Detailed Button Text |
202 | if (swaynag) { | 244 | if (swaynag) { |
203 | free(swaynag->details.button_details->text); | 245 | free(swaynag->details.details_text); |
204 | swaynag->details.button_details->text = strdup(optarg); | 246 | swaynag->details.details_text = strdup(optarg); |
205 | } | 247 | } |
206 | break; | 248 | break; |
207 | case 'm': // Message | 249 | case 'm': // Message |
@@ -218,8 +260,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | |||
218 | break; | 260 | break; |
219 | case 's': // Dismiss Button Text | 261 | case 's': // Dismiss Button Text |
220 | if (swaynag) { | 262 | if (swaynag) { |
221 | struct swaynag_button *button_close; | 263 | struct swaynag_button *button_close = swaynag->buttons->items[0]; |
222 | button_close = swaynag->buttons->items[0]; | ||
223 | free(button_close->text); | 264 | free(button_close->text); |
224 | button_close->text = strdup(optarg); | 265 | button_close->text = strdup(optarg); |
225 | } | 266 | } |
@@ -378,23 +419,24 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) { | |||
378 | 419 | ||
379 | if (line[0] == '[') { | 420 | if (line[0] == '[') { |
380 | char *close = strchr(line, ']'); | 421 | char *close = strchr(line, ']'); |
381 | if (!close) { | 422 | if (!close || close != &line[nread - 2] || nread <= 3) { |
382 | fprintf(stderr, "Closing bracket not found on line %d\n", | 423 | fprintf(stderr, "Line %d is malformed\n", line_number); |
383 | line_number); | ||
384 | result = 1; | 424 | result = 1; |
385 | break; | 425 | break; |
386 | } | 426 | } |
387 | char *name = calloc(1, close - line); | 427 | *close = '\0'; |
388 | strncat(name, line + 1, close - line - 1); | 428 | type = swaynag_type_get(types, &line[1]); |
389 | type = swaynag_type_get(types, name); | ||
390 | if (!type) { | 429 | if (!type) { |
391 | type = swaynag_type_new(name); | 430 | type = swaynag_type_new(&line[1]); |
392 | list_add(types, type); | 431 | list_add(types, type); |
393 | } | 432 | } |
394 | free(name); | ||
395 | } else { | 433 | } else { |
396 | char *flag = malloc(sizeof(char) * (nread + 3)); | 434 | char *flag = malloc(nread + 3); |
397 | sprintf(flag, "--%s", line); | 435 | if (!flag) { |
436 | perror("calloc"); | ||
437 | return EXIT_FAILURE; | ||
438 | } | ||
439 | snprintf(flag, nread + 3, "--%s", line); | ||
398 | char *argv[] = {"swaynag", flag}; | 440 | char *argv[] = {"swaynag", flag}; |
399 | result = swaynag_parse_options(2, argv, swaynag, types, type, | 441 | result = swaynag_parse_options(2, argv, swaynag, types, type, |
400 | NULL, NULL); | 442 | NULL, NULL); |
diff --git a/swaynag/main.c b/swaynag/main.c index 88007818..634bddbf 100644 --- a/swaynag/main.c +++ b/swaynag/main.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <signal.h> | 2 | #include <signal.h> |
4 | #include "log.h" | 3 | #include "log.h" |
@@ -20,33 +19,27 @@ void sway_terminate(int code) { | |||
20 | } | 19 | } |
21 | 20 | ||
22 | int main(int argc, char **argv) { | 21 | int main(int argc, char **argv) { |
23 | int exit_code = EXIT_SUCCESS; | 22 | int status = EXIT_SUCCESS; |
24 | 23 | ||
25 | list_t *types = create_list(); | 24 | list_t *types = create_list(); |
26 | swaynag_types_add_default(types); | 25 | swaynag_types_add_default(types); |
27 | 26 | ||
28 | memset(&swaynag, 0, sizeof(swaynag)); | ||
29 | swaynag.buttons = create_list(); | 27 | swaynag.buttons = create_list(); |
30 | wl_list_init(&swaynag.outputs); | 28 | wl_list_init(&swaynag.outputs); |
31 | wl_list_init(&swaynag.seats); | 29 | wl_list_init(&swaynag.seats); |
32 | 30 | ||
33 | struct swaynag_button *button_close = | 31 | struct swaynag_button *button_close = calloc(1, sizeof(struct swaynag_button)); |
34 | calloc(sizeof(struct swaynag_button), 1); | ||
35 | button_close->text = strdup("X"); | 32 | button_close->text = strdup("X"); |
36 | button_close->type = SWAYNAG_ACTION_DISMISS; | 33 | button_close->type = SWAYNAG_ACTION_DISMISS; |
37 | list_add(swaynag.buttons, button_close); | 34 | list_add(swaynag.buttons, button_close); |
38 | 35 | ||
39 | swaynag.details.button_details = | 36 | swaynag.details.details_text = strdup("Toggle details"); |
40 | calloc(sizeof(struct swaynag_button), 1); | ||
41 | swaynag.details.button_details->text = strdup("Toggle details"); | ||
42 | swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND; | ||
43 | 37 | ||
44 | char *config_path = NULL; | 38 | char *config_path = NULL; |
45 | bool debug = false; | 39 | bool debug = false; |
46 | int launch_status = swaynag_parse_options(argc, argv, NULL, NULL, NULL, | 40 | status = swaynag_parse_options(argc, argv, NULL, NULL, NULL, |
47 | &config_path, &debug); | 41 | &config_path, &debug); |
48 | if (launch_status != 0) { | 42 | if (status != 0) { |
49 | exit_code = launch_status; | ||
50 | goto cleanup; | 43 | goto cleanup; |
51 | } | 44 | } |
52 | sway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL); | 45 | sway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL); |
@@ -56,29 +49,27 @@ int main(int argc, char **argv) { | |||
56 | } | 49 | } |
57 | if (config_path) { | 50 | if (config_path) { |
58 | sway_log(SWAY_DEBUG, "Loading config file: %s", config_path); | 51 | sway_log(SWAY_DEBUG, "Loading config file: %s", config_path); |
59 | int config_status = swaynag_load_config(config_path, &swaynag, types); | 52 | status = swaynag_load_config(config_path, &swaynag, types); |
60 | free(config_path); | 53 | if (status != 0) { |
61 | if (config_status != 0) { | ||
62 | exit_code = config_status; | ||
63 | goto cleanup; | 54 | goto cleanup; |
64 | } | 55 | } |
65 | } | 56 | } |
66 | 57 | ||
58 | |||
67 | if (argc > 1) { | 59 | if (argc > 1) { |
68 | struct swaynag_type *type_args = swaynag_type_new("<args>"); | 60 | struct swaynag_type *type_args = swaynag_type_new("<args>"); |
69 | list_add(types, type_args); | 61 | list_add(types, type_args); |
70 | 62 | ||
71 | int result = swaynag_parse_options(argc, argv, &swaynag, types, | 63 | status = swaynag_parse_options(argc, argv, &swaynag, types, |
72 | type_args, NULL, NULL); | 64 | type_args, NULL, NULL); |
73 | if (result != 0) { | 65 | if (status != 0) { |
74 | exit_code = result; | ||
75 | goto cleanup; | 66 | goto cleanup; |
76 | } | 67 | } |
77 | } | 68 | } |
78 | 69 | ||
79 | if (!swaynag.message) { | 70 | if (!swaynag.message) { |
80 | sway_log(SWAY_ERROR, "No message passed. Please provide --message/-m"); | 71 | sway_log(SWAY_ERROR, "No message passed. Please provide --message/-m"); |
81 | exit_code = EXIT_FAILURE; | 72 | status = EXIT_FAILURE; |
82 | goto cleanup; | 73 | goto cleanup; |
83 | } | 74 | } |
84 | 75 | ||
@@ -96,20 +87,20 @@ int main(int argc, char **argv) { | |||
96 | swaynag_type_merge(type, swaynag_type_get(types, "<args>")); | 87 | swaynag_type_merge(type, swaynag_type_get(types, "<args>")); |
97 | swaynag.type = type; | 88 | swaynag.type = type; |
98 | 89 | ||
99 | swaynag_types_free(types); | ||
100 | |||
101 | if (swaynag.details.message) { | 90 | if (swaynag.details.message) { |
91 | swaynag.details.button_details = calloc(1, sizeof(struct swaynag_button)); | ||
92 | swaynag.details.button_details->text = strdup(swaynag.details.details_text); | ||
93 | swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND; | ||
102 | list_add(swaynag.buttons, swaynag.details.button_details); | 94 | list_add(swaynag.buttons, swaynag.details.button_details); |
103 | } else { | ||
104 | free(swaynag.details.button_details->text); | ||
105 | free(swaynag.details.button_details); | ||
106 | } | 95 | } |
107 | 96 | ||
108 | sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output); | 97 | sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output); |
109 | sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors); | 98 | sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors); |
110 | sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name); | 99 | sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name); |
111 | sway_log(SWAY_DEBUG, "Message: %s", swaynag.message); | 100 | sway_log(SWAY_DEBUG, "Message: %s", swaynag.message); |
112 | sway_log(SWAY_DEBUG, "Font: %s", swaynag.type->font); | 101 | char *font = pango_font_description_to_string(swaynag.type->font_description); |
102 | sway_log(SWAY_DEBUG, "Font: %s", font); | ||
103 | free(font); | ||
113 | sway_log(SWAY_DEBUG, "Buttons"); | 104 | sway_log(SWAY_DEBUG, "Buttons"); |
114 | for (int i = 0; i < swaynag.buttons->length; i++) { | 105 | for (int i = 0; i < swaynag.buttons->length; i++) { |
115 | struct swaynag_button *button = swaynag.buttons->items[i]; | 106 | struct swaynag_button *button = swaynag.buttons->items[i]; |
@@ -120,12 +111,9 @@ int main(int argc, char **argv) { | |||
120 | 111 | ||
121 | swaynag_setup(&swaynag); | 112 | swaynag_setup(&swaynag); |
122 | swaynag_run(&swaynag); | 113 | swaynag_run(&swaynag); |
123 | return exit_code; | ||
124 | 114 | ||
125 | cleanup: | 115 | cleanup: |
126 | swaynag_types_free(types); | 116 | swaynag_types_free(types); |
127 | free(swaynag.details.button_details->text); | ||
128 | free(swaynag.details.button_details); | ||
129 | swaynag_destroy(&swaynag); | 117 | swaynag_destroy(&swaynag); |
130 | return exit_code; | 118 | return status; |
131 | } | 119 | } |
diff --git a/swaynag/meson.build b/swaynag/meson.build index 71f2fc2d..aef21483 100644 --- a/swaynag/meson.build +++ b/swaynag/meson.build | |||
@@ -5,13 +5,14 @@ executable( | |||
5 | 'render.c', | 5 | 'render.c', |
6 | 'swaynag.c', | 6 | 'swaynag.c', |
7 | 'types.c', | 7 | 'types.c', |
8 | wl_protos_src, | ||
8 | ], | 9 | ], |
9 | include_directories: [sway_inc], | 10 | include_directories: [sway_inc], |
10 | dependencies: [ | 11 | dependencies: [ |
11 | cairo, | 12 | cairo, |
12 | client_protos, | ||
13 | pango, | 13 | pango, |
14 | pangocairo, | 14 | pangocairo, |
15 | rt, | ||
15 | wayland_client, | 16 | wayland_client, |
16 | wayland_cursor, | 17 | wayland_cursor, |
17 | ], | 18 | ], |
diff --git a/swaynag/render.c b/swaynag/render.c index cf2cc9e0..21b03289 100644 --- a/swaynag/render.c +++ b/swaynag/render.c | |||
@@ -1,5 +1,5 @@ | |||
1 | #include <stdint.h> | 1 | #include <stdint.h> |
2 | #include "cairo.h" | 2 | #include "cairo_util.h" |
3 | #include "log.h" | 3 | #include "log.h" |
4 | #include "pango.h" | 4 | #include "pango.h" |
5 | #include "pool-buffer.h" | 5 | #include "pool-buffer.h" |
@@ -9,20 +9,20 @@ | |||
9 | 9 | ||
10 | static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { | 10 | static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { |
11 | int text_width, text_height; | 11 | int text_width, text_height; |
12 | get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, | 12 | get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, |
13 | swaynag->scale, true, "%s", swaynag->message); | 13 | 1, true, "%s", swaynag->message); |
14 | 14 | ||
15 | int padding = swaynag->type->message_padding * swaynag->scale; | 15 | int padding = swaynag->type->message_padding; |
16 | 16 | ||
17 | uint32_t ideal_height = text_height + padding * 2; | 17 | uint32_t ideal_height = text_height + padding * 2; |
18 | uint32_t ideal_surface_height = ideal_height / swaynag->scale; | 18 | uint32_t ideal_surface_height = ideal_height; |
19 | if (swaynag->height < ideal_surface_height) { | 19 | if (swaynag->height < ideal_surface_height) { |
20 | return ideal_surface_height; | 20 | return ideal_surface_height; |
21 | } | 21 | } |
22 | 22 | ||
23 | cairo_set_source_u32(cairo, swaynag->type->text); | 23 | cairo_set_source_u32(cairo, swaynag->type->text); |
24 | cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); | 24 | cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); |
25 | pango_printf(cairo, swaynag->type->font, swaynag->scale, false, | 25 | render_text(cairo, swaynag->type->font_description, 1, false, |
26 | "%s", swaynag->message); | 26 | "%s", swaynag->message); |
27 | 27 | ||
28 | return ideal_surface_height; | 28 | return ideal_surface_height; |
@@ -31,11 +31,11 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { | |||
31 | static void render_details_scroll_button(cairo_t *cairo, | 31 | static void render_details_scroll_button(cairo_t *cairo, |
32 | struct swaynag *swaynag, struct swaynag_button *button) { | 32 | struct swaynag *swaynag, struct swaynag_button *button) { |
33 | int text_width, text_height; | 33 | int text_width, text_height; |
34 | get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, | 34 | get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, |
35 | swaynag->scale, true, "%s", button->text); | 35 | 1, true, "%s", button->text); |
36 | 36 | ||
37 | int border = swaynag->type->button_border_thickness * swaynag->scale; | 37 | int border = swaynag->type->button_border_thickness; |
38 | int padding = swaynag->type->button_padding * swaynag->scale; | 38 | int padding = swaynag->type->button_padding; |
39 | 39 | ||
40 | cairo_set_source_u32(cairo, swaynag->type->details_background); | 40 | cairo_set_source_u32(cairo, swaynag->type->details_background); |
41 | cairo_rectangle(cairo, button->x, button->y, | 41 | cairo_rectangle(cairo, button->x, button->y, |
@@ -50,41 +50,41 @@ static void render_details_scroll_button(cairo_t *cairo, | |||
50 | cairo_set_source_u32(cairo, swaynag->type->button_text); | 50 | cairo_set_source_u32(cairo, swaynag->type->button_text); |
51 | cairo_move_to(cairo, button->x + border + padding, | 51 | cairo_move_to(cairo, button->x + border + padding, |
52 | button->y + border + (button->height - text_height) / 2); | 52 | button->y + border + (button->height - text_height) / 2); |
53 | pango_printf(cairo, swaynag->type->font, swaynag->scale, true, | 53 | render_text(cairo, swaynag->type->font_description, 1, true, |
54 | "%s", button->text); | 54 | "%s", button->text); |
55 | } | 55 | } |
56 | 56 | ||
57 | static int get_detailed_scroll_button_width(cairo_t *cairo, | 57 | static int get_detailed_scroll_button_width(cairo_t *cairo, |
58 | struct swaynag *swaynag) { | 58 | struct swaynag *swaynag) { |
59 | int up_width, down_width, temp_height; | 59 | int up_width, down_width, temp_height; |
60 | get_text_size(cairo, swaynag->type->font, &up_width, &temp_height, NULL, | 60 | get_text_size(cairo, swaynag->type->font_description, &up_width, &temp_height, NULL, |
61 | swaynag->scale, true, | 61 | 1, true, |
62 | "%s", swaynag->details.button_up.text); | 62 | "%s", swaynag->details.button_up.text); |
63 | get_text_size(cairo, swaynag->type->font, &down_width, &temp_height, NULL, | 63 | get_text_size(cairo, swaynag->type->font_description, &down_width, &temp_height, NULL, |
64 | swaynag->scale, true, | 64 | 1, true, |
65 | "%s", swaynag->details.button_down.text); | 65 | "%s", swaynag->details.button_down.text); |
66 | 66 | ||
67 | int text_width = up_width > down_width ? up_width : down_width; | 67 | int text_width = up_width > down_width ? up_width : down_width; |
68 | int border = swaynag->type->button_border_thickness * swaynag->scale; | 68 | int border = swaynag->type->button_border_thickness; |
69 | int padding = swaynag->type->button_padding * swaynag->scale; | 69 | int padding = swaynag->type->button_padding; |
70 | 70 | ||
71 | return text_width + border * 2 + padding * 2; | 71 | return text_width + border * 2 + padding * 2; |
72 | } | 72 | } |
73 | 73 | ||
74 | static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, | 74 | static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, |
75 | uint32_t y) { | 75 | uint32_t y) { |
76 | uint32_t width = swaynag->width * swaynag->scale; | 76 | uint32_t width = swaynag->width; |
77 | 77 | ||
78 | int border = swaynag->type->details_border_thickness * swaynag->scale; | 78 | int border = swaynag->type->details_border_thickness; |
79 | int padding = swaynag->type->message_padding * swaynag->scale; | 79 | int padding = swaynag->type->message_padding; |
80 | int decor = padding + border; | 80 | int decor = padding + border; |
81 | 81 | ||
82 | swaynag->details.x = decor; | 82 | swaynag->details.x = decor; |
83 | swaynag->details.y = y * swaynag->scale + decor; | 83 | swaynag->details.y = y + decor; |
84 | swaynag->details.width = width - decor * 2; | 84 | swaynag->details.width = width - decor * 2; |
85 | 85 | ||
86 | PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font, | 86 | PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font_description, |
87 | swaynag->details.message, swaynag->scale, false); | 87 | swaynag->details.message, 1, false); |
88 | pango_layout_set_width(layout, | 88 | pango_layout_set_width(layout, |
89 | (swaynag->details.width - padding * 2) * PANGO_SCALE); | 89 | (swaynag->details.width - padding * 2) * PANGO_SCALE); |
90 | pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); | 90 | pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); |
@@ -164,7 +164,7 @@ static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, | |||
164 | pango_cairo_show_layout(cairo, layout); | 164 | pango_cairo_show_layout(cairo, layout); |
165 | g_object_unref(layout); | 165 | g_object_unref(layout); |
166 | 166 | ||
167 | return ideal_height / swaynag->scale; | 167 | return ideal_height; |
168 | } | 168 | } |
169 | 169 | ||
170 | static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, | 170 | static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, |
@@ -172,14 +172,14 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, | |||
172 | struct swaynag_button *button = swaynag->buttons->items[button_index]; | 172 | struct swaynag_button *button = swaynag->buttons->items[button_index]; |
173 | 173 | ||
174 | int text_width, text_height; | 174 | int text_width, text_height; |
175 | get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, | 175 | get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, |
176 | swaynag->scale, true, "%s", button->text); | 176 | 1, true, "%s", button->text); |
177 | 177 | ||
178 | int border = swaynag->type->button_border_thickness * swaynag->scale; | 178 | int border = swaynag->type->button_border_thickness; |
179 | int padding = swaynag->type->button_padding * swaynag->scale; | 179 | int padding = swaynag->type->button_padding; |
180 | 180 | ||
181 | uint32_t ideal_height = text_height + padding * 2 + border * 2; | 181 | uint32_t ideal_height = text_height + padding * 2 + border * 2; |
182 | uint32_t ideal_surface_height = ideal_height / swaynag->scale; | 182 | uint32_t ideal_surface_height = ideal_height; |
183 | if (swaynag->height < ideal_surface_height) { | 183 | if (swaynag->height < ideal_surface_height) { |
184 | return ideal_surface_height; | 184 | return ideal_surface_height; |
185 | } | 185 | } |
@@ -201,7 +201,7 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, | |||
201 | 201 | ||
202 | cairo_set_source_u32(cairo, swaynag->type->button_text); | 202 | cairo_set_source_u32(cairo, swaynag->type->button_text); |
203 | cairo_move_to(cairo, button->x + padding, button->y + padding); | 203 | cairo_move_to(cairo, button->x + padding, button->y + padding); |
204 | pango_printf(cairo, swaynag->type->font, swaynag->scale, true, | 204 | render_text(cairo, swaynag->type->font_description, 1, true, |
205 | "%s", button->text); | 205 | "%s", button->text); |
206 | 206 | ||
207 | *x = button->x - border; | 207 | *x = button->x - border; |
@@ -220,13 +220,12 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) { | |||
220 | max_height = h > max_height ? h : max_height; | 220 | max_height = h > max_height ? h : max_height; |
221 | 221 | ||
222 | int x = swaynag->width - swaynag->type->button_margin_right; | 222 | int x = swaynag->width - swaynag->type->button_margin_right; |
223 | x *= swaynag->scale; | ||
224 | for (int i = 0; i < swaynag->buttons->length; i++) { | 223 | for (int i = 0; i < swaynag->buttons->length; i++) { |
225 | h = render_button(cairo, swaynag, i, &x); | 224 | h = render_button(cairo, swaynag, i, &x); |
226 | max_height = h > max_height ? h : max_height; | 225 | max_height = h > max_height ? h : max_height; |
227 | x -= swaynag->type->button_gap * swaynag->scale; | 226 | x -= swaynag->type->button_gap; |
228 | if (i == 0) { | 227 | if (i == 0) { |
229 | x -= swaynag->type->button_gap_close * swaynag->scale; | 228 | x -= swaynag->type->button_gap_close; |
230 | } | 229 | } |
231 | } | 230 | } |
232 | 231 | ||
@@ -235,14 +234,14 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) { | |||
235 | max_height = h > max_height ? h : max_height; | 234 | max_height = h > max_height ? h : max_height; |
236 | } | 235 | } |
237 | 236 | ||
238 | int border = swaynag->type->bar_border_thickness * swaynag->scale; | 237 | int border = swaynag->type->bar_border_thickness; |
239 | if (max_height > swaynag->height) { | 238 | if (max_height > swaynag->height) { |
240 | max_height += border; | 239 | max_height += border; |
241 | } | 240 | } |
242 | cairo_set_source_u32(cairo, swaynag->type->border_bottom); | 241 | cairo_set_source_u32(cairo, swaynag->type->border_bottom); |
243 | cairo_rectangle(cairo, 0, | 242 | cairo_rectangle(cairo, 0, |
244 | swaynag->height * swaynag->scale - border, | 243 | swaynag->height - border, |
245 | swaynag->width * swaynag->scale, | 244 | swaynag->width, |
246 | border); | 245 | border); |
247 | cairo_fill(cairo); | 246 | cairo_fill(cairo); |
248 | 247 | ||
@@ -257,6 +256,7 @@ void render_frame(struct swaynag *swaynag) { | |||
257 | cairo_surface_t *recorder = cairo_recording_surface_create( | 256 | cairo_surface_t *recorder = cairo_recording_surface_create( |
258 | CAIRO_CONTENT_COLOR_ALPHA, NULL); | 257 | CAIRO_CONTENT_COLOR_ALPHA, NULL); |
259 | cairo_t *cairo = cairo_create(recorder); | 258 | cairo_t *cairo = cairo_create(recorder); |
259 | cairo_scale(cairo, swaynag->scale, swaynag->scale); | ||
260 | cairo_save(cairo); | 260 | cairo_save(cairo); |
261 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | 261 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); |
262 | cairo_paint(cairo); | 262 | cairo_paint(cairo); |
diff --git a/swaynag/swaynag.1.scd b/swaynag/swaynag.1.scd index 4a03469e..1cc85db7 100644 --- a/swaynag/swaynag.1.scd +++ b/swaynag/swaynag.1.scd | |||
@@ -48,6 +48,9 @@ _swaynag_ [options...] | |||
48 | *-e, --edge* top|bottom | 48 | *-e, --edge* top|bottom |
49 | Set the edge to use. | 49 | Set the edge to use. |
50 | 50 | ||
51 | *-y, --layer* overlay|top|bottom|background | ||
52 | Set the layer to use. | ||
53 | |||
51 | *-f, --font* <font> | 54 | *-f, --font* <font> |
52 | Set the font to use. | 55 | Set the font to use. |
53 | 56 | ||
diff --git a/swaynag/swaynag.5.scd b/swaynag/swaynag.5.scd index a078a4d7..3c367d0f 100644 --- a/swaynag/swaynag.5.scd +++ b/swaynag/swaynag.5.scd | |||
@@ -53,7 +53,7 @@ The following sizing options can also be set: | |||
53 | *message-padding=<padding>* | 53 | *message-padding=<padding>* |
54 | Set the padding for the message. | 54 | Set the padding for the message. |
55 | 55 | ||
56 | *details-gackground=<color>* | 56 | *details-background=<color>* |
57 | The background color for the details. | 57 | The background color for the details. |
58 | 58 | ||
59 | *details-border-size=<size>* | 59 | *details-border-size=<size>* |
@@ -79,6 +79,9 @@ Additionally, the following options can be assigned a default per-type: | |||
79 | *edge=top|bottom* | 79 | *edge=top|bottom* |
80 | Set the edge to use. | 80 | Set the edge to use. |
81 | 81 | ||
82 | *layer=overlay|top|bottom|background* | ||
83 | Set the layer to use. | ||
84 | |||
82 | *font=<font>* | 85 | *font=<font>* |
83 | Set the font to use. | 86 | Set the font to use. |
84 | 87 | ||
diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index d9bec368..50eea148 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <assert.h> | 2 | #include <assert.h> |
4 | #include <sys/stat.h> | 3 | #include <sys/stat.h> |
@@ -28,10 +27,15 @@ static bool terminal_execute(char *terminal, char *command) { | |||
28 | fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command); | 27 | fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command); |
29 | fclose(tmp); | 28 | fclose(tmp); |
30 | chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR); | 29 | chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR); |
31 | char *cmd = malloc(sizeof(char) * (strlen(terminal) + strlen(" -e ") + strlen(fname) + 1)); | 30 | size_t cmd_size = strlen(terminal) + strlen(" -e ") + strlen(fname) + 1; |
32 | sprintf(cmd, "%s -e %s", terminal, fname); | 31 | char *cmd = malloc(cmd_size); |
33 | execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); | 32 | if (!cmd) { |
34 | sway_log_errno(SWAY_ERROR, "Failed to run command, execl() returned."); | 33 | perror("malloc"); |
34 | return false; | ||
35 | } | ||
36 | snprintf(cmd, cmd_size, "%s -e %s", terminal, fname); | ||
37 | execlp("sh", "sh", "-c", cmd, NULL); | ||
38 | sway_log_errno(SWAY_ERROR, "Failed to run command, execlp() returned."); | ||
35 | free(cmd); | 39 | free(cmd); |
36 | return false; | 40 | return false; |
37 | } | 41 | } |
@@ -58,7 +62,7 @@ static void swaynag_button_execute(struct swaynag *swaynag, | |||
58 | } else if (pid == 0) { | 62 | } else if (pid == 0) { |
59 | // Child of the child. Will be reparented to the init process | 63 | // Child of the child. Will be reparented to the init process |
60 | char *terminal = getenv("TERMINAL"); | 64 | char *terminal = getenv("TERMINAL"); |
61 | if (button->terminal && terminal && strlen(terminal)) { | 65 | if (button->terminal && terminal && *terminal) { |
62 | sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal); | 66 | sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal); |
63 | if (!terminal_execute(terminal, button->action)) { | 67 | if (!terminal_execute(terminal, button->action)) { |
64 | swaynag_destroy(swaynag); | 68 | swaynag_destroy(swaynag); |
@@ -69,8 +73,8 @@ static void swaynag_button_execute(struct swaynag *swaynag, | |||
69 | sway_log(SWAY_DEBUG, | 73 | sway_log(SWAY_DEBUG, |
70 | "$TERMINAL not found. Running directly"); | 74 | "$TERMINAL not found. Running directly"); |
71 | } | 75 | } |
72 | execl("/bin/sh", "/bin/sh", "-c", button->action, NULL); | 76 | execlp("sh", "sh", "-c", button->action, NULL); |
73 | sway_log_errno(SWAY_DEBUG, "execl failed"); | 77 | sway_log_errno(SWAY_DEBUG, "execlp failed"); |
74 | _exit(EXIT_FAILURE); | 78 | _exit(EXIT_FAILURE); |
75 | } | 79 | } |
76 | } | 80 | } |
@@ -103,7 +107,7 @@ static void layer_surface_closed(void *data, | |||
103 | swaynag_destroy(swaynag); | 107 | swaynag_destroy(swaynag); |
104 | } | 108 | } |
105 | 109 | ||
106 | static struct zwlr_layer_surface_v1_listener layer_surface_listener = { | 110 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { |
107 | .configure = layer_surface_configure, | 111 | .configure = layer_surface_configure, |
108 | .closed = layer_surface_closed, | 112 | .closed = layer_surface_closed, |
109 | }; | 113 | }; |
@@ -124,7 +128,7 @@ static void surface_enter(void *data, struct wl_surface *surface, | |||
124 | }; | 128 | }; |
125 | } | 129 | } |
126 | 130 | ||
127 | static struct wl_surface_listener surface_listener = { | 131 | static const struct wl_surface_listener surface_listener = { |
128 | .enter = surface_enter, | 132 | .enter = surface_enter, |
129 | .leave = nop, | 133 | .leave = nop, |
130 | }; | 134 | }; |
@@ -138,7 +142,7 @@ static void update_cursor(struct swaynag_seat *seat) { | |||
138 | const char *cursor_theme = getenv("XCURSOR_THEME"); | 142 | const char *cursor_theme = getenv("XCURSOR_THEME"); |
139 | unsigned cursor_size = 24; | 143 | unsigned cursor_size = 24; |
140 | const char *env_cursor_size = getenv("XCURSOR_SIZE"); | 144 | const char *env_cursor_size = getenv("XCURSOR_SIZE"); |
141 | if (env_cursor_size && strlen(env_cursor_size) > 0) { | 145 | if (env_cursor_size && *env_cursor_size) { |
142 | errno = 0; | 146 | errno = 0; |
143 | char *end; | 147 | char *end; |
144 | unsigned size = strtoul(env_cursor_size, &end, 10); | 148 | unsigned size = strtoul(env_cursor_size, &end, 10); |
@@ -148,8 +152,15 @@ static void update_cursor(struct swaynag_seat *seat) { | |||
148 | } | 152 | } |
149 | pointer->cursor_theme = wl_cursor_theme_load( | 153 | pointer->cursor_theme = wl_cursor_theme_load( |
150 | cursor_theme, cursor_size * swaynag->scale, swaynag->shm); | 154 | cursor_theme, cursor_size * swaynag->scale, swaynag->shm); |
151 | struct wl_cursor *cursor = | 155 | if (!pointer->cursor_theme) { |
152 | wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); | 156 | sway_log(SWAY_ERROR, "Failed to load cursor theme"); |
157 | return; | ||
158 | } | ||
159 | struct wl_cursor *cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default"); | ||
160 | if (!cursor) { | ||
161 | sway_log(SWAY_ERROR, "Failed to get default cursor from theme"); | ||
162 | return; | ||
163 | } | ||
153 | pointer->cursor_image = cursor->images[0]; | 164 | pointer->cursor_image = cursor->images[0]; |
154 | wl_surface_set_buffer_scale(pointer->cursor_surface, | 165 | wl_surface_set_buffer_scale(pointer->cursor_surface, |
155 | swaynag->scale); | 166 | swaynag->scale); |
@@ -177,9 +188,22 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | |||
177 | uint32_t serial, struct wl_surface *surface, | 188 | uint32_t serial, struct wl_surface *surface, |
178 | wl_fixed_t surface_x, wl_fixed_t surface_y) { | 189 | wl_fixed_t surface_x, wl_fixed_t surface_y) { |
179 | struct swaynag_seat *seat = data; | 190 | struct swaynag_seat *seat = data; |
191 | |||
180 | struct swaynag_pointer *pointer = &seat->pointer; | 192 | struct swaynag_pointer *pointer = &seat->pointer; |
181 | pointer->serial = serial; | 193 | pointer->x = wl_fixed_to_int(surface_x); |
182 | update_cursor(seat); | 194 | pointer->y = wl_fixed_to_int(surface_y); |
195 | |||
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 | } | ||
183 | } | 207 | } |
184 | 208 | ||
185 | 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, |
@@ -198,8 +222,8 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, | |||
198 | return; | 222 | return; |
199 | } | 223 | } |
200 | 224 | ||
201 | double x = seat->pointer.x * swaynag->scale; | 225 | double x = seat->pointer.x; |
202 | double y = seat->pointer.y * swaynag->scale; | 226 | double y = seat->pointer.y; |
203 | for (int i = 0; i < swaynag->buttons->length; i++) { | 227 | for (int i = 0; i < swaynag->buttons->length; i++) { |
204 | struct swaynag_button *nagbutton = swaynag->buttons->items[i]; | 228 | struct swaynag_button *nagbutton = swaynag->buttons->items[i]; |
205 | if (x >= nagbutton->x | 229 | if (x >= nagbutton->x |
@@ -263,7 +287,7 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, | |||
263 | render_frame(swaynag); | 287 | render_frame(swaynag); |
264 | } | 288 | } |
265 | 289 | ||
266 | static struct wl_pointer_listener pointer_listener = { | 290 | static const struct wl_pointer_listener pointer_listener = { |
267 | .enter = wl_pointer_enter, | 291 | .enter = wl_pointer_enter, |
268 | .leave = nop, | 292 | .leave = nop, |
269 | .motion = wl_pointer_motion, | 293 | .motion = wl_pointer_motion, |
@@ -289,7 +313,7 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, | |||
289 | } | 313 | } |
290 | } | 314 | } |
291 | 315 | ||
292 | const struct wl_seat_listener seat_listener = { | 316 | static const struct wl_seat_listener seat_listener = { |
293 | .capabilities = seat_handle_capabilities, | 317 | .capabilities = seat_handle_capabilities, |
294 | .name = nop, | 318 | .name = nop, |
295 | }; | 319 | }; |
@@ -305,33 +329,25 @@ static void output_scale(void *data, struct wl_output *output, | |||
305 | } | 329 | } |
306 | } | 330 | } |
307 | 331 | ||
308 | static struct wl_output_listener output_listener = { | 332 | static void output_name(void *data, struct wl_output *output, |
309 | .geometry = nop, | 333 | const char *name) { |
310 | .mode = nop, | ||
311 | .done = nop, | ||
312 | .scale = output_scale, | ||
313 | }; | ||
314 | |||
315 | static void xdg_output_handle_name(void *data, | ||
316 | struct zxdg_output_v1 *xdg_output, const char *name) { | ||
317 | struct swaynag_output *swaynag_output = data; | 334 | struct swaynag_output *swaynag_output = data; |
318 | char *outname = swaynag_output->swaynag->type->output; | 335 | swaynag_output->name = strdup(name); |
319 | sway_log(SWAY_DEBUG, "Checking against output %s for %s", name, outname); | 336 | |
320 | if (!swaynag_output->swaynag->output && outname && name | 337 | const char *outname = swaynag_output->swaynag->type->output; |
321 | && strcmp(outname, name) == 0) { | 338 | if (!swaynag_output->swaynag->output && outname && |
339 | strcmp(outname, name) == 0) { | ||
322 | sway_log(SWAY_DEBUG, "Using output %s", name); | 340 | sway_log(SWAY_DEBUG, "Using output %s", name); |
323 | swaynag_output->swaynag->output = swaynag_output; | 341 | swaynag_output->swaynag->output = swaynag_output; |
324 | } | 342 | } |
325 | swaynag_output->name = strdup(name); | ||
326 | zxdg_output_v1_destroy(xdg_output); | ||
327 | swaynag_output->swaynag->querying_outputs--; | ||
328 | } | 343 | } |
329 | 344 | ||
330 | static struct zxdg_output_v1_listener xdg_output_listener = { | 345 | static const struct wl_output_listener output_listener = { |
331 | .logical_position = nop, | 346 | .geometry = nop, |
332 | .logical_size = nop, | 347 | .mode = nop, |
333 | .done = nop, | 348 | .done = nop, |
334 | .name = xdg_output_handle_name, | 349 | .scale = output_scale, |
350 | .name = output_name, | ||
335 | .description = nop, | 351 | .description = nop, |
336 | }; | 352 | }; |
337 | 353 | ||
@@ -345,6 +361,7 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
345 | struct swaynag_seat *seat = | 361 | struct swaynag_seat *seat = |
346 | calloc(1, sizeof(struct swaynag_seat)); | 362 | calloc(1, sizeof(struct swaynag_seat)); |
347 | if (!seat) { | 363 | if (!seat) { |
364 | perror("calloc"); | ||
348 | return; | 365 | return; |
349 | } | 366 | } |
350 | 367 | ||
@@ -359,33 +376,28 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
359 | } else if (strcmp(interface, wl_shm_interface.name) == 0) { | 376 | } else if (strcmp(interface, wl_shm_interface.name) == 0) { |
360 | swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); | 377 | swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); |
361 | } else if (strcmp(interface, wl_output_interface.name) == 0) { | 378 | } else if (strcmp(interface, wl_output_interface.name) == 0) { |
362 | if (!swaynag->output && swaynag->xdg_output_manager) { | 379 | if (!swaynag->output) { |
363 | swaynag->querying_outputs++; | ||
364 | struct swaynag_output *output = | 380 | struct swaynag_output *output = |
365 | calloc(1, sizeof(struct swaynag_output)); | 381 | calloc(1, sizeof(struct swaynag_output)); |
382 | if (!output) { | ||
383 | perror("calloc"); | ||
384 | return; | ||
385 | } | ||
366 | output->wl_output = wl_registry_bind(registry, name, | 386 | output->wl_output = wl_registry_bind(registry, name, |
367 | &wl_output_interface, 3); | 387 | &wl_output_interface, 4); |
368 | output->wl_name = name; | 388 | output->wl_name = name; |
369 | output->scale = 1; | 389 | output->scale = 1; |
370 | output->swaynag = swaynag; | 390 | output->swaynag = swaynag; |
371 | wl_list_insert(&swaynag->outputs, &output->link); | 391 | wl_list_insert(&swaynag->outputs, &output->link); |
372 | wl_output_add_listener(output->wl_output, | 392 | wl_output_add_listener(output->wl_output, |
373 | &output_listener, output); | 393 | &output_listener, output); |
374 | |||
375 | struct zxdg_output_v1 *xdg_output; | ||
376 | xdg_output = zxdg_output_manager_v1_get_xdg_output( | ||
377 | swaynag->xdg_output_manager, output->wl_output); | ||
378 | zxdg_output_v1_add_listener(xdg_output, | ||
379 | &xdg_output_listener, output); | ||
380 | } | 394 | } |
381 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { | 395 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { |
382 | swaynag->layer_shell = wl_registry_bind( | 396 | swaynag->layer_shell = wl_registry_bind( |
383 | registry, name, &zwlr_layer_shell_v1_interface, 1); | 397 | registry, name, &zwlr_layer_shell_v1_interface, 1); |
384 | } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 | 398 | } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { |
385 | && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { | 399 | swaynag->cursor_shape_manager = wl_registry_bind( |
386 | swaynag->xdg_output_manager = wl_registry_bind(registry, name, | 400 | registry, name, &wp_cursor_shape_manager_v1_interface, 1); |
387 | &zxdg_output_manager_v1_interface, | ||
388 | ZXDG_OUTPUT_V1_NAME_SINCE_VERSION); | ||
389 | } | 401 | } |
390 | } | 402 | } |
391 | 403 | ||
@@ -451,12 +463,11 @@ void swaynag_setup(struct swaynag *swaynag) { | |||
451 | 463 | ||
452 | assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm); | 464 | assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm); |
453 | 465 | ||
454 | while (swaynag->querying_outputs > 0) { | 466 | // Second roundtrip to get wl_output properties |
455 | if (wl_display_roundtrip(swaynag->display) < 0) { | 467 | if (wl_display_roundtrip(swaynag->display) < 0) { |
456 | sway_log(SWAY_ERROR, "Error during outputs init."); | 468 | sway_log(SWAY_ERROR, "Error during outputs init."); |
457 | swaynag_destroy(swaynag); | 469 | swaynag_destroy(swaynag); |
458 | exit(EXIT_FAILURE); | 470 | exit(EXIT_FAILURE); |
459 | } | ||
460 | } | 471 | } |
461 | 472 | ||
462 | if (!swaynag->output && swaynag->type->output) { | 473 | if (!swaynag->output && swaynag->type->output) { |
@@ -465,7 +476,9 @@ void swaynag_setup(struct swaynag *swaynag) { | |||
465 | exit(EXIT_FAILURE); | 476 | exit(EXIT_FAILURE); |
466 | } | 477 | } |
467 | 478 | ||
468 | swaynag_setup_cursors(swaynag); | 479 | if (!swaynag->cursor_shape_manager) { |
480 | swaynag_setup_cursors(swaynag); | ||
481 | } | ||
469 | 482 | ||
470 | swaynag->surface = wl_compositor_create_surface(swaynag->compositor); | 483 | swaynag->surface = wl_compositor_create_surface(swaynag->compositor); |
471 | assert(swaynag->surface); | 484 | assert(swaynag->surface); |
@@ -474,7 +487,8 @@ void swaynag_setup(struct swaynag *swaynag) { | |||
474 | swaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface( | 487 | swaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface( |
475 | swaynag->layer_shell, swaynag->surface, | 488 | swaynag->layer_shell, swaynag->surface, |
476 | swaynag->output ? swaynag->output->wl_output : NULL, | 489 | swaynag->output ? swaynag->output->wl_output : NULL, |
477 | ZWLR_LAYER_SHELL_V1_LAYER_TOP, "swaynag"); | 490 | swaynag->type->layer, |
491 | "swaynag"); | ||
478 | assert(swaynag->layer_surface); | 492 | assert(swaynag->layer_surface); |
479 | zwlr_layer_surface_v1_add_listener(swaynag->layer_surface, | 493 | zwlr_layer_surface_v1_add_listener(swaynag->layer_surface, |
480 | &layer_surface_listener, swaynag); | 494 | &layer_surface_listener, swaynag); |
@@ -491,10 +505,6 @@ void swaynag_run(struct swaynag *swaynag) { | |||
491 | && wl_display_dispatch(swaynag->display) != -1) { | 505 | && wl_display_dispatch(swaynag->display) != -1) { |
492 | // This is intentionally left blank | 506 | // This is intentionally left blank |
493 | } | 507 | } |
494 | |||
495 | if (swaynag->display) { | ||
496 | wl_display_disconnect(swaynag->display); | ||
497 | } | ||
498 | } | 508 | } |
499 | 509 | ||
500 | void swaynag_destroy(struct swaynag *swaynag) { | 510 | void swaynag_destroy(struct swaynag *swaynag) { |
@@ -509,6 +519,7 @@ void swaynag_destroy(struct swaynag *swaynag) { | |||
509 | } | 519 | } |
510 | list_free(swaynag->buttons); | 520 | list_free(swaynag->buttons); |
511 | free(swaynag->details.message); | 521 | free(swaynag->details.message); |
522 | free(swaynag->details.details_text); | ||
512 | free(swaynag->details.button_up.text); | 523 | free(swaynag->details.button_up.text); |
513 | free(swaynag->details.button_down.text); | 524 | free(swaynag->details.button_down.text); |
514 | 525 | ||
@@ -529,13 +540,8 @@ void swaynag_destroy(struct swaynag *swaynag) { | |||
529 | swaynag_seat_destroy(seat); | 540 | swaynag_seat_destroy(seat); |
530 | } | 541 | } |
531 | 542 | ||
532 | if (&swaynag->buffers[0]) { | 543 | destroy_buffer(&swaynag->buffers[0]); |
533 | destroy_buffer(&swaynag->buffers[0]); | 544 | destroy_buffer(&swaynag->buffers[1]); |
534 | } | ||
535 | |||
536 | if (&swaynag->buffers[1]) { | ||
537 | destroy_buffer(&swaynag->buffers[1]); | ||
538 | } | ||
539 | 545 | ||
540 | if (swaynag->outputs.prev || swaynag->outputs.next) { | 546 | if (swaynag->outputs.prev || swaynag->outputs.next) { |
541 | struct swaynag_output *output, *temp; | 547 | struct swaynag_output *output, *temp; |
@@ -554,4 +560,8 @@ void swaynag_destroy(struct swaynag *swaynag) { | |||
554 | if (swaynag->shm) { | 560 | if (swaynag->shm) { |
555 | wl_shm_destroy(swaynag->shm); | 561 | wl_shm_destroy(swaynag->shm); |
556 | } | 562 | } |
563 | |||
564 | if (swaynag->display) { | ||
565 | wl_display_disconnect(swaynag->display); | ||
566 | } | ||
557 | } | 567 | } |
diff --git a/swaynag/types.c b/swaynag/types.c index fa045532..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> |
@@ -26,15 +25,18 @@ struct swaynag_type *swaynag_type_new(const char *name) { | |||
26 | type->button_gap_close = -1; | 25 | type->button_gap_close = -1; |
27 | type->button_margin_right = -1; | 26 | type->button_margin_right = -1; |
28 | type->button_padding = -1; | 27 | type->button_padding = -1; |
28 | type->layer = -1; | ||
29 | return type; | 29 | return type; |
30 | } | 30 | } |
31 | 31 | ||
32 | void swaynag_types_add_default(list_t *types) { | 32 | void swaynag_types_add_default(list_t *types) { |
33 | struct swaynag_type *type_defaults = swaynag_type_new("<defaults>"); | 33 | struct swaynag_type *type_defaults = swaynag_type_new("<defaults>"); |
34 | type_defaults->font = strdup("pango:Monospace 10"); | 34 | type_defaults->font_description = |
35 | pango_font_description_from_string("pango:Monospace 10"); | ||
35 | type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | 36 | type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
36 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | 37 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
37 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; | 38 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; |
39 | type_defaults->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; | ||
38 | type_defaults->button_background = 0x333333FF; | 40 | type_defaults->button_background = 0x333333FF; |
39 | type_defaults->details_background = 0x333333FF; | 41 | type_defaults->details_background = 0x333333FF; |
40 | type_defaults->background = 0x323232FF; | 42 | type_defaults->background = 0x323232FF; |
@@ -88,8 +90,8 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { | |||
88 | return; | 90 | return; |
89 | } | 91 | } |
90 | 92 | ||
91 | if (src->font) { | 93 | if (src->font_description) { |
92 | dest->font = strdup(src->font); | 94 | dest->font_description = pango_font_description_copy(src->font_description); |
93 | } | 95 | } |
94 | 96 | ||
95 | if (src->output) { | 97 | if (src->output) { |
@@ -100,6 +102,10 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { | |||
100 | dest->anchors = src->anchors; | 102 | dest->anchors = src->anchors; |
101 | } | 103 | } |
102 | 104 | ||
105 | if (src->layer >= 0) { | ||
106 | dest->layer = src->layer; | ||
107 | } | ||
108 | |||
103 | // Colors | 109 | // Colors |
104 | if (src->button_background > 0) { | 110 | if (src->button_background > 0) { |
105 | dest->button_background = src->button_background; | 111 | dest->button_background = src->button_background; |
@@ -166,7 +172,7 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { | |||
166 | 172 | ||
167 | void swaynag_type_free(struct swaynag_type *type) { | 173 | void swaynag_type_free(struct swaynag_type *type) { |
168 | free(type->name); | 174 | free(type->name); |
169 | free(type->font); | 175 | pango_font_description_free(type->font_description); |
170 | free(type->output); | 176 | free(type->output); |
171 | free(type); | 177 | free(type); |
172 | } | 178 | } |