aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.builds/alpine.yml4
-rw-r--r--.builds/archlinux.yml2
-rw-r--r--.builds/freebsd.yml3
-rw-r--r--.gitignore1
-rw-r--r--.mailmap1
-rw-r--r--README.ar.md91
-rw-r--r--README.cs.md89
-rw-r--r--README.de.md18
-rw-r--r--README.dk.md6
-rw-r--r--README.es.md6
-rw-r--r--README.fr.md6
-rw-r--r--README.ge.md61
-rw-r--r--README.gr.md6
-rw-r--r--README.hi.md7
-rw-r--r--README.hu.md7
-rw-r--r--README.ir.md6
-rw-r--r--README.it.md7
-rw-r--r--README.ja.md14
-rw-r--r--README.ko.md6
-rw-r--r--README.md18
-rw-r--r--README.nl.md6
-rw-r--r--README.no.md68
-rw-r--r--README.pl.md6
-rw-r--r--README.pt.md6
-rw-r--r--README.ro.md8
-rw-r--r--README.ru.md6
-rw-r--r--README.sv.md6
-rw-r--r--README.tr.md6
-rw-r--r--README.uk.md6
-rw-r--r--README.zh-CN.md6
-rw-r--r--README.zh-TW.md6
-rw-r--r--client/pool-buffer.c67
-rw-r--r--common/gesture.c32
-rw-r--r--common/ipc-client.c1
-rw-r--r--common/log.c1
-rw-r--r--common/loop.c1
-rw-r--r--common/meson.build2
-rw-r--r--common/pango.c20
-rw-r--r--common/stringop.c34
-rw-r--r--common/util.c1
-rw-r--r--completions/meson.build57
-rw-r--r--config.in4
-rwxr-xr-xcontrib/autoname-workspaces.py124
-rwxr-xr-xcontrib/grimshot168
-rw-r--r--contrib/grimshot.1109
-rw-r--r--contrib/grimshot.1.scd80
-rwxr-xr-xcontrib/inactive-windows-transparency.py69
-rw-r--r--include/background-image.h20
-rw-r--r--include/pango.h5
-rw-r--r--include/stringop.h9
-rw-r--r--include/sway/commands.h14
-rw-r--r--include/sway/config.h37
-rw-r--r--include/sway/criteria.h1
-rw-r--r--include/sway/desktop.h13
-rw-r--r--include/sway/desktop/idle_inhibit_v1.h12
-rw-r--r--include/sway/desktop/launcher.h40
-rw-r--r--include/sway/desktop/transaction.h14
-rw-r--r--include/sway/input/cursor.h11
-rw-r--r--include/sway/input/input-manager.h8
-rw-r--r--include/sway/input/libinput.h5
-rw-r--r--include/sway/input/seat.h85
-rw-r--r--include/sway/input/tablet.h2
-rw-r--r--include/sway/input/text_input.h6
-rw-r--r--include/sway/input/text_input_popup.h20
-rw-r--r--include/sway/ipc-json.h2
-rw-r--r--include/sway/ipc-server.h1
-rw-r--r--include/sway/layers.h51
-rw-r--r--include/sway/output.h107
-rw-r--r--include/sway/scene_descriptor.h33
-rw-r--r--include/sway/server.h83
-rw-r--r--include/sway/surface.h18
-rw-r--r--include/sway/sway_text_node.h28
-rw-r--r--include/sway/swaynag.h3
-rw-r--r--include/sway/tree/container.h102
-rw-r--r--include/sway/tree/node.h13
-rw-r--r--include/sway/tree/root.h50
-rw-r--r--include/sway/tree/view.h120
-rw-r--r--include/sway/tree/workspace.h8
-rw-r--r--include/sway/xdg_decoration.h2
-rw-r--r--include/swaybar/bar.h2
-rw-r--r--include/swaybar/i3bar.h2
-rw-r--r--include/swaybar/image.h7
-rw-r--r--include/swaybar/input.h2
-rw-r--r--include/swaybar/tray/item.h1
-rw-r--r--include/swaynag/swaynag.h6
-rw-r--r--include/swaynag/types.h5
-rw-r--r--meson.build113
-rw-r--r--meson_options.txt2
-rw-r--r--protocols/meson.build76
-rw-r--r--protocols/wlr-input-inhibitor-unstable-v1.xml67
-rwxr-xr-xrelease.sh31
-rw-r--r--sway/commands.c27
-rw-r--r--sway/commands/assign.c3
-rw-r--r--sway/commands/bar.c5
-rw-r--r--sway/commands/bar/bind.c2
-rw-r--r--sway/commands/bar/font.c1
-rw-r--r--sway/commands/bar/hidden_state.c1
-rw-r--r--sway/commands/bar/icon_theme.c1
-rw-r--r--sway/commands/bar/id.c1
-rw-r--r--sway/commands/bar/mode.c1
-rw-r--r--sway/commands/bar/output.c1
-rw-r--r--sway/commands/bar/position.c1
-rw-r--r--sway/commands/bar/separator_symbol.c1
-rw-r--r--sway/commands/bar/tray_bind.c2
-rw-r--r--sway/commands/bar/tray_output.c1
-rw-r--r--sway/commands/bind.c14
-rw-r--r--sway/commands/client.c12
-rw-r--r--sway/commands/exec_always.c28
-rw-r--r--sway/commands/floating_minmax_size.c6
-rw-r--r--sway/commands/font.c1
-rw-r--r--sway/commands/for_window.c2
-rw-r--r--sway/commands/gesture.c1
-rw-r--r--sway/commands/hide_edge_borders.c4
-rw-r--r--sway/commands/inhibit_idle.c2
-rw-r--r--sway/commands/input.c3
-rw-r--r--sway/commands/input/calibration_matrix.c1
-rw-r--r--sway/commands/input/dwtp.c25
-rw-r--r--sway/commands/input/events.c10
-rw-r--r--sway/commands/input/map_from_region.c19
-rw-r--r--sway/commands/input/map_to_output.c1
-rw-r--r--sway/commands/input/map_to_region.c3
-rw-r--r--sway/commands/input/rotation_angle.c29
-rw-r--r--sway/commands/input/scroll_button.c2
-rw-r--r--sway/commands/input/scroll_button_lock.c26
-rw-r--r--sway/commands/input/xkb_file.c1
-rw-r--r--sway/commands/input/xkb_layout.c1
-rw-r--r--sway/commands/input/xkb_model.c1
-rw-r--r--sway/commands/input/xkb_numlock.c1
-rw-r--r--sway/commands/input/xkb_options.c1
-rw-r--r--sway/commands/input/xkb_rules.c1
-rw-r--r--sway/commands/input/xkb_switch_layout.c1
-rw-r--r--sway/commands/input/xkb_variant.c1
-rw-r--r--sway/commands/layout.c2
-rw-r--r--sway/commands/mark.c3
-rw-r--r--sway/commands/mode.c1
-rw-r--r--sway/commands/move.c49
-rw-r--r--sway/commands/no_focus.c2
-rw-r--r--sway/commands/opacity.c3
-rw-r--r--sway/commands/output.c10
-rw-r--r--sway/commands/output/background.c4
-rw-r--r--sway/commands/output/toggle.c2
-rw-r--r--sway/commands/output/transform.c1
-rw-r--r--sway/commands/output/unplug.c54
-rw-r--r--sway/commands/primary_selection.c25
-rw-r--r--sway/commands/reload.c8
-rw-r--r--sway/commands/rename.c7
-rw-r--r--sway/commands/resize.c50
-rw-r--r--sway/commands/seat/attach.c1
-rw-r--r--sway/commands/seat/cursor.c25
-rw-r--r--sway/commands/seat/hide_cursor.c1
-rw-r--r--sway/commands/seat/idle.c7
-rw-r--r--sway/commands/seat/xcursor_theme.c1
-rw-r--r--sway/commands/set.c1
-rw-r--r--sway/commands/show_marks.c12
-rw-r--r--sway/commands/split.c2
-rw-r--r--sway/commands/swap.c180
-rw-r--r--sway/commands/title_align.c9
-rw-r--r--sway/commands/title_format.c1
-rw-r--r--sway/commands/titlebar_border_thickness.c1
-rw-r--r--sway/commands/titlebar_padding.c1
-rw-r--r--sway/commands/unmark.c12
-rw-r--r--sway/commands/workspace.c13
-rw-r--r--sway/commands/xwayland.c2
-rw-r--r--sway/config.c121
-rw-r--r--sway/config/bar.c2
-rw-r--r--sway/config/input.c13
-rw-r--r--sway/config/output.c552
-rw-r--r--sway/config/seat.c2
-rw-r--r--sway/criteria.c33
-rw-r--r--sway/desktop/desktop.c40
-rw-r--r--sway/desktop/idle_inhibit_v1.c46
-rw-r--r--sway/desktop/launcher.c267
-rw-r--r--sway/desktop/layer_shell.c773
-rw-r--r--sway/desktop/output.c979
-rw-r--r--sway/desktop/render.c1204
-rw-r--r--sway/desktop/surface.c46
-rw-r--r--sway/desktop/transaction.c500
-rw-r--r--sway/desktop/xdg_shell.c252
-rw-r--r--sway/desktop/xwayland.c268
-rw-r--r--sway/input/cursor.c426
-rw-r--r--sway/input/input-manager.c149
-rw-r--r--sway/input/keyboard.c72
-rw-r--r--sway/input/libinput.c94
-rw-r--r--sway/input/seat.c347
-rw-r--r--sway/input/seatop_default.c236
-rw-r--r--sway/input/seatop_down.c139
-rw-r--r--sway/input/seatop_move_floating.c6
-rw-r--r--sway/input/seatop_move_tiling.c75
-rw-r--r--sway/input/seatop_resize_floating.c3
-rw-r--r--sway/input/seatop_resize_tiling.c3
-rw-r--r--sway/input/switch.c10
-rw-r--r--sway/input/tablet.c27
-rw-r--r--sway/input/text_input.c227
-rw-r--r--sway/ipc-json.c178
-rw-r--r--sway/ipc-server.c25
-rw-r--r--sway/lock.c352
-rw-r--r--sway/main.c58
-rw-r--r--sway/meson.build22
-rw-r--r--sway/scene_descriptor.c66
-rw-r--r--sway/server.c241
-rw-r--r--sway/sway-input.5.scd40
-rw-r--r--sway/sway-ipc.7.scd43
-rw-r--r--sway/sway-output.5.scd14
-rw-r--r--sway/sway.5.scd38
-rw-r--r--sway/sway_text_node.c302
-rw-r--r--sway/swaynag.c15
-rw-r--r--sway/tree/arrange.c15
-rw-r--r--sway/tree/container.c1306
-rw-r--r--sway/tree/node.c38
-rw-r--r--sway/tree/output.c98
-rw-r--r--sway/tree/root.c271
-rw-r--r--sway/tree/view.c570
-rw-r--r--sway/tree/workspace.c52
-rw-r--r--sway/xdg_activation_v1.c57
-rw-r--r--sway/xdg_decoration.c63
-rw-r--r--swaybar/bar.c22
-rw-r--r--swaybar/config.c1
-rw-r--r--swaybar/i3bar.c8
-rw-r--r--swaybar/image.c (renamed from common/background-image.c)92
-rw-r--r--swaybar/input.c52
-rw-r--r--swaybar/ipc.c10
-rw-r--r--swaybar/main.c1
-rw-r--r--swaybar/meson.build5
-rw-r--r--swaybar/render.c45
-rw-r--r--swaybar/status_line.c1
-rw-r--r--swaybar/tray/host.c11
-rw-r--r--swaybar/tray/icon.c27
-rw-r--r--swaybar/tray/item.c12
-rw-r--r--swaybar/tray/watcher.c12
-rw-r--r--swaymsg/main.c33
-rw-r--r--swaymsg/swaymsg.1.scd2
-rw-r--r--swaynag/config.c11
-rw-r--r--swaynag/main.c26
-rw-r--r--swaynag/meson.build3
-rw-r--r--swaynag/swaynag.c43
-rw-r--r--swaynag/types.c10
236 files changed, 7106 insertions, 7328 deletions
diff --git a/.builds/alpine.yml b/.builds/alpine.yml
index 324cf6ce..59df7737 100644
--- a/.builds/alpine.yml
+++ b/.builds/alpine.yml
@@ -4,6 +4,7 @@ packages:
4 - eudev-dev 4 - eudev-dev
5 - gdk-pixbuf-dev 5 - gdk-pixbuf-dev
6 - json-c-dev 6 - json-c-dev
7 - libdisplay-info-dev
7 - libevdev-dev 8 - libevdev-dev
8 - libinput-dev 9 - libinput-dev
9 - libseat-dev 10 - libseat-dev
@@ -19,7 +20,8 @@ packages:
19 - wayland-protocols 20 - wayland-protocols
20 - xcb-util-image-dev 21 - xcb-util-image-dev
21 - xcb-util-wm-dev 22 - xcb-util-wm-dev
22 - xwayland 23 - xwayland-dev
24 - hwdata-dev
23sources: 25sources:
24 - https://github.com/swaywm/sway 26 - https://github.com/swaywm/sway
25 - https://gitlab.freedesktop.org/wlroots/wlroots.git 27 - https://gitlab.freedesktop.org/wlroots/wlroots.git
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml
index ac5536e5..9972c01a 100644
--- a/.builds/archlinux.yml
+++ b/.builds/archlinux.yml
@@ -3,6 +3,7 @@ packages:
3 - cairo 3 - cairo
4 - gdk-pixbuf2 4 - gdk-pixbuf2
5 - json-c 5 - json-c
6 - libdisplay-info
6 - libegl 7 - libegl
7 - libinput 8 - libinput
8 - libxcb 9 - libxcb
@@ -17,6 +18,7 @@ packages:
17 - xcb-util-wm 18 - xcb-util-wm
18 - xorg-xwayland 19 - xorg-xwayland
19 - seatd 20 - seatd
21 - hwdata
20sources: 22sources:
21 - https://github.com/swaywm/sway 23 - https://github.com/swaywm/sway
22 - https://gitlab.freedesktop.org/wlroots/wlroots.git 24 - https://gitlab.freedesktop.org/wlroots/wlroots.git
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
index aa3007d8..29c6312a 100644
--- a/.builds/freebsd.yml
+++ b/.builds/freebsd.yml
@@ -20,11 +20,14 @@ packages:
20- devel/libudev-devd 20- devel/libudev-devd
21- graphics/libdrm 21- graphics/libdrm
22- graphics/mesa-libs 22- graphics/mesa-libs
23- sysutils/libdisplay-info
23- sysutils/seatd 24- sysutils/seatd
24- x11/libinput 25- x11/libinput
25- x11/libX11 26- x11/libX11
26- x11/pixman 27- x11/pixman
27- x11/xcb-util-wm 28- x11/xcb-util-wm
29- x11-servers/xwayland-devel
30- misc/hwdata
28sources: 31sources:
29- https://github.com/swaywm/sway 32- https://github.com/swaywm/sway
30- https://gitlab.freedesktop.org/wlroots/wlroots.git 33- https://gitlab.freedesktop.org/wlroots/wlroots.git
diff --git a/.gitignore b/.gitignore
index 1ec0cb52..167960e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ bin/
6test/ 6test/
7build/ 7build/
8build-*/ 8build-*/
9.cache/
9.lvimrc 10.lvimrc
10config-debug 11config-debug
11wayland-*-protocol.* 12wayland-*-protocol.*
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 00000000..35ee25b0
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1 @@
Ronan Pigott <ronan@rjp.ie> <rpigott@berkeley.edu>
diff --git a/README.ar.md b/README.ar.md
new file mode 100644
index 00000000..4f9bf828
--- /dev/null
+++ b/README.ar.md
@@ -0,0 +1,91 @@
1# sway
2
3sway
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
5sway 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
10Vydání jsou podepsána [E88F5E48] a publikována [na GitHubu][GitHub releases].
11
12## Instalace
13
14### Z balíčků
15
16Sway je dostupný ve spoustě distribucí. Zkuste nainstalovat balíček "sway" ve vaší
17distribuci.
18
19### Kompilace ze zdrojových kódů
20
21Podívejte se na [tuto stránku wiki][Development setup], pokud chcete sestavit HEAD
22sway a wlroots pro testování nebo vývoj.
23
24Nainstalujte 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
40Spusťte tyto příkazy:
41
42 meson build/
43 ninja -C build/
44 sudo ninja -C build/ install
45
46## Konfigurace
47
48Pokud již používáte i3, zkopírujte svou konfiguraci i3 do `~/.config/sway/config`
49a ta bude ihned fungovat. Jinak zkopírujte do `~/.config/sway/config` ukázkový
50konfigurační soubor. Ten se obvykle nachází v `/etc/sway/config`.
51Pro více informací o konfiguraci spusťte `man 5 sway`.
52
53## Spuštění
54
55Spusťte `sway` z TTY. Některé správce zobrazení mohou fungovat, ale nejsou
56podporovány sway (je známo, že gdm funguje docela dobře).
57
58[en]: https://github.com/swaywm/sway#readme
59[cs]: README.cs.md
60[de]: README.de.md
61[dk]: README.dk.md
62[es]: README.es.md
63[fr]: README.fr.md
64[sv]: README.sv.md
65[gr]: README.gr.md
66[hi]: README.hi.md
67[hu]: README.hu.md
68[ir]: README.ir.md
69[it]: README.it.md
70[ja]: README.ja.md
71[ko]: README.ko.md
72[nl]: README.nl.md
73[pl]: README.pl.md
74[pt]: README.pt.md
75[ro]: README.ro.md
76[ru]: README.ru.md
77[tr]: README.tr.md
78[uk]: README.uk.md
79[zh-CN]: README.zh-CN.md
80[zh-TW]: README.zh-TW.md
81[i3]: https://i3wm.org/
82[Wayland]: http://wayland.freedesktop.org/
83[FAQ]: https://github.com/swaywm/sway/wiki
84[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
85[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
86[GitHub releases]: https://github.com/swaywm/sway/releases
87[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
88[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
89[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.de.md b/README.de.md
index 059fda89..68b411d9 100644
--- a/README.de.md
+++ b/README.de.md
@@ -2,13 +2,13 @@
2Sway 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). 2Sway 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
5Jedes Release wird mit dem PGP-Schlüssel [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) signiert und auf GitHub veröffentlicht. 5Jedes 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
9Sway 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
11Falls 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
11Sway kann in vielen Distributionen direkt durch die Paketverwaltung installiert werden. Versuche einfach das Packet "sway" zu installieren.
12 12
13### Quellcode selbst kompilieren 13### Quellcode selbst kompilieren
14 14
@@ -23,8 +23,8 @@ sway benötigt die folgenden Pakete:
23* pango 23* pango
24* cairo 24* cairo
25* gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt) 25* gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt)
26* [scdoc](https://git.sr.ht/~sircmpwn/scdoc)\* (Optional, wird für die Dokumentation (Man Pages) benötigt) 26* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (Optional, wird für die Dokumentation (Man Pages) benötigt)\*
27* git\* 27* git (Optional: Versionsinfo)\*
28 28
29_\*Werden nur während des Kompilierens benötigt_ 29_\*Werden nur während des Kompilierens benötigt_
30 30
@@ -34,12 +34,6 @@ Führe die folgenden Befehle aus:
34 ninja -C build 34 ninja -C build
35 sudo ninja -C build install 35 sudo ninja -C build install
36 36
37Falls dein System nicht logind benutzt, musst du sway noch die passenden Berechtigungen geben:
38
39 sudo chmod a+s /usr/local/bin/sway
40
41Sway läuft nur in der Startphase mit Root-Rechten.
42
43## Konfiguration 37## Konfiguration
44 38
45Falls 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`. 39Falls du von i3 migrierst, kannst du deine Konfigurationsdatei nach `~/.config/sway/config` kopieren und die Einstellungen sollten ohne Weiteres funktionieren. Ansonsten kannst du die Beispielkonfiguration, die normalerweise in `/etc/sway/config` liegt, nach `~/.config/sway/config` kopieren. Die Dokumentation zur Konfigurationsdatei findest du in `man 5 sway`.
diff --git a/README.dk.md b/README.dk.md
index 3a641295..5ce94cde 100644
--- a/README.dk.md
+++ b/README.dk.md
@@ -45,12 +45,6 @@ Kør følgende kommandoer:
45 ninja -C build 45 ninja -C build
46 sudo ninja -C build install 46 sudo ninja -C build install
47 47
48På systemer uden logind eller seatd skal du sætte SUID bit på sway filen:
49
50 sudo chmod a+s /usr/local/bin/sway
51
52Sway dropper 'root' tilladelser kort efter opstart.
53
54## Konfiguration 48## Konfiguration
55 49
56Hvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til 50Hvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til
diff --git a/README.es.md b/README.es.md
index 5e598e1a..1f1657df 100644
--- a/README.es.md
+++ b/README.es.md
@@ -44,12 +44,6 @@ Desde su consola, ejecute las órdenes:
44 ninja -C build 44 ninja -C build
45 sudo ninja -C build install 45 sudo ninja -C build install
46 46
47En sistemas sin `logind`, necesitará cambiar los permisos del archivo compilado de sway:
48
49 sudo chmod a+s /usr/local/bin/sway
50
51Sway abandonará los permisos de super-usuario al poco de arrancar.
52
53## Configuración 47## Configuración
54 48
55Si ya utiliza i3, copie su archivo de configuración de i3 a `~/.config/sway/config` y 49Si ya utiliza i3, copie su archivo de configuración de i3 a `~/.config/sway/config` y
diff --git a/README.fr.md b/README.fr.md
index e99160e8..7752fc70 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -51,12 +51,6 @@ Exécutez ces commandes :
51 ninja -C build 51 ninja -C build
52 sudo ninja -C build install 52 sudo ninja -C build install
53 53
54Sur les systèmes sans logind, vous devez suid le binaire de sway :
55
56 sudo chmod a+s /usr/local/bin/sway
57
58Sway se débarassera des permissions *root* peu de temps après le démarrage.
59
60## Configuration 54## Configuration
61 55
62Si vous utilisez déjà i3, copiez votre configuration i3 vers 56Si vous utilisez déjà i3, copiez votre configuration i3 vers
diff --git a/README.ge.md b/README.ge.md
new file mode 100644
index 00000000..bb8b9a34
--- /dev/null
+++ b/README.ge.md
@@ -0,0 +1,61 @@
1# sway
2
3sway არის [i3]-თავსებადი [Wayland]-ის კომპოზიტორი. მეტი ინფორმაციისთვის იხილეთ
4[FAQ]. დაუკავშირდით [IRC არხს][IRC channel] \(#sway irc.libera.chat-ზე).
5
6## გამოშვების ხელმოწერები
7
8გამოშვებები ხელმოწერილია [E88F5E48]-ით და გამოქვეყნებულია [GitHub-ზე][GitHub releases].
9
10## ინსტალაცია
11
12### რეპოზიტორიიდან
13
14Sway არის ხელმისაწვდომი ბევრი დისტრიბუტაციისთვის. ცადეთ "sway" პაკეტის ინსტალაცია თქვენთვის.
15
16### კოდის კომპილაცია
17
18იხილეთ [ეს ვიკი გვერდი][Development setup] თუ გინდათ რომ ააწყოთ sway და wlroots სატესტოდ ან დეველოპმენტისთვის.
19
20დააინსტალირეთ დამოკიდებულებები:
21
22* meson \*
23* [wlroots]
24* wayland
25* wayland-protocols \*
26* pcre2
27* json-c
28* pango
29* cairo
30* gdk-pixbuf2 (ასევე არჩევითია: system tray)
31* [scdoc] (ასევე არჩევითია: man pages) \*
32* git (ასევე არჩევითია: version info) \*
33
34_\* Compile-time dep_
35
36გაუშვით ეს ბრძანებები:
37
38 meson build/
39 ninja -C build/
40 sudo ninja -C build/ install
41
42## კონფიგურაცია
43
44თუ უკვე იყენებთ i3-ს, მაშინ დააკოპირე i3 კონფიგურაცია და ჩასვი `~/.config/sway/config`
45და უპრობლემოდ იმუშავებს პირდაპირ. წინააღმდეგ შემთხვევაში კონფიგურაციის ნიმუში ჩააკოპირეთ აქ: `~/.config/sway/config`. კომპიგურაციის ნიმუში ხშირ შემთხვევაში არის `/etc/sway/config`.
46გაუშვი `man 5 sway` კონპიგურაციაზე ინფორმაციის მისაღებად.
47
48## გაშვება
49
50გაუშვი `sway` TTY-ისთვის. ზოგიერთმა ლოგინ მენეჯერმა შეიძლება იმუშავოს, მაგრამ არ
51არის მხარდაჭერილი sway-სგან (როგორც წესი კარგად მუშაობს gdm).
52
53[i3]: https://i3wm.org/
54[Wayland]: http://wayland.freedesktop.org/
55[FAQ]: https://github.com/swaywm/sway/wiki
56[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
57[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
58[GitHub releases]: https://github.com/swaywm/sway/releases
59[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
60[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
61[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.gr.md b/README.gr.md
index f325cb88..d697f78e 100644
--- a/README.gr.md
+++ b/README.gr.md
@@ -44,12 +44,6 @@ _\*Compile-time dep_
44 ninja -C build/ 44 ninja -C build/
45 sudo ninja -C build/ install 45 sudo ninja -C build/ install
46 46
47Σε συστήματα χωρίς logind ή seatd, πρέπει να κάνετε suid το sway binary:
48
49 sudo chmod a+s /usr/local/bin/sway
50
51Το Sway θα κάνει drop root δικαιώματα λίγο μετά την εκκίνηση.
52
53## Configuration 47## Configuration
54 48
55Εάν ήδη χρησιμοποιήτε το i3, αντιγράψτε το i3 config σας στο `~/.config/sway/config` και 49Εάν ήδη χρησιμοποιήτε το i3, αντιγράψτε το i3 config σας στο `~/.config/sway/config` και
diff --git a/README.hi.md b/README.hi.md
index af64eada..eae5e90a 100644
--- a/README.hi.md
+++ b/README.hi.md
@@ -48,13 +48,6 @@ _\* Compilation के समय आवश्यक_
48 ninja -C build/ 48 ninja -C build/
49 sudo ninja -C build/ install 49 sudo ninja -C build/ install
50 50
51उन systems पर जिनमें ना तो logind है, ना ही seatd है, आपको sway की binary
52को suid करना पडेगा:
53
54 sudo chmod a+s /usr/local/bin/sway
55
56Sway अपनी root अनुमतियां प्रारंभ होने के कुछ ही देर बाद छोड़ देगी।
57
58## Configuration 51## Configuration
59 52
60अगर आप पहले से ही i3 का उपयोग करते हैं तो अपने i3 config को 53अगर आप पहले से ही i3 का उपयोग करते हैं तो अपने i3 config को
diff --git a/README.hu.md b/README.hu.md
index 9e3bc9e1..82ca6785 100644
--- a/README.hu.md
+++ b/README.hu.md
@@ -44,13 +44,6 @@ Futtasd ezeket a parancsokat:
44 ninja -C build 44 ninja -C build
45 sudo ninja -C build install 45 sudo ninja -C build install
46 46
47Ha `logind` nélküli rendszert használsz, akkor be kell állítanod a `suid` bitet
48a futtaható állományon:
49
50 sudo chmod a+s /usr/local/bin/sway
51
52A Sway indulás után nem sokkal el fogja engedni a root jogosultságait.
53
54## Konfiguráció 47## Konfiguráció
55 48
56Ha előzőleg i3-mat használtál, akkor átmásolhatod az i3 beállításaidat a 49Ha előzőleg i3-mat használtál, akkor átmásolhatod az i3 beállításaidat a
diff --git a/README.ir.md b/README.ir.md
index 64956a9e..a485a405 100644
--- a/README.ir.md
+++ b/README.ir.md
@@ -45,12 +45,6 @@ _\*نیازمندی‌های زمان کامپایل برنامه_
45 ninja -C build 45 ninja -C build
46 sudo ninja -C build install 46 sudo ninja -C build install
47 47
48روی سیستم‌های بدون logind، باید فرمان زیر را برای suid کردن باینری sway اجرا کنید:
49
50 sudo chmod a+s /usr/local/bin/sway
51
52&rlm;sway پس از startup مجوزهای دسترسی root را رها می‌کند.
53
54### شخصی سازی و تنظیمات 48### شخصی سازی و تنظیمات
55 49
56اگر در حال حاضر از i3 استفاده می‌کنید، تنظیمات i3 خودتان را در فایل ‪`~/.config/sway/config`‬ کپی کنید و بدون نیاز به تغییر کار خواهد کرد. در غیر این‌صورت، فایل نمونه تنظیمات را استفاده کنید. این فایل عموما در ‪`/etc/sway/config`‬ قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید. 50اگر در حال حاضر از i3 استفاده می‌کنید، تنظیمات i3 خودتان را در فایل ‪`~/.config/sway/config`‬ کپی کنید و بدون نیاز به تغییر کار خواهد کرد. در غیر این‌صورت، فایل نمونه تنظیمات را استفاده کنید. این فایل عموما در ‪`/etc/sway/config`‬ قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید.
diff --git a/README.it.md b/README.it.md
index b14b522d..82bb5783 100644
--- a/README.it.md
+++ b/README.it.md
@@ -42,13 +42,6 @@ Esegui questi comandi:
42 ninja -C build/ 42 ninja -C build/
43 sudo ninja -C build/ install 43 sudo ninja -C build/ install
44 44
45Nei sistemi in cui non sono disponibili né logind né seatd, è necessario
46impostare il permesso suid al binario di sway:
47
48 sudo chmod a+s /usr/local/bin/sway
49
50Sway rinuncerà ai permessi di root poco dopo l'avvio.
51
52## Configurazione 45## Configurazione
53 46
54Se hai già usato i3, copia il tuo file di configurazione in 47Se hai già usato i3, copia il tuo file di configurazione in
diff --git a/README.ja.md b/README.ja.md
index ece9ce48..4e9a9971 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -34,11 +34,11 @@ Swayは沢山のディストリビューションで提供されています。"
34* json-c 34* json-c
35* pango 35* pango
36* cairo 36* cairo
37* gdk-pixbuf2 (システムイコンで必要です) 37* gdk-pixbuf2 (任意: システムイコンで必要です)
38* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (manで必要です) \* 38* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (任意: manで必要です) \*
39* git \* 39* git (任意: バージョン情報で必要です) \*
40 40
41_\*コンパイル時_ 41_\*コンパイル時の依存_
42 42
43次のコマンドを実行してください: 43次のコマンドを実行してください:
44 44
@@ -46,12 +46,6 @@ _\*コンパイルの時_
46 ninja -C build 46 ninja -C build
47 sudo ninja -C build install 47 sudo ninja -C build install
48 48
49logindを使用していないシステムでは、バイナリにsuidを設定する必要があります:
50
51 sudo chmod a+s /usr/local/bin/sway
52
53swayは起動後、すぐにroot許可を落とします。
54
55## 設定 49## 設定
56 50
57既にi3を使用している場合は、i3の設定ファイルを`~/.config/sway/config`にコピーすれば動きます。そうでない場合は、サンプルの設定ファイルを`~/.config/sway/config`にコピーしてください。サンプルの設定ファイルは、通常`/etc/sway/config`にあります。`man 5 sway`を実行することで、設定に関する情報を見ることができます。 51既にi3を使用している場合は、i3の設定ファイルを`~/.config/sway/config`にコピーすれば動きます。そうでない場合は、サンプルの設定ファイルを`~/.config/sway/config`にコピーしてください。サンプルの設定ファイルは、通常`/etc/sway/config`にあります。`man 5 sway`を実行することで、設定に関する情報を見ることができます。
diff --git a/README.ko.md b/README.ko.md
index 7a1f4ce1..e086c174 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -43,12 +43,6 @@ _\*컴파일 떄 필요_
43 ninja -C build 43 ninja -C build
44 sudo ninja -C build install 44 sudo ninja -C build install
45 45
46logind를 사용하고 있지 않는 시스템에서는, 바이너리에 suid를 설정할 필요가 있습니다:
47
48 sudo chmod a+s /usr/local/bin/sway
49
50Sway는 시작 후에 root 권한을 drop할 것 입니다.
51
52## 설정 46## 설정
53 47
54i3를 이미 사용 중이라면, i3 config을 `~/.config/sway/config`로 복사하세요. 48i3를 이미 사용 중이라면, i3 config을 `~/.config/sway/config`로 복사하세요.
diff --git a/README.md b/README.md
index 27918868..15c7c099 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
1# sway 1# sway
2 2
3**[English][en]** - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [Svenska][sv] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] 3**[English][en]** - [عربي][ar] - [Česky][cs] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქართული][ge] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Svenska][sv] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW]
4 4
5sway is an [i3]-compatible [Wayland] compositor. Read the [FAQ]. Join the 5sway is an [i3]-compatible [Wayland] compositor. Read the [FAQ]. Join the
6[IRC channel] \(#sway on irc.libera.chat). 6[IRC channel] \(#sway on irc.libera.chat).
@@ -31,7 +31,8 @@ Install dependencies:
31* json-c 31* json-c
32* pango 32* pango
33* cairo 33* cairo
34* gdk-pixbuf2 (optional: system tray) 34* gdk-pixbuf2 (optional: additional image formats for system tray)
35* [swaybg] (optional: wallpaper)
35* [scdoc] (optional: man pages) \* 36* [scdoc] (optional: man pages) \*
36* git (optional: version info) \* 37* git (optional: version info) \*
37 38
@@ -43,12 +44,6 @@ Run these commands:
43 ninja -C build/ 44 ninja -C build/
44 sudo ninja -C build/ install 45 sudo ninja -C build/ install
45 46
46On systems without logind nor seatd, you need to suid the sway binary:
47
48 sudo chmod a+s /usr/local/bin/sway
49
50Sway will drop root permissions shortly after startup.
51
52## Configuration 47## Configuration
53 48
54If you already use i3, then copy your i3 config to `~/.config/sway/config` and 49If you already use i3, then copy your i3 config to `~/.config/sway/config` and
@@ -62,11 +57,13 @@ Run `sway` from a TTY. Some display managers may work but are not supported by
62sway (gdm is known to work fairly well). 57sway (gdm is known to work fairly well).
63 58
64[en]: https://github.com/swaywm/sway#readme 59[en]: https://github.com/swaywm/sway#readme
60[ar]: README.ar.md
61[cs]: README.cs.md
65[de]: README.de.md 62[de]: README.de.md
66[dk]: README.dk.md 63[dk]: README.dk.md
67[es]: README.es.md 64[es]: README.es.md
68[fr]: README.fr.md 65[fr]: README.fr.md
69[sv]: README.sv.md 66[ge]: README.ge.md
70[gr]: README.gr.md 67[gr]: README.gr.md
71[hi]: README.hi.md 68[hi]: README.hi.md
72[hu]: README.hu.md 69[hu]: README.hu.md
@@ -75,10 +72,12 @@ sway (gdm is known to work fairly well).
75[ja]: README.ja.md 72[ja]: README.ja.md
76[ko]: README.ko.md 73[ko]: README.ko.md
77[nl]: README.nl.md 74[nl]: README.nl.md
75[no]: README.no.md
78[pl]: README.pl.md 76[pl]: README.pl.md
79[pt]: README.pt.md 77[pt]: README.pt.md
80[ro]: README.ro.md 78[ro]: README.ro.md
81[ru]: README.ru.md 79[ru]: README.ru.md
80[sv]: README.sv.md
82[tr]: README.tr.md 81[tr]: README.tr.md
83[uk]: README.uk.md 82[uk]: README.uk.md
84[zh-CN]: README.zh-CN.md 83[zh-CN]: README.zh-CN.md
@@ -91,4 +90,5 @@ sway (gdm is known to work fairly well).
91[GitHub releases]: https://github.com/swaywm/sway/releases 90[GitHub releases]: https://github.com/swaywm/sway/releases
92[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup 91[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
93[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots 92[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
93[swaybg]: https://github.com/swaywm/swaybg/
94[scdoc]: https://git.sr.ht/~sircmpwn/scdoc 94[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.nl.md b/README.nl.md
index 8a344f03..bf1ea975 100644
--- a/README.nl.md
+++ b/README.nl.md
@@ -44,12 +44,6 @@ Voer deze opdrachten uit:
44 ninja -C build 44 ninja -C build
45 sudo ninja -C build install 45 sudo ninja -C build install
46 46
47Op systemen zonder logind, moet je bij het binaire bestand het suid bit instellen:
48
49 sudo chmod a+s /usr/local/bin/sway
50
51Sway zal root-rechten kort na het opstarten loslaten.
52
53## Configuratie 47## Configuratie
54 48
55Als je al i3 gebruikt, kopieer dan je i3-configuratie naar `~/.config/sway/config` en 49Als 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
3Sway er en [i3]-kompatibel [Wayland] compositor. Les [Ofte stilte spørsmål].
4Delta på [IRC kanalen][IRC kanal] \(#sway på irc.libera.chat).
5
6## Utgivelses Signaturer
7
8Utgivelser er signert med [E88F5E48] og publisert [på GitHub][GitHub
9releases].
10
11## Installasjon
12
13### Fra system pakker
14
15Sway er tilgjengelig i mange distribusjoner. Prøv å installere "sway" pakken
16fra din distro sine repoer.
17
18Er du interessert i å pakke Sway for din distribusjon kan du ta turen innom
19IRC-kanalen eller send en e-post til sir@cmpwn.com for råd.
20
21### Kompilering fra kildekode
22
23Se [denne wiki-siden][Oppsetting for utvikling] hvis du vil bygge fra HEAD grenen av sway og
24wlroots for testing eller utvikling.
25
26Installasjonsavhengigheter:
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
42Kjør følgende kommandoer:
43
44 meson build
45 ninja -C build
46 sudo ninja -C build install
47
48## Konfigurasjon
49
50Hvis 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
57Kjør `sway` fra en TTY. Noen display managers kan fungere, men Sway har ikke
58støtte for dem (gdm er kjent for å fungere ganske bra).
59
60[i3]: https://i3wm.org/
61[Wayland]: http://wayland.freedesktop.org/
62[Ofte stilte spørsmål]: https://github.com/swaywm/sway/wiki
63[IRC kanal]: https://web.libera.chat/gamja/?channels=#sway
64[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
65[GitHub releases]: https://github.com/swaywm/sway/releases
66[Oppsetting for utvikling]: https://github.com/swaywm/sway/wiki/Development-Setup
67[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
68[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.pl.md b/README.pl.md
index ab99abb1..65b3c3a1 100644
--- a/README.pl.md
+++ b/README.pl.md
@@ -44,12 +44,6 @@ Wykonaj następujące polecenia:
44 ninja -C build 44 ninja -C build
45 sudo ninja -C build install 45 sudo ninja -C build install
46 46
47Na systemach bez logind należy wykonać polecenie suid na pliku wykonywalnym sway:
48
49 sudo chmod a+s /usr/local/bin/sway
50
51Sway pozbędzie się uprawnień roota tuż po wystartowaniu.
52
53## Konfiguracja 47## Konfiguracja
54 48
55Jeśli już korzystasz z i3, skopiuj swoją konfigurację i3 do katalogu `~/.config/sway/config` i 49Jeśli już korzystasz z i3, skopiuj swoją konfigurację i3 do katalogu `~/.config/sway/config` i
diff --git a/README.pt.md b/README.pt.md
index b22948aa..c1611a31 100644
--- a/README.pt.md
+++ b/README.pt.md
@@ -46,12 +46,6 @@ Execute esses comandos:
46 ninja -C build 46 ninja -C build
47 sudo ninja -C build install 47 sudo ninja -C build install
48 48
49Em sistemas sem logind, você precisa preparar o binário do sway:
50
51 sudo chmod a+s /usr/local/bin/sway
52
53O 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
57Se você já utiliza o i3, então copie os seus arquivos de configuração para `~/.config/sway/config` e 51Se você já utiliza o i3, então copie os seus arquivos de configuração para `~/.config/sway/config` e
diff --git a/README.ro.md b/README.ro.md
index 4ce05093..a3559a8b 100644
--- a/README.ro.md
+++ b/README.ro.md
@@ -43,14 +43,6 @@ Rulați aceste comenzi:
43 sudo ninja -C build install 43 sudo ninja -C build install
44``` 44```
45 45
46Pe 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
52Imediat după pornire, Sway va renunța la permisiunile de root.
53
54## Configurare 46## Configurare
55 47
56Dacă 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`. 48Dacă folosiți deja i3, copiați fișierul de configurare din i3 în `~/.config/sway/config`, și va funcționa fără a necesita nici o modificare. In caz contrar, copiați exemplul de configurare (disponibil de obicei în `/etc/sway/config`) în `~/.config/sway/config`.
diff --git a/README.ru.md b/README.ru.md
index fb6f3344..edc0eda7 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -45,12 +45,6 @@ _\*Зависимости для сборки_
45 ninja -C build 45 ninja -C build
46 sudo ninja -C build install 46 sudo ninja -C build install
47 47
48На системах без logind вам понадобится добавить suid к файлу программы sway:
49
50 sudo chmod a+s /usr/local/bin/sway
51
52sway сбросит root-права при запуске.
53
54## Настройка 48## Настройка
55 49
56Если вы уже используете i3, скопируйте ваш конфигурационный файл i3 в `~/.config/sway/config`, и 50Если вы уже используете i3, скопируйте ваш конфигурационный файл i3 в `~/.config/sway/config`, и
diff --git a/README.sv.md b/README.sv.md
index 28724270..c50ca068 100644
--- a/README.sv.md
+++ b/README.sv.md
@@ -41,12 +41,6 @@ Kör dessa kommandon:
41 ninja -C build/ 41 ninja -C build/
42 sudo ninja -C build/ install 42 sudo ninja -C build/ install
43 43
44På system utan logind eller seatd måste du ge sways exekverbara fil root-privilegier:
45
46 sudo chmod a+s /usr/local/bin/sway
47
48Sway kommer att överge sina root-privilegier kort efter uppstart.
49
50## Konfiguration 44## Konfiguration
51 45
52Ifall du redan använder i3 så kan du kopiera din konfigurationsfil till `~/.config/sway/config` och det kommer då att fungera som det ska. 46Ifall du redan använder i3 så kan du kopiera din konfigurationsfil till `~/.config/sway/config` och det kommer då att fungera som det ska.
diff --git a/README.tr.md b/README.tr.md
index e09d2eaf..40de1474 100644
--- a/README.tr.md
+++ b/README.tr.md
@@ -42,12 +42,6 @@ _\*Derleme-anı bağımlılıkları_
42 ninja -C build 42 ninja -C build
43 sudo ninja -C build install 43 sudo ninja -C build install
44 44
45logind olmayan sistemlerde, sway ikilisine (binary) izin vermeniz (suid) gerekir:
46
47 sudo chmod a+s /usr/local/bin/sway
48
49Sway, başlangıçtan kısa bir süre sonra kök(root) izinlerini bırakacaktır.
50
51## Yapılandırma 45## Yapılandırma
52 46
53Zaten 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. 47Zaten i3 kullanıyorsanız, i3 yapılandırmanızı `~/.config/sway/config` konumuna kopyalayın ve kutudan çıktığı gibi çalışacaktır. Aksi takdirde, örnek yapılandırma dosyasını `~/.config/sway/config` konumuna kopyalayın. Genellikle `/etc/sway/config` konumunda bulunur.
diff --git a/README.uk.md b/README.uk.md
index d9adcd13..33359cff 100644
--- a/README.uk.md
+++ b/README.uk.md
@@ -55,12 +55,6 @@ _\*Лише для компіляції_
55 ninja -C build 55 ninja -C build
56 sudo ninja -C build install 56 sudo ninja -C build install
57 57
58На системах без logind, необхідно встановити біт SUID на виконуваний файл sway:
59
60 sudo chmod a+s /usr/local/bin/sway
61
62Sway втратить права доступу root незабаром після запуску.
63
64## Налаштування 58## Налаштування
65 59
66Якщо ви вже використовуєте i3, скопіюйте свій файл налаштувань 60Якщо ви вже використовуєте i3, скопіюйте свій файл налаштувань
diff --git a/README.zh-CN.md b/README.zh-CN.md
index b057bfb8..a6f4518a 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -39,12 +39,6 @@ _\*编译时依赖_
39 ninja -C build/ 39 ninja -C build/
40 sudo ninja -C build/ install 40 sudo ninja -C build/ install
41 41
42在没有logind或seatd的系统上, 你需要给sway二进制文件设置suid:
43
44 sudo chmod a+s /usr/local/bin/sway
45
46启动后,Sway会尽快放弃root权限。
47
48## 配置 42## 配置
49 43
50如果你已经在使用i3,直接复制i3配置文件到 `~/.config/sway/config`,这是开箱即用的。或者,你可以复制配置样例到`~/.config/sway/config`。它通常位于 `/etc/sway/config`。 44如果你已经在使用i3,直接复制i3配置文件到 `~/.config/sway/config`,这是开箱即用的。或者,你可以复制配置样例到`~/.config/sway/config`。它通常位于 `/etc/sway/config`。
diff --git a/README.zh-TW.md b/README.zh-TW.md
index a7168244..2de2f63f 100644
--- a/README.zh-TW.md
+++ b/README.zh-TW.md
@@ -44,12 +44,6 @@ _\*編譯時相依_
44 ninja -C build 44 ninja -C build
45 sudo ninja -C build install 45 sudo ninja -C build install
46 46
47在沒有 logind 的系統上,你需要為 sway 的執行檔加上 suid。
48
49 sudo chmod a+s /usr/local/bin/sway
50
51Sway 在啟動不久後就會放棄 root 權限。
52
53## 設定檔 47## 設定檔
54 48
55如果你已經在使用 i3,你可以直接將你的 i3 設定檔複製到 `~/.config/sway/config` 然後就能直接使用。 49如果你已經在使用 i3,你可以直接將你的 i3 設定檔複製到 `~/.config/sway/config` 然後就能直接使用。
diff --git a/client/pool-buffer.c b/client/pool-buffer.c
index ea31edd3..c47c40eb 100644
--- a/client/pool-buffer.c
+++ b/client/pool-buffer.c
@@ -1,50 +1,42 @@
1#define _POSIX_C_SOURCE 200809
2#include <assert.h> 1#include <assert.h>
3#include <cairo.h> 2#include <cairo.h>
3#include <errno.h>
4#include <fcntl.h> 4#include <fcntl.h>
5#include <pango/pangocairo.h> 5#include <pango/pangocairo.h>
6#include <stdio.h> 6#include <stdio.h>
7#include <stdlib.h> 7#include <stdlib.h>
8#include <string.h> 8#include <string.h>
9#include <sys/mman.h> 9#include <sys/mman.h>
10#include <time.h>
10#include <unistd.h> 11#include <unistd.h>
11#include <wayland-client.h> 12#include <wayland-client.h>
12#include "config.h" 13#include "config.h"
13#include "pool-buffer.h" 14#include "pool-buffer.h"
14#include "util.h" 15#include "util.h"
15 16
16static int create_pool_file(size_t size, char **name) { 17static 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
50static void buffer_release(void *data, struct wl_buffer *wl_buffer) { 42static void buffer_release(void *data, struct wl_buffer *wl_buffer) {
@@ -62,17 +54,20 @@ static struct pool_buffer *create_buffer(struct wl_shm *shm,
62 uint32_t stride = width * 4; 54 uint32_t stride = width * 4;
63 size_t size = stride * height; 55 size_t size = stride * height;
64 56
65 char *name; 57 int fd = anonymous_shm_open();
66 int fd = create_pool_file(size, &name); 58 if (fd == -1) {
67 assert(fd != -1); 59 return NULL;
60 }
61 if (ftruncate(fd, size) < 0) {
62 close(fd);
63 return NULL;
64 }
68 void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 65 void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
69 struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); 66 struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
70 buf->buffer = wl_shm_pool_create_buffer(pool, 0, 67 buf->buffer = wl_shm_pool_create_buffer(pool, 0,
71 width, height, stride, format); 68 width, height, stride, format);
72 wl_shm_pool_destroy(pool); 69 wl_shm_pool_destroy(pool);
73 close(fd); 70 close(fd);
74 unlink(name);
75 free(name);
76 71
77 buf->size = size; 72 buf->size = size;
78 buf->width = width; 73 buf->width = width;
diff --git a/common/gesture.c b/common/gesture.c
index 8c2efe99..272aa837 100644
--- a/common/gesture.c
+++ b/common/gesture.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "gesture.h" 1#include "gesture.h"
3 2
4#include <math.h> 3#include <math.h>
@@ -12,23 +11,6 @@
12 11
13const uint8_t GESTURE_FINGERS_ANY = 0; 12const uint8_t GESTURE_FINGERS_ANY = 0;
14 13
15// Helper to easily allocate and format string
16static char *strformat(const char *format, ...) {
17 va_list args;
18 va_start(args, format);
19 int length = vsnprintf(NULL, 0, format, args) + 1;
20 va_end(args);
21
22 char *result = malloc(length);
23 if (result) {
24 va_start(args, format);
25 vsnprintf(result, length, format, args);
26 va_end(args);
27 }
28
29 return result;
30}
31
32char *gesture_parse(const char *input, struct gesture *output) { 14char *gesture_parse(const char *input, struct gesture *output) {
33 // Clear output in case of failure 15 // Clear output in case of failure
34 output->type = GESTURE_TYPE_NONE; 16 output->type = GESTURE_TYPE_NONE;
@@ -38,7 +20,7 @@ char *gesture_parse(const char *input, struct gesture *output) {
38 // Split input type, fingers and directions 20 // Split input type, fingers and directions
39 list_t *split = split_string(input, ":"); 21 list_t *split = split_string(input, ":");
40 if (split->length < 1 || split->length > 3) { 22 if (split->length < 1 || split->length > 3) {
41 return strformat( 23 return format_str(
42 "expected <gesture>[:<fingers>][:direction], got %s", 24 "expected <gesture>[:<fingers>][:direction], got %s",
43 input); 25 input);
44 } 26 }
@@ -51,8 +33,8 @@ char *gesture_parse(const char *input, struct gesture *output) {
51 } else if (strcmp(split->items[0], "swipe") == 0) { 33 } else if (strcmp(split->items[0], "swipe") == 0) {
52 output->type = GESTURE_TYPE_SWIPE; 34 output->type = GESTURE_TYPE_SWIPE;
53 } else { 35 } else {
54 return strformat("expected hold|pinch|swipe, got %s", 36 return format_str("expected hold|pinch|swipe, got %s",
55 split->items[0]); 37 (const char *)split->items[0]);
56 } 38 }
57 39
58 // Parse optional arguments 40 // Parse optional arguments
@@ -67,7 +49,7 @@ char *gesture_parse(const char *input, struct gesture *output) {
67 next = split->length == 3 ? split->items[2] : NULL; 49 next = split->length == 3 ? split->items[2] : NULL;
68 } else if (split->length == 3) { 50 } else if (split->length == 3) {
69 // Fail here if argument can only be finger count 51 // Fail here if argument can only be finger count
70 return strformat("expected 1-9, got %s", next); 52 return format_str("expected 1-9, got %s", next);
71 } 53 }
72 54
73 // If there is an argument left, try to parse as direction 55 // If there is an argument left, try to parse as direction
@@ -95,7 +77,7 @@ char *gesture_parse(const char *input, struct gesture *output) {
95 } else if (strcmp(item, "counterclockwise") == 0) { 77 } else if (strcmp(item, "counterclockwise") == 0) {
96 output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; 78 output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
97 } else { 79 } else {
98 return strformat("expected direction, got %s", item); 80 return format_str("expected direction, got %s", item);
99 } 81 }
100 } 82 }
101 list_free_items_and_destroy(directions); 83 list_free_items_and_destroy(directions);
@@ -163,7 +145,7 @@ static char *gesture_directions_to_string(uint32_t directions) {
163 if (!result) { 145 if (!result) {
164 result = strdup(name); 146 result = strdup(name);
165 } else { 147 } else {
166 char *new = strformat("%s+%s", result, name); 148 char *new = format_str("%s+%s", result, name);
167 free(result); 149 free(result);
168 result = new; 150 result = new;
169 } 151 }
@@ -179,7 +161,7 @@ static char *gesture_directions_to_string(uint32_t directions) {
179 161
180char *gesture_to_string(struct gesture *gesture) { 162char *gesture_to_string(struct gesture *gesture) {
181 char *directions = gesture_directions_to_string(gesture->directions); 163 char *directions = gesture_directions_to_string(gesture->directions);
182 char *result = strformat("%s:%u:%s", 164 char *result = format_str("%s:%u:%s",
183 gesture_type_string(gesture->type), 165 gesture_type_string(gesture->type),
184 gesture->fingers, directions); 166 gesture->fingers, directions);
185 free(directions); 167 free(directions);
diff --git a/common/ipc-client.c b/common/ipc-client.c
index d30212d2..a0be2b2d 100644
--- a/common/ipc-client.c
+++ b/common/ipc-client.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <stdint.h> 2#include <stdint.h>
4#include <stdlib.h> 3#include <stdlib.h>
diff --git a/common/log.c b/common/log.c
index 483420e7..3eacdb34 100644
--- a/common/log.c
+++ b/common/log.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200112L
2#include <signal.h> 1#include <signal.h>
3#include <stdarg.h> 2#include <stdarg.h>
4#include <stdio.h> 3#include <stdio.h>
diff --git a/common/loop.c b/common/loop.c
index 80fe18ea..b99c6d55 100644
--- a/common/loop.c
+++ b/common/loop.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200112L
2#include <limits.h> 1#include <limits.h>
3#include <string.h> 2#include <string.h>
4#include <stdbool.h> 3#include <stdbool.h>
diff --git a/common/meson.build b/common/meson.build
index 3756075a..c0ce1f68 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -1,7 +1,6 @@
1lib_sway_common = static_library( 1lib_sway_common = static_library(
2 'sway-common', 2 'sway-common',
3 files( 3 files(
4 'background-image.c',
5 'cairo.c', 4 'cairo.c',
6 'gesture.c', 5 'gesture.c',
7 'ipc-client.c', 6 'ipc-client.c',
@@ -14,7 +13,6 @@ lib_sway_common = static_library(
14 ), 13 ),
15 dependencies: [ 14 dependencies: [
16 cairo, 15 cairo,
17 gdk_pixbuf,
18 pango, 16 pango,
19 pangocairo, 17 pangocairo,
20 wayland_client.partial_dependency(compile_args: true) 18 wayland_client.partial_dependency(compile_args: true)
diff --git a/common/pango.c b/common/pango.c
index e04bf80f..288569b3 100644
--- a/common/pango.c
+++ b/common/pango.c
@@ -84,18 +84,11 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width,
84 int *baseline, double scale, bool markup, const char *fmt, ...) { 84 int *baseline, double scale, bool markup, const char *fmt, ...) {
85 va_list args; 85 va_list args;
86 va_start(args, fmt); 86 va_start(args, fmt);
87 // Add one since vsnprintf excludes null terminator. 87 char *buf = vformat_str(fmt, args);
88 int length = vsnprintf(NULL, 0, fmt, args) + 1;
89 va_end(args); 88 va_end(args);
90
91 char *buf = malloc(length);
92 if (buf == NULL) { 89 if (buf == NULL) {
93 sway_log(SWAY_ERROR, "Failed to allocate memory");
94 return; 90 return;
95 } 91 }
96 va_start(args, fmt);
97 vsnprintf(buf, length, fmt, args);
98 va_end(args);
99 92
100 PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); 93 PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
101 pango_cairo_update_layout(cairo, layout); 94 pango_cairo_update_layout(cairo, layout);
@@ -104,6 +97,7 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width,
104 *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; 97 *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
105 } 98 }
106 g_object_unref(layout); 99 g_object_unref(layout);
100
107 free(buf); 101 free(buf);
108} 102}
109 103
@@ -125,18 +119,11 @@ void render_text(cairo_t *cairo, const PangoFontDescription *desc,
125 double scale, bool markup, const char *fmt, ...) { 119 double scale, bool markup, const char *fmt, ...) {
126 va_list args; 120 va_list args;
127 va_start(args, fmt); 121 va_start(args, fmt);
128 // Add one since vsnprintf excludes null terminator. 122 char *buf = vformat_str(fmt, args);
129 int length = vsnprintf(NULL, 0, fmt, args) + 1;
130 va_end(args); 123 va_end(args);
131
132 char *buf = malloc(length);
133 if (buf == NULL) { 124 if (buf == NULL) {
134 sway_log(SWAY_ERROR, "Failed to allocate memory");
135 return; 125 return;
136 } 126 }
137 va_start(args, fmt);
138 vsnprintf(buf, length, fmt, args);
139 va_end(args);
140 127
141 PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); 128 PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
142 cairo_font_options_t *fo = cairo_font_options_create(); 129 cairo_font_options_t *fo = cairo_font_options_create();
@@ -146,5 +133,6 @@ void render_text(cairo_t *cairo, const PangoFontDescription *desc,
146 pango_cairo_update_layout(cairo, layout); 133 pango_cairo_update_layout(cairo, layout);
147 pango_cairo_show_layout(cairo, layout); 134 pango_cairo_show_layout(cairo, layout);
148 g_object_unref(layout); 135 g_object_unref(layout);
136
149 free(buf); 137 free(buf);
150} 138}
diff --git a/common/stringop.c b/common/stringop.c
index 7fb3fe12..16d04917 100644
--- a/common/stringop.c
+++ b/common/stringop.c
@@ -1,5 +1,5 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
2#include <stdarg.h>
3#include <stdbool.h> 3#include <stdbool.h>
4#include <stdio.h> 4#include <stdio.h>
5#include <stdlib.h> 5#include <stdlib.h>
@@ -328,3 +328,35 @@ bool expand_path(char **path) {
328 wordfree(&p); 328 wordfree(&p);
329 return true; 329 return true;
330} 330}
331
332char *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
351out:
352 va_end(args_copy);
353 return str;
354}
355
356char *format_str(const char *fmt, ...) {
357 va_list args;
358 va_start(args, fmt);
359 char *str = vformat_str(fmt, args);
360 va_end(args);
361 return str;
362}
diff --git a/common/util.c b/common/util.c
index 5d4c0673..7c492bcb 100644
--- a/common/util.c
+++ b/common/util.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <fcntl.h> 2#include <fcntl.h>
4#include <math.h> 3#include <math.h>
diff --git a/completions/meson.build b/completions/meson.build
new file mode 100644
index 00000000..6bca9391
--- /dev/null
+++ b/completions/meson.build
@@ -0,0 +1,57 @@
1if 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)
9endif
10
11if 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)
33endif
34
35if 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)
57endif
diff --git a/config.in b/config.in
index 3eda7ac7..a5173165 100644
--- a/config.in
+++ b/config.in
@@ -18,7 +18,7 @@ set $term foot
18# Your preferred application launcher 18# Your preferred application launcher
19# Note: pass the final command to swaymsg so that the resulting window can be opened 19# Note: pass the final command to swaymsg so that the resulting window can be opened
20# on the original workspace that the command was run on. 20# on the original workspace that the command was run on.
21set $menu dmenu_path | dmenu | xargs swaymsg exec -- 21set $menu dmenu_path | wmenu | xargs swaymsg exec --
22 22
23### Output configuration 23### Output configuration
24# 24#
@@ -205,7 +205,7 @@ bar {
205 205
206 # When the status_command prints a new line to stdout, swaybar updates. 206 # When the status_command prints a new line to stdout, swaybar updates.
207 # The default just shows the current date and time. 207 # The default just shows the current date and time.
208 status_command while date +'%Y-%m-%d %I:%M:%S %p'; do sleep 1; done 208 status_command while date +'%Y-%m-%d %X'; do sleep 1; done
209 209
210 colors { 210 colors {
211 statusline #ffffff 211 statusline #ffffff
diff --git a/contrib/autoname-workspaces.py b/contrib/autoname-workspaces.py
deleted file mode 100755
index 3ec39928..00000000
--- a/contrib/autoname-workspaces.py
+++ /dev/null
@@ -1,124 +0,0 @@
1#!/usr/bin/python
2
3# This script requires i3ipc-python package (install it from a system package manager
4# or pip).
5# It adds icons to the workspace name for each open window.
6# Set your keybindings like this: set $workspace1 workspace number 1
7# Add your icons to WINDOW_ICONS.
8# Based on https://github.com/maximbaz/dotfiles/blob/master/bin/i3-autoname-workspaces
9
10import argparse
11import i3ipc
12import logging
13import re
14import signal
15import sys
16
17WINDOW_ICONS = {
18 "firefox": "",
19}
20
21DEFAULT_ICON = "󰀏"
22
23
24def icon_for_window(window):
25 name = None
26 if window.app_id is not None and len(window.app_id) > 0:
27 name = window.app_id.lower()
28 elif window.window_class is not None and len(window.window_class) > 0:
29 name = window.window_class.lower()
30
31 if name in WINDOW_ICONS:
32 return WINDOW_ICONS[name]
33
34 logging.info("No icon available for window with name: %s" % str(name))
35 return DEFAULT_ICON
36
37def rename_workspaces(ipc):
38 for workspace in ipc.get_tree().workspaces():
39 name_parts = parse_workspace_name(workspace.name)
40 icon_tuple = ()
41 for w in workspace:
42 if w.app_id is not None or w.window_class is not None:
43 icon = icon_for_window(w)
44 if not ARGUMENTS.duplicates and icon in icon_tuple:
45 continue
46 icon_tuple += (icon,)
47 name_parts["icons"] = " ".join(icon_tuple) + " "
48 new_name = construct_workspace_name(name_parts)
49 ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name))
50
51
52def undo_window_renaming(ipc):
53 for workspace in ipc.get_tree().workspaces():
54 name_parts = parse_workspace_name(workspace.name)
55 name_parts["icons"] = None
56 new_name = construct_workspace_name(name_parts)
57 ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name))
58 ipc.main_quit()
59 sys.exit(0)
60
61
62def parse_workspace_name(name):
63 return re.match(
64 "(?P<num>[0-9]+):?(?P<shortname>\w+)? ?(?P<icons>.+)?", name
65 ).groupdict()
66
67
68def construct_workspace_name(parts):
69 new_name = str(parts["num"])
70 if parts["shortname"] or parts["icons"]:
71 new_name += ":"
72
73 if parts["shortname"]:
74 new_name += parts["shortname"]
75
76 if parts["icons"]:
77 new_name += " " + parts["icons"]
78
79 return new_name
80
81
82if __name__ == "__main__":
83 parser = argparse.ArgumentParser(
84 description="This script automatically changes the workspace name in sway depending on your open applications."
85 )
86 parser.add_argument(
87 "--duplicates",
88 "-d",
89 action="store_true",
90 help="Set it when you want an icon for each instance of the same application per workspace.",
91 )
92 parser.add_argument(
93 "--logfile",
94 "-l",
95 type=str,
96 default="/tmp/sway-autoname-workspaces.log",
97 help="Path for the logfile.",
98 )
99 args = parser.parse_args()
100 global ARGUMENTS
101 ARGUMENTS = args
102
103 logging.basicConfig(
104 level=logging.INFO,
105 filename=ARGUMENTS.logfile,
106 filemode="w",
107 format="%(message)s",
108 )
109
110 ipc = i3ipc.Connection()
111
112 for sig in [signal.SIGINT, signal.SIGTERM]:
113 signal.signal(sig, lambda signal, frame: undo_window_renaming(ipc))
114
115 def window_event_handler(ipc, e):
116 if e.change in ["new", "close", "move"]:
117 rename_workspaces(ipc)
118
119 ipc.on("window", window_event_handler)
120
121 rename_workspaces(ipc)
122
123 ipc.main()
124
diff --git a/contrib/grimshot b/contrib/grimshot
deleted file mode 100755
index 1ec19def..00000000
--- a/contrib/grimshot
+++ /dev/null
@@ -1,168 +0,0 @@
1#!/bin/sh
2
3## Grimshot: a helper for screenshots within sway
4## Requirements:
5## - `grim`: screenshot utility for wayland
6## - `slurp`: to select an area
7## - `swaymsg`: to read properties of current window
8## - `wl-copy`: clipboard utility
9## - `jq`: json utility to parse swaymsg output
10## - `notify-send`: to show notifications
11## Those are needed to be installed, if unsure, run `grimshot check`
12##
13## See `man 1 grimshot` or `grimshot usage` for further details.
14
15getTargetDirectory() {
16 test -f "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" && \
17 . "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs"
18
19 echo "${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}}"
20}
21
22NOTIFY=no
23CURSOR=
24
25while [ $# -gt 0 ]; do
26 key="$1"
27
28 case $key in
29 -n|--notify)
30 NOTIFY=yes
31 shift # past argument
32 ;;
33 -c|--cursor)
34 CURSOR=yes
35 shift # past argument
36 ;;
37 *) # unknown option
38 break # done with parsing --flags
39 ;;
40 esac
41done
42
43ACTION=${1:-usage}
44SUBJECT=${2:-screen}
45FILE=${3:-$(getTargetDirectory)/$(date -Ins).png}
46
47if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then
48 echo "Usage:"
49 echo " grimshot [--notify] [--cursor] (copy|save) [active|screen|output|area|window] [FILE|-]"
50 echo " grimshot check"
51 echo " grimshot usage"
52 echo ""
53 echo "Commands:"
54 echo " copy: Copy the screenshot data into the clipboard."
55 echo " save: Save the screenshot to a regular file or '-' to pipe to STDOUT."
56 echo " check: Verify if required tools are installed and exit."
57 echo " usage: Show this message and exit."
58 echo ""
59 echo "Targets:"
60 echo " active: Currently active window."
61 echo " screen: All visible outputs."
62 echo " output: Currently active output."
63 echo " area: Manually select a region."
64 echo " window: Manually select a window."
65 exit
66fi
67
68notify() {
69 notify-send -t 3000 -a grimshot "$@"
70}
71notifyOk() {
72 [ "$NOTIFY" = "no" ] && return
73
74 TITLE=${2:-"Screenshot"}
75 MESSAGE=${1:-"OK"}
76 notify "$TITLE" "$MESSAGE"
77}
78notifyError() {
79 if [ $NOTIFY = "yes" ]; then
80 TITLE=${2:-"Screenshot"}
81 MESSAGE=${1:-"Error taking screenshot with grim"}
82 notify -u critical "$TITLE" "$MESSAGE"
83 else
84 echo "$1"
85 fi
86}
87
88die() {
89 MSG=${1:-Bye}
90 notifyError "Error: $MSG"
91 exit 2
92}
93
94check() {
95 COMMAND=$1
96 if command -v "$COMMAND" > /dev/null 2>&1; then
97 RESULT="OK"
98 else
99 RESULT="NOT FOUND"
100 fi
101 echo " $COMMAND: $RESULT"
102}
103
104takeScreenshot() {
105 FILE=$1
106 GEOM=$2
107 OUTPUT=$3
108 if [ -n "$OUTPUT" ]; then
109 grim ${CURSOR:+-c} -o "$OUTPUT" "$FILE" || die "Unable to invoke grim"
110 elif [ -z "$GEOM" ]; then
111 grim ${CURSOR:+-c} "$FILE" || die "Unable to invoke grim"
112 else
113 grim ${CURSOR:+-c} -g "$GEOM" "$FILE" || die "Unable to invoke grim"
114 fi
115}
116
117if [ "$ACTION" = "check" ] ; then
118 echo "Checking if required tools are installed. If something is missing, install it to your system and make it available in PATH..."
119 check grim
120 check slurp
121 check swaymsg
122 check wl-copy
123 check jq
124 check notify-send
125 exit
126elif [ "$SUBJECT" = "area" ] ; then
127 GEOM=$(slurp -d)
128 # Check if user exited slurp without selecting the area
129 if [ -z "$GEOM" ]; then
130 exit 1
131 fi
132 WHAT="Area"
133elif [ "$SUBJECT" = "active" ] ; then
134 FOCUSED=$(swaymsg -t get_tree | jq -r 'recurse(.nodes[]?, .floating_nodes[]?) | select(.focused)')
135 GEOM=$(echo "$FOCUSED" | jq -r '.rect | "\(.x),\(.y) \(.width)x\(.height)"')
136 APP_ID=$(echo "$FOCUSED" | jq -r '.app_id')
137 WHAT="$APP_ID window"
138elif [ "$SUBJECT" = "screen" ] ; then
139 GEOM=""
140 WHAT="Screen"
141elif [ "$SUBJECT" = "output" ] ; then
142 GEOM=""
143 OUTPUT=$(swaymsg -t get_outputs | jq -r '.[] | select(.focused)' | jq -r '.name')
144 WHAT="$OUTPUT"
145elif [ "$SUBJECT" = "window" ] ; then
146 GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp)
147 # Check if user exited slurp without selecting the area
148 if [ -z "$GEOM" ]; then
149 exit 1
150 fi
151 WHAT="Window"
152else
153 die "Unknown subject to take a screen shot from" "$SUBJECT"
154fi
155
156if [ "$ACTION" = "copy" ] ; then
157 takeScreenshot - "$GEOM" "$OUTPUT" | wl-copy --type image/png || die "Clipboard error"
158 notifyOk "$WHAT copied to buffer"
159else
160 if takeScreenshot "$FILE" "$GEOM" "$OUTPUT"; then
161 TITLE="Screenshot of $SUBJECT"
162 MESSAGE=$(basename "$FILE")
163 notifyOk "$MESSAGE" "$TITLE"
164 echo "$FILE"
165 else
166 notifyError "Error taking screenshot with grim"
167 fi
168fi
diff --git a/contrib/grimshot.1 b/contrib/grimshot.1
deleted file mode 100644
index 2c4c6a95..00000000
--- a/contrib/grimshot.1
+++ /dev/null
@@ -1,109 +0,0 @@
1.\" Generated by scdoc 1.11.2
2.\" Complete documentation for this program is not available as a GNU info page
3.ie \n(.g .ds Aq \(aq
4.el .ds Aq '
5.nh
6.ad l
7.\" Begin generated content:
8.TH "grimshot" "1" "2022-03-31"
9.P
10.SH NAME
11.P
12grimshot - a helper for screenshots within sway
13.P
14.SH SYNOPSIS
15.P
16\fBgrimshot\fR [--notify] [--cursor] (copy|save) [TARGET] [FILE]
17.br
18\fBgrimshot\fR check
19.br
20\fBgrimshot\fR usage
21.P
22.SH OPTIONS
23.P
24\fB--notify\fR
25.RS 4
26Show notifications to the user that a screenshot has been taken.\&
27.P
28.RE
29\fB--cursor\fR
30.RS 4
31Include cursors in the screenshot.\&
32.P
33.RE
34\fBsave\fR
35.RS 4
36Save the screenshot into a regular file.\& Grimshot will write images
37files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined
38in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\&
39Set FILE to '\&-'\& to pipe the output to STDOUT.\&
40.P
41.RE
42\fBcopy\fR
43.RS 4
44Copy the screenshot data (as image/png) into the clipboard.\&
45.P
46.RE
47.SH DESCRIPTION
48.P
49Grimshot is an easy-to-use screenshot utility for sway.\& It provides a
50convenient interface over grim, slurp and jq, and supports storing the
51screenshot either directly to the clipboard using wl-copy or to a file.\&
52.P
53.SH EXAMPLES
54.P
55An example usage pattern is to add these bindings to your sway config:
56.P
57.nf
58.RS 4
59# Screenshots:
60# Super+P: Current window
61# Super+Shift+p: Select area
62# Super+Alt+p Current output
63# Super+Ctrl+p Select a window
64
65bindsym Mod4+p exec grimshot save active
66bindsym Mod4+Shift+p exec grimshot save area
67bindsym Mod4+Mod1+p exec grimshot save output
68bindsym Mod4+Ctrl+p exec grimshot save window
69.fi
70.RE
71.P
72.SH TARGETS
73.P
74grimshot can capture the following named targets:
75.P
76\fIactive\fR
77.RS 4
78Captures the currently active window.\&
79.P
80.RE
81\fIscreen\fR
82.RS 4
83Captures the entire screen.\& This includes all visible outputs.\&
84.P
85.RE
86\fIarea\fR
87.RS 4
88Allows manually selecting a rectangular region, and captures that.\&
89.P
90.RE
91\fIwindow\fR
92.RS 4
93Allows manually selecting a single window (by clicking on it), and
94captures it.\&
95.P
96.RE
97\fIoutput\fR
98.RS 4
99Captures the currently active output.\&
100.P
101.RE
102.SH OUTPUT
103.P
104Grimshot will print the filename of the captured screenshot to stdout if called
105with the \fIsave\fR subcommand.\&
106.P
107.SH SEE ALSO
108.P
109\fBgrim\fR(1)
diff --git a/contrib/grimshot.1.scd b/contrib/grimshot.1.scd
deleted file mode 100644
index e356f99d..00000000
--- a/contrib/grimshot.1.scd
+++ /dev/null
@@ -1,80 +0,0 @@
1grimshot(1)
2
3# NAME
4
5grimshot - a helper for screenshots within sway
6
7# SYNOPSIS
8
9*grimshot* [--notify] [--cursor] (copy|save) [TARGET] [FILE]++
10*grimshot* check++
11*grimshot* usage
12
13# OPTIONS
14
15*--notify*
16 Show notifications to the user that a screenshot has been taken.
17
18*--cursor*
19 Include cursors in the screenshot.
20
21*save*
22 Save the screenshot into a regular file. Grimshot will write image
23 files to *XDG_SCREENSHOTS_DIR* if this is set (or defined
24 in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*.
25 Set FILE to '-' to pipe the output to STDOUT.
26
27*copy*
28 Copy the screenshot data (as image/png) into the clipboard.
29
30# DESCRIPTION
31
32Grimshot is an easy-to-use screenshot utility for sway. It provides a
33convenient interface over grim, slurp and jq, and supports storing the
34screenshot either directly to the clipboard using wl-copy or to a file.
35
36# EXAMPLES
37
38An example usage pattern is to add these bindings to your sway config:
39
40```
41# Screenshots:
42# Super+P: Current window
43# Super+Shift+p: Select area
44# Super+Alt+p Current output
45# Super+Ctrl+p Select a window
46
47bindsym Mod4+p exec grimshot save active
48bindsym Mod4+Shift+p exec grimshot save area
49bindsym Mod4+Mod1+p exec grimshot save output
50bindsym Mod4+Ctrl+p exec grimshot save window
51```
52
53# TARGETS
54
55grimshot can capture the following named targets:
56
57_active_
58 Captures the currently active window.
59
60_screen_
61 Captures the entire screen. This includes all visible outputs.
62
63_area_
64 Allows manually selecting a rectangular region, and captures that.
65
66_window_
67 Allows manually selecting a single window (by clicking on it), and
68 captures it.
69
70_output_
71 Captures the currently active output.
72
73# OUTPUT
74
75Grimshot will print the filename of the captured screenshot to stdout if called
76with the _save_ subcommand.
77
78# SEE ALSO
79
80*grim*(1)
diff --git a/contrib/inactive-windows-transparency.py b/contrib/inactive-windows-transparency.py
deleted file mode 100755
index b81134dd..00000000
--- a/contrib/inactive-windows-transparency.py
+++ /dev/null
@@ -1,69 +0,0 @@
1#!/usr/bin/python
2
3# This script requires i3ipc-python package (install it from a system package manager
4# or pip).
5# It makes inactive windows transparent. Use `transparency_val` variable to control
6# transparency strength in range of 0…1 or use the command line argument -o.
7
8import argparse
9import i3ipc
10import signal
11import sys
12from functools import partial
13
14def on_window_focus(inactive_opacity, ipc, event):
15 global prev_focused
16 global prev_workspace
17
18 focused_workspace = ipc.get_tree().find_focused()
19
20 if focused_workspace == None:
21 return
22
23 focused = event.container
24 workspace = focused_workspace.workspace().num
25
26 if focused.id != prev_focused.id: # https://github.com/swaywm/sway/issues/2859
27 focused.command("opacity 1")
28 if workspace == prev_workspace:
29 prev_focused.command("opacity " + inactive_opacity)
30 prev_focused = focused
31 prev_workspace = workspace
32
33
34def remove_opacity(ipc):
35 for workspace in ipc.get_tree().workspaces():
36 for w in workspace:
37 w.command("opacity 1")
38 ipc.main_quit()
39 sys.exit(0)
40
41
42if __name__ == "__main__":
43 transparency_val = "0.80"
44
45 parser = argparse.ArgumentParser(
46 description="This script allows you to set the transparency of unfocused windows in sway."
47 )
48 parser.add_argument(
49 "--opacity",
50 "-o",
51 type=str,
52 default=transparency_val,
53 help="set opacity value in range 0...1",
54 )
55 args = parser.parse_args()
56
57 ipc = i3ipc.Connection()
58 prev_focused = None
59 prev_workspace = ipc.get_tree().find_focused().workspace().num
60
61 for window in ipc.get_tree():
62 if window.focused:
63 prev_focused = window
64 else:
65 window.command("opacity " + args.opacity)
66 for sig in [signal.SIGINT, signal.SIGTERM]:
67 signal.signal(sig, lambda signal, frame: remove_opacity(ipc))
68 ipc.on("window::focus", partial(on_window_focus, args.opacity))
69 ipc.main() \ No newline at end of file
diff --git a/include/background-image.h b/include/background-image.h
deleted file mode 100644
index a97ef375..00000000
--- a/include/background-image.h
+++ /dev/null
@@ -1,20 +0,0 @@
1#ifndef _SWAY_BACKGROUND_IMAGE_H
2#define _SWAY_BACKGROUND_IMAGE_H
3#include "cairo_util.h"
4
5enum 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
15enum background_mode parse_background_mode(const char *mode);
16cairo_surface_t *load_background_image(const char *path);
17void render_background_image(cairo_t *cairo, cairo_surface_t *image,
18 enum background_mode mode, int buffer_width, int buffer_height);
19
20#endif
diff --git a/include/pango.h b/include/pango.h
index 1db113c2..228e39cf 100644
--- a/include/pango.h
+++ b/include/pango.h
@@ -5,6 +5,7 @@
5#include <stdint.h> 5#include <stdint.h>
6#include <cairo.h> 6#include <cairo.h>
7#include <pango/pangocairo.h> 7#include <pango/pangocairo.h>
8#include "stringop.h"
8 9
9/** 10/**
10 * Utility function which escape characters a & < > ' ". 11 * Utility function which escape characters a & < > ' ".
@@ -16,9 +17,9 @@ size_t escape_markup_text(const char *src, char *dest);
16PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, 17PangoLayout *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);
18void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, 19void 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);
20void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline); 21void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline);
21void render_text(cairo_t *cairo, PangoFontDescription *desc, 22void render_text(cairo_t *cairo, PangoFontDescription *desc,
22 double scale, bool markup, const char *fmt, ...); 23 double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(5, 6);
23 24
24#endif 25#endif
diff --git a/include/stringop.h b/include/stringop.h
index b29f59b2..19a50f23 100644
--- a/include/stringop.h
+++ b/include/stringop.h
@@ -5,6 +5,12 @@
5#include <stddef.h> 5#include <stddef.h>
6#include "list.h" 6#include "list.h"
7 7
8#ifdef __GNUC__
9#define _SWAY_ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end)))
10#else
11#define _SWAY_ATTRIB_PRINTF(start, end)
12#endif
13
8void strip_whitespace(char *str); 14void strip_whitespace(char *str);
9void strip_quotes(char *str); 15void strip_quotes(char *str);
10 16
@@ -31,4 +37,7 @@ char *argsep(char **stringp, const char *delim, char *matched_delim);
31// Expand a path using shell replacements such as $HOME and ~ 37// Expand a path using shell replacements such as $HOME and ~
32bool expand_path(char **path); 38bool expand_path(char **path);
33 39
40char *vformat_str(const char *fmt, va_list args) _SWAY_ATTRIB_PRINTF(1, 0);
41char *format_str(const char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2);
42
34#endif 43#endif
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 013a7b82..27058587 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -3,13 +3,14 @@
3 3
4#include <wlr/util/edges.h> 4#include <wlr/util/edges.h>
5#include "config.h" 5#include "config.h"
6#include "stringop.h"
6 7
7struct sway_container; 8struct sway_container;
8 9
9typedef struct cmd_results *sway_cmd(int argc, char **argv); 10typedef struct cmd_results *sway_cmd(int argc, char **argv);
10 11
11struct cmd_handler { 12struct cmd_handler {
12 char *command; 13 const char *command;
13 sway_cmd *handle; 14 sway_cmd *handle;
14}; 15};
15 16
@@ -46,7 +47,7 @@ enum expected_args {
46struct cmd_results *checkarg(int argc, const char *name, 47struct cmd_results *checkarg(int argc, const char *name,
47 enum expected_args type, int val); 48 enum expected_args type, int val);
48 49
49const struct cmd_handler *find_handler(char *line, 50const struct cmd_handler *find_handler(const char *line,
50 const struct cmd_handler *cmd_handlers, size_t handlers_size); 51 const struct cmd_handler *cmd_handlers, size_t handlers_size);
51 52
52/** 53/**
@@ -76,7 +77,7 @@ struct cmd_results *config_commands_command(char *exec);
76/** 77/**
77 * Allocates a cmd_results object. 78 * Allocates a cmd_results object.
78 */ 79 */
79struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...); 80struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...) _SWAY_ATTRIB_PRINTF(2, 3);
80/** 81/**
81 * Frees a cmd_results object. 82 * Frees a cmd_results object.
82 */ 83 */
@@ -159,12 +160,11 @@ sway_cmd cmd_new_float;
159sway_cmd cmd_new_window; 160sway_cmd cmd_new_window;
160sway_cmd cmd_nop; 161sway_cmd cmd_nop;
161sway_cmd cmd_opacity; 162sway_cmd cmd_opacity;
162sway_cmd cmd_new_float;
163sway_cmd cmd_new_window;
164sway_cmd cmd_no_focus; 163sway_cmd cmd_no_focus;
165sway_cmd cmd_output; 164sway_cmd cmd_output;
166sway_cmd cmd_permit; 165sway_cmd cmd_permit;
167sway_cmd cmd_popup_during_fullscreen; 166sway_cmd cmd_popup_during_fullscreen;
167sway_cmd cmd_primary_selection;
168sway_cmd cmd_reject; 168sway_cmd cmd_reject;
169sway_cmd cmd_reload; 169sway_cmd cmd_reload;
170sway_cmd cmd_rename; 170sway_cmd cmd_rename;
@@ -252,6 +252,7 @@ sway_cmd input_cmd_click_method;
252sway_cmd input_cmd_drag; 252sway_cmd input_cmd_drag;
253sway_cmd input_cmd_drag_lock; 253sway_cmd input_cmd_drag_lock;
254sway_cmd input_cmd_dwt; 254sway_cmd input_cmd_dwt;
255sway_cmd input_cmd_dwtp;
255sway_cmd input_cmd_events; 256sway_cmd input_cmd_events;
256sway_cmd input_cmd_left_handed; 257sway_cmd input_cmd_left_handed;
257sway_cmd input_cmd_map_from_region; 258sway_cmd input_cmd_map_from_region;
@@ -260,10 +261,12 @@ sway_cmd input_cmd_map_to_region;
260sway_cmd input_cmd_middle_emulation; 261sway_cmd input_cmd_middle_emulation;
261sway_cmd input_cmd_natural_scroll; 262sway_cmd input_cmd_natural_scroll;
262sway_cmd input_cmd_pointer_accel; 263sway_cmd input_cmd_pointer_accel;
264sway_cmd input_cmd_rotation_angle;
263sway_cmd input_cmd_scroll_factor; 265sway_cmd input_cmd_scroll_factor;
264sway_cmd input_cmd_repeat_delay; 266sway_cmd input_cmd_repeat_delay;
265sway_cmd input_cmd_repeat_rate; 267sway_cmd input_cmd_repeat_rate;
266sway_cmd input_cmd_scroll_button; 268sway_cmd input_cmd_scroll_button;
269sway_cmd input_cmd_scroll_button_lock;
267sway_cmd input_cmd_scroll_method; 270sway_cmd input_cmd_scroll_method;
268sway_cmd input_cmd_tap; 271sway_cmd input_cmd_tap;
269sway_cmd input_cmd_tap_button_map; 272sway_cmd input_cmd_tap_button_map;
@@ -294,6 +297,7 @@ sway_cmd output_cmd_scale_filter;
294sway_cmd output_cmd_subpixel; 297sway_cmd output_cmd_subpixel;
295sway_cmd output_cmd_toggle; 298sway_cmd output_cmd_toggle;
296sway_cmd output_cmd_transform; 299sway_cmd output_cmd_transform;
300sway_cmd output_cmd_unplug;
297 301
298sway_cmd seat_cmd_attach; 302sway_cmd seat_cmd_attach;
299sway_cmd seat_cmd_cursor; 303sway_cmd seat_cmd_cursor;
diff --git a/include/sway/config.h b/include/sway/config.h
index 68c06846..0be1cd22 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -12,6 +12,7 @@
12#include "../include/config.h" 12#include "../include/config.h"
13#include "gesture.h" 13#include "gesture.h"
14#include "list.h" 14#include "list.h"
15#include "stringop.h"
15#include "swaynag.h" 16#include "swaynag.h"
16#include "tree/container.h" 17#include "tree/container.h"
17#include "sway/input/tablet.h" 18#include "sway/input/tablet.h"
@@ -150,14 +151,17 @@ struct input_config {
150 int drag; 151 int drag;
151 int drag_lock; 152 int drag_lock;
152 int dwt; 153 int dwt;
154 int dwtp;
153 int left_handed; 155 int left_handed;
154 int middle_emulation; 156 int middle_emulation;
155 int natural_scroll; 157 int natural_scroll;
156 float pointer_accel; 158 float pointer_accel;
159 float rotation_angle;
157 float scroll_factor; 160 float scroll_factor;
158 int repeat_delay; 161 int repeat_delay;
159 int repeat_rate; 162 int repeat_rate;
160 int scroll_button; 163 int scroll_button;
164 int scroll_button_lock;
161 int scroll_method; 165 int scroll_method;
162 int send_events; 166 int send_events;
163 int tap; 167 int tap;
@@ -288,6 +292,14 @@ struct output_config {
288}; 292};
289 293
290/** 294/**
295 * An output config pre-matched to an output
296 */
297struct matched_output_config {
298 struct sway_output *output;
299 struct output_config *config;
300};
301
302/**
291 * Stores size of gaps for each side 303 * Stores size of gaps for each side
292 */ 304 */
293struct side_gaps { 305struct side_gaps {
@@ -529,6 +541,7 @@ struct sway_config {
529 bool auto_back_and_forth; 541 bool auto_back_and_forth;
530 bool show_marks; 542 bool show_marks;
531 enum alignment title_align; 543 enum alignment title_align;
544 bool primary_selection;
532 545
533 bool tiling_drag; 546 bool tiling_drag;
534 int tiling_drag_threshold; 547 int tiling_drag_threshold;
@@ -622,7 +635,7 @@ void run_deferred_bindings(void);
622/** 635/**
623 * Adds a warning entry to the swaynag instance used for errors. 636 * Adds a warning entry to the swaynag instance used for errors.
624 */ 637 */
625void config_add_swaynag_warning(char *fmt, ...); 638void config_add_swaynag_warning(char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2);
626 639
627/** 640/**
628 * Free config struct 641 * Free config struct
@@ -675,20 +688,22 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt
675 688
676struct output_config *new_output_config(const char *name); 689struct output_config *new_output_config(const char *name);
677 690
678void merge_output_config(struct output_config *dst, struct output_config *src); 691bool apply_output_configs(struct matched_output_config *configs,
679 692 size_t configs_len, bool test_only);
680bool apply_output_config(struct output_config *oc, struct sway_output *output);
681 693
682bool test_output_config(struct output_config *oc, struct sway_output *output); 694void apply_all_output_configs(void);
683 695
684struct output_config *store_output_config(struct output_config *oc); 696/**
697 * store_output_config stores a new output config. An output may be matched by
698 * three different config types, in order of precedence: Identifier, name and
699 * wildcard. When storing a config type of lower precedence, assume that the
700 * user wants the config to take immediate effect by superseding (clearing) the
701 * same values from higher presedence configuration.
702 */
703void store_output_config(struct output_config *oc);
685 704
686struct output_config *find_output_config(struct sway_output *output); 705struct output_config *find_output_config(struct sway_output *output);
687 706
688void apply_output_config_to_outputs(struct output_config *oc);
689
690void reset_outputs(void);
691
692void free_output_config(struct output_config *oc); 707void free_output_config(struct output_config *oc);
693 708
694bool spawn_swaybg(void); 709bool spawn_swaybg(void);
@@ -718,7 +733,7 @@ void free_workspace_config(struct workspace_config *wsc);
718/** 733/**
719 * Updates the value of config->font_height based on the metrics for title's 734 * Updates the value of config->font_height based on the metrics for title's
720 * font as reported by pango. 735 * font as reported by pango.
721 * 736 *
722 * If the height has changed, all containers will be rearranged to take on the 737 * If the height has changed, all containers will be rearranged to take on the
723 * new size. 738 * new size.
724 */ 739 */
diff --git a/include/sway/criteria.h b/include/sway/criteria.h
index 59f57f94..8da345ea 100644
--- a/include/sway/criteria.h
+++ b/include/sway/criteria.h
@@ -43,6 +43,7 @@ struct criteria {
43 struct pattern *window_role; 43 struct pattern *window_role;
44 enum atom_name window_type; 44 enum atom_name window_type;
45#endif 45#endif
46 bool all;
46 bool floating; 47 bool floating;
47 bool tiling; 48 bool tiling;
48 char urgent; // 'l' for latest or 'o' for oldest 49 char urgent; // 'l' for latest or 'o' for oldest
diff --git a/include/sway/desktop.h b/include/sway/desktop.h
deleted file mode 100644
index 7f2f5b3e..00000000
--- a/include/sway/desktop.h
+++ /dev/null
@@ -1,13 +0,0 @@
1#include <wlr/types/wlr_compositor.h>
2
3struct sway_container;
4struct sway_view;
5
6void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
7 bool whole);
8
9void desktop_damage_whole_container(struct sway_container *con);
10
11void desktop_damage_box(struct wlr_box *box);
12
13void desktop_damage_view(struct sway_view *view);
diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h
index 58d54c68..84cc666d 100644
--- a/include/sway/desktop/idle_inhibit_v1.h
+++ b/include/sway/desktop/idle_inhibit_v1.h
@@ -1,8 +1,6 @@
1#ifndef _SWAY_DESKTOP_IDLE_INHIBIT_V1_H 1#ifndef _SWAY_DESKTOP_IDLE_INHIBIT_V1_H
2#define _SWAY_DESKTOP_IDLE_INHIBIT_V1_H 2#define _SWAY_DESKTOP_IDLE_INHIBIT_V1_H
3#include <wlr/types/wlr_idle_inhibit_v1.h> 3#include <wlr/types/wlr_idle_inhibit_v1.h>
4#include <wlr/types/wlr_idle.h>
5#include "sway/server.h"
6 4
7enum sway_idle_inhibit_mode { 5enum sway_idle_inhibit_mode {
8 INHIBIT_IDLE_APPLICATION, // Application set inhibitor (when visible) 6 INHIBIT_IDLE_APPLICATION, // Application set inhibitor (when visible)
@@ -16,12 +14,9 @@ struct sway_idle_inhibit_manager_v1 {
16 struct wlr_idle_inhibit_manager_v1 *wlr_manager; 14 struct wlr_idle_inhibit_manager_v1 *wlr_manager;
17 struct wl_listener new_idle_inhibitor_v1; 15 struct wl_listener new_idle_inhibitor_v1;
18 struct wl_list inhibitors; 16 struct wl_list inhibitors;
19
20 struct wlr_idle *idle;
21}; 17};
22 18
23struct sway_idle_inhibitor_v1 { 19struct sway_idle_inhibitor_v1 {
24 struct sway_idle_inhibit_manager_v1 *manager;
25 struct wlr_idle_inhibitor_v1 *wlr_inhibitor; 20 struct wlr_idle_inhibitor_v1 *wlr_inhibitor;
26 struct sway_view *view; 21 struct sway_view *view;
27 enum sway_idle_inhibit_mode mode; 22 enum sway_idle_inhibit_mode mode;
@@ -33,8 +28,7 @@ struct sway_idle_inhibitor_v1 {
33bool sway_idle_inhibit_v1_is_active( 28bool sway_idle_inhibit_v1_is_active(
34 struct sway_idle_inhibitor_v1 *inhibitor); 29 struct sway_idle_inhibitor_v1 *inhibitor);
35 30
36void sway_idle_inhibit_v1_check_active( 31void sway_idle_inhibit_v1_check_active(void);
37 struct sway_idle_inhibit_manager_v1 *manager);
38 32
39void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, 33void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view,
40 enum sway_idle_inhibit_mode mode); 34 enum sway_idle_inhibit_mode mode);
@@ -48,6 +42,6 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_vi
48void sway_idle_inhibit_v1_user_inhibitor_destroy( 42void sway_idle_inhibit_v1_user_inhibitor_destroy(
49 struct sway_idle_inhibitor_v1 *inhibitor); 43 struct sway_idle_inhibitor_v1 *inhibitor);
50 44
51struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( 45bool sway_idle_inhibit_manager_v1_init(void);
52 struct wl_display *wl_display, struct wlr_idle *idle); 46
53#endif 47#endif
diff --git a/include/sway/desktop/launcher.h b/include/sway/desktop/launcher.h
new file mode 100644
index 00000000..412068a9
--- /dev/null
+++ b/include/sway/desktop/launcher.h
@@ -0,0 +1,40 @@
1#ifndef _SWAY_LAUNCHER_H
2#define _SWAY_LAUNCHER_H
3
4#include <stdlib.h>
5#include <wayland-server-core.h>
6#include "sway/input/seat.h"
7
8struct 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
25struct launcher_ctx *launcher_ctx_find_pid(pid_t pid);
26
27struct sway_workspace *launcher_ctx_get_workspace(struct launcher_ctx *ctx);
28
29void launcher_ctx_consume(struct launcher_ctx *ctx);
30
31void launcher_ctx_destroy(struct launcher_ctx *ctx);
32
33struct launcher_ctx *launcher_ctx_create_internal(void);
34
35struct launcher_ctx *launcher_ctx_create(
36 struct wlr_xdg_activation_token_v1 *token, struct sway_node *node);
37
38const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx);
39
40#endif
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h
index 7dd58ba8..dd7edb7a 100644
--- a/include/sway/desktop/transaction.h
+++ b/include/sway/desktop/transaction.h
@@ -1,6 +1,8 @@
1#ifndef _SWAY_TRANSACTION_H 1#ifndef _SWAY_TRANSACTION_H
2#define _SWAY_TRANSACTION_H 2#define _SWAY_TRANSACTION_H
3#include <stdint.h> 3#include <stdint.h>
4#include <stdbool.h>
5#include <wlr/types/wlr_scene.h>
4 6
5/** 7/**
6 * Transactions enable us to perform atomic layout updates. 8 * Transactions enable us to perform atomic layout updates.
@@ -38,8 +40,11 @@ void transaction_commit_dirty_client(void);
38 * Notify the transaction system that a view is ready for the new layout. 40 * Notify the transaction system that a view is ready for the new layout.
39 * 41 *
40 * When all views in the transaction are ready, the layout will be applied. 42 * When all views in the transaction are ready, the layout will be applied.
43 *
44 * A success boolean is returned denoting that this part of the transaction is
45 * ready.
41 */ 46 */
42void transaction_notify_view_ready_by_serial(struct sway_view *view, 47bool transaction_notify_view_ready_by_serial(struct sway_view *view,
43 uint32_t serial); 48 uint32_t serial);
44 49
45/** 50/**
@@ -47,8 +52,13 @@ void transaction_notify_view_ready_by_serial(struct sway_view *view,
47 * identifying the instruction by geometry rather than by serial. 52 * identifying the instruction by geometry rather than by serial.
48 * 53 *
49 * This is used by xwayland views, as they don't have serials. 54 * This is used by xwayland views, as they don't have serials.
55 *
56 * A success boolean is returned denoting that this part of the transaction is
57 * ready.
50 */ 58 */
51void transaction_notify_view_ready_by_geometry(struct sway_view *view, 59bool transaction_notify_view_ready_by_geometry(struct sway_view *view,
52 double x, double y, int width, int height); 60 double x, double y, int width, int height);
53 61
62void arrange_popups(struct wlr_scene_tree *popups);
63
54#endif 64#endif
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index 8a2898dd..527d0350 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -35,7 +35,6 @@ struct sway_cursor {
35 pixman_region32_t confine; // invalid if active_constraint == NULL 35 pixman_region32_t confine; // invalid if active_constraint == NULL
36 bool active_confine_requires_warp; 36 bool active_confine_requires_warp;
37 37
38 struct wlr_pointer_gestures_v1 *pointer_gestures;
39 struct wl_listener hold_begin; 38 struct wl_listener hold_begin;
40 struct wl_listener hold_end; 39 struct wl_listener hold_end;
41 struct wl_listener pinch_begin; 40 struct wl_listener pinch_begin;
@@ -53,6 +52,7 @@ struct sway_cursor {
53 52
54 struct wl_listener touch_down; 53 struct wl_listener touch_down;
55 struct wl_listener touch_up; 54 struct wl_listener touch_up;
55 struct wl_listener touch_cancel;
56 struct wl_listener touch_motion; 56 struct wl_listener touch_motion;
57 struct wl_listener touch_frame; 57 struct wl_listener touch_frame;
58 bool simulating_pointer_from_touch; 58 bool simulating_pointer_from_touch;
@@ -64,6 +64,7 @@ struct sway_cursor {
64 struct wl_listener tool_proximity; 64 struct wl_listener tool_proximity;
65 struct wl_listener tool_button; 65 struct wl_listener tool_button;
66 bool simulating_pointer_from_tool_tip; 66 bool simulating_pointer_from_tool_tip;
67 bool simulating_pointer_from_tool_button;
67 uint32_t tool_buttons; 68 uint32_t tool_buttons;
68 69
69 struct wl_listener request_set_cursor; 70 struct wl_listener request_set_cursor;
@@ -107,9 +108,13 @@ void cursor_unhide(struct sway_cursor *cursor);
107int cursor_get_timeout(struct sway_cursor *cursor); 108int cursor_get_timeout(struct sway_cursor *cursor);
108void cursor_notify_key_press(struct sway_cursor *cursor); 109void cursor_notify_key_press(struct sway_cursor *cursor);
109 110
111void 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
110void dispatch_cursor_button(struct sway_cursor *cursor, 115void dispatch_cursor_button(struct sway_cursor *cursor,
111 struct wlr_input_device *device, uint32_t time_msec, uint32_t button, 116 struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
112 enum wlr_button_state state); 117 enum wl_pointer_button_state state);
113 118
114void dispatch_cursor_axis(struct sway_cursor *cursor, 119void dispatch_cursor_axis(struct sway_cursor *cursor,
115 struct wlr_pointer_axis_event *event); 120 struct wlr_pointer_axis_event *event);
@@ -140,4 +145,6 @@ uint32_t get_mouse_button(const char *name, char **error);
140 145
141const char *get_mouse_button_name(uint32_t button); 146const char *get_mouse_button_name(uint32_t button);
142 147
148void handle_request_set_cursor_shape(struct wl_listener *listener, void *data);
149
143#endif 150#endif
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index c9bd08f0..45c75199 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -1,10 +1,10 @@
1#ifndef _SWAY_INPUT_INPUT_MANAGER_H 1#ifndef _SWAY_INPUT_INPUT_MANAGER_H
2#define _SWAY_INPUT_INPUT_MANAGER_H 2#define _SWAY_INPUT_INPUT_MANAGER_H
3#include <libinput.h> 3#include <libinput.h>
4#include <wlr/types/wlr_input_inhibitor.h>
5#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> 4#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
6#include <wlr/types/wlr_virtual_keyboard_v1.h> 5#include <wlr/types/wlr_virtual_keyboard_v1.h>
7#include <wlr/types/wlr_virtual_pointer_v1.h> 6#include <wlr/types/wlr_virtual_pointer_v1.h>
7#include <wlr/types/wlr_transient_seat_v1.h>
8#include "sway/server.h" 8#include "sway/server.h"
9#include "sway/config.h" 9#include "sway/config.h"
10#include "list.h" 10#include "list.h"
@@ -21,10 +21,11 @@ struct sway_input_manager {
21 struct wl_list devices; 21 struct wl_list devices;
22 struct wl_list seats; 22 struct wl_list seats;
23 23
24 struct wlr_input_inhibit_manager *inhibit;
25 struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; 24 struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit;
26 struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; 25 struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard;
27 struct wlr_virtual_pointer_manager_v1 *virtual_pointer; 26 struct wlr_virtual_pointer_manager_v1 *virtual_pointer;
27 struct wlr_pointer_gestures_v1 *pointer_gestures;
28 struct wlr_transient_seat_manager_v1 *transient_seat_manager;
28 29
29 struct wl_listener new_input; 30 struct wl_listener new_input;
30 struct wl_listener inhibit_activate; 31 struct wl_listener inhibit_activate;
@@ -32,6 +33,7 @@ struct sway_input_manager {
32 struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor; 33 struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor;
33 struct wl_listener virtual_keyboard_new; 34 struct wl_listener virtual_keyboard_new;
34 struct wl_listener virtual_pointer_new; 35 struct wl_listener virtual_pointer_new;
36 struct wl_listener transient_seat_create;
35}; 37};
36 38
37struct sway_input_manager *input_manager_create(struct sway_server *server); 39struct sway_input_manager *input_manager_create(struct sway_server *server);
@@ -44,7 +46,7 @@ void input_manager_configure_xcursor(void);
44 46
45void input_manager_apply_input_config(struct input_config *input_config); 47void input_manager_apply_input_config(struct input_config *input_config);
46 48
47void input_manager_configure_all_inputs(void); 49void input_manager_configure_all_input_mappings(void);
48 50
49void input_manager_reset_input(struct sway_input_device *input_device); 51void input_manager_reset_input(struct sway_input_device *input_device);
50 52
diff --git a/include/sway/input/libinput.h b/include/sway/input/libinput.h
index 890d632e..1f84a8e3 100644
--- a/include/sway/input/libinput.h
+++ b/include/sway/input/libinput.h
@@ -2,7 +2,10 @@
2#define _SWAY_INPUT_LIBINPUT_H 2#define _SWAY_INPUT_LIBINPUT_H
3#include "sway/input/input-manager.h" 3#include "sway/input/input-manager.h"
4 4
5void sway_input_configure_libinput_device(struct sway_input_device *device); 5bool sway_input_configure_libinput_device(struct sway_input_device *device);
6
7void sway_input_configure_libinput_device_send_events(
8 struct sway_input_device *device);
6 9
7void sway_input_reset_libinput_device(struct sway_input_device *device); 10void sway_input_reset_libinput_device(struct sway_input_device *device);
8 11
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index c2041742..428f9679 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -3,7 +3,9 @@
3 3
4#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> 4#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
5#include <wlr/types/wlr_layer_shell_v1.h> 5#include <wlr/types/wlr_layer_shell_v1.h>
6#include <wlr/types/wlr_scene.h>
6#include <wlr/types/wlr_seat.h> 7#include <wlr/types/wlr_seat.h>
8#include <wlr/types/wlr_touch.h>
7#include <wlr/util/edges.h> 9#include <wlr/util/edges.h>
8#include "sway/config.h" 10#include "sway/config.h"
9#include "sway/input/input-manager.h" 11#include "sway/input/input-manager.h"
@@ -15,7 +17,7 @@ struct sway_seat;
15struct sway_seatop_impl { 17struct sway_seatop_impl {
16 void (*button)(struct sway_seat *seat, uint32_t time_msec, 18 void (*button)(struct sway_seat *seat, uint32_t time_msec,
17 struct wlr_input_device *device, uint32_t button, 19 struct wlr_input_device *device, uint32_t button,
18 enum wlr_button_state state); 20 enum wl_pointer_button_state state);
19 void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); 21 void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec);
20 void (*pointer_axis)(struct sway_seat *seat, 22 void (*pointer_axis)(struct sway_seat *seat,
21 struct wlr_pointer_axis_event *event); 23 struct wlr_pointer_axis_event *event);
@@ -36,14 +38,20 @@ struct sway_seatop_impl {
36 void (*swipe_end)(struct sway_seat *seat, 38 void (*swipe_end)(struct sway_seat *seat,
37 struct wlr_pointer_swipe_end_event *event); 39 struct wlr_pointer_swipe_end_event *event);
38 void (*rebase)(struct sway_seat *seat, uint32_t time_msec); 40 void (*rebase)(struct sway_seat *seat, uint32_t time_msec);
41 void (*touch_motion)(struct sway_seat *seat,
42 struct wlr_touch_motion_event *event, double lx, double ly);
43 void (*touch_up)(struct sway_seat *seat,
44 struct wlr_touch_up_event *event);
45 void (*touch_down)(struct sway_seat *seat,
46 struct wlr_touch_down_event *event, double lx, double ly);
47 void (*touch_cancel)(struct sway_seat *seat,
48 struct wlr_touch_cancel_event *event);
39 void (*tablet_tool_motion)(struct sway_seat *seat, 49 void (*tablet_tool_motion)(struct sway_seat *seat,
40 struct sway_tablet_tool *tool, uint32_t time_msec); 50 struct sway_tablet_tool *tool, uint32_t time_msec);
41 void (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool, 51 void (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool,
42 uint32_t time_msec, enum wlr_tablet_tool_tip_state state); 52 uint32_t time_msec, enum wlr_tablet_tool_tip_state state);
43 void (*end)(struct sway_seat *seat); 53 void (*end)(struct sway_seat *seat);
44 void (*unref)(struct sway_seat *seat, struct sway_container *con); 54 void (*unref)(struct sway_seat *seat, struct sway_container *con);
45 void (*render)(struct sway_seat *seat, struct sway_output *output,
46 pixman_region32_t *damage);
47 bool allow_set_cursor; 55 bool allow_set_cursor;
48}; 56};
49 57
@@ -66,19 +74,6 @@ struct sway_seat_node {
66 struct wl_listener destroy; 74 struct wl_listener destroy;
67}; 75};
68 76
69struct sway_drag_icon {
70 struct sway_seat *seat;
71 struct wlr_drag_icon *wlr_drag_icon;
72 struct wl_list link; // sway_root::drag_icons
73
74 double x, y; // in layout-local coordinates
75
76 struct wl_listener surface_commit;
77 struct wl_listener map;
78 struct wl_listener unmap;
79 struct wl_listener destroy;
80};
81
82struct sway_drag { 77struct sway_drag {
83 struct sway_seat *seat; 78 struct sway_seat *seat;
84 struct wlr_drag *wlr_drag; 79 struct wlr_drag *wlr_drag;
@@ -89,16 +84,23 @@ struct sway_seat {
89 struct wlr_seat *wlr_seat; 84 struct wlr_seat *wlr_seat;
90 struct sway_cursor *cursor; 85 struct sway_cursor *cursor;
91 86
87 // Seat scene tree structure
88 // - scene_tree
89 // - drag icons
90 // - drag icon 1
91 // - drag icon 2
92 // - seatop specific stuff
93 struct wlr_scene_tree *scene_tree;
94 struct wlr_scene_tree *drag_icons;
95
92 bool has_focus; 96 bool has_focus;
93 struct wl_list focus_stack; // list of containers in focus order 97 struct wl_list focus_stack; // list of containers in focus order
94 struct sway_workspace *workspace; 98 struct sway_workspace *workspace;
95 char *prev_workspace_name; // for workspace back_and_forth 99 char *prev_workspace_name; // for workspace back_and_forth
96 100
97 // If the focused layer is set, views cannot receive keyboard focus
98 struct wlr_layer_surface_v1 *focused_layer; 101 struct wlr_layer_surface_v1 *focused_layer;
99 102 // If the exclusive layer is set, views cannot receive keyboard focus
100 // If exclusive_client is set, no other clients will receive input events 103 bool has_exclusive_layer;
101 struct wl_client *exclusive_client;
102 104
103 // Last touch point 105 // Last touch point
104 int32_t touch_id; 106 int32_t touch_id;
@@ -122,6 +124,7 @@ struct sway_seat {
122 struct wl_listener start_drag; 124 struct wl_listener start_drag;
123 struct wl_listener request_set_selection; 125 struct wl_listener request_set_selection;
124 struct wl_listener request_set_primary_selection; 126 struct wl_listener request_set_primary_selection;
127 struct wl_listener destroy;
125 128
126 struct wl_list devices; // sway_seat_device::link 129 struct wl_list devices; // sway_seat_device::link
127 struct wl_list keyboard_groups; // sway_keyboard_group::link 130 struct wl_list keyboard_groups; // sway_keyboard_group::link
@@ -157,6 +160,9 @@ void seat_add_device(struct sway_seat *seat,
157void seat_configure_device(struct sway_seat *seat, 160void seat_configure_device(struct sway_seat *seat,
158 struct sway_input_device *device); 161 struct sway_input_device *device);
159 162
163void seat_configure_device_mapping(struct sway_seat *seat,
164 struct sway_input_device *input_device);
165
160void seat_reset_device(struct sway_seat *seat, 166void seat_reset_device(struct sway_seat *seat,
161 struct sway_input_device *input_device); 167 struct sway_input_device *input_device);
162 168
@@ -187,8 +193,7 @@ void seat_set_focus_surface(struct sway_seat *seat,
187void seat_set_focus_layer(struct sway_seat *seat, 193void seat_set_focus_layer(struct sway_seat *seat,
188 struct wlr_layer_surface_v1 *layer); 194 struct wlr_layer_surface_v1 *layer);
189 195
190void seat_set_exclusive_client(struct sway_seat *seat, 196void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client);
191 struct wl_client *client);
192 197
193struct sway_node *seat_get_focus(struct sway_seat *seat); 198struct sway_node *seat_get_focus(struct sway_seat *seat);
194 199
@@ -201,10 +206,6 @@ struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat);
201 206
202struct sway_container *seat_get_focused_container(struct sway_seat *seat); 207struct sway_container *seat_get_focused_container(struct sway_seat *seat);
203 208
204// Force focus to a particular surface that is not part of the workspace
205// hierarchy (used for lockscreen)
206void sway_force_focus(struct wlr_surface *surface);
207
208/** 209/**
209 * Return the last container to be focused for the seat (or the most recently 210 * Return the last container to be focused for the seat (or the most recently
210 * opened if no container has received focused) that is a child of the given 211 * opened if no container has received focused) that is a child of the given
@@ -251,7 +252,7 @@ void seat_idle_notify_activity(struct sway_seat *seat,
251 252
252bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); 253bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface);
253 254
254void drag_icon_update_position(struct sway_drag_icon *icon); 255void drag_icons_update_position(struct sway_seat *seat);
255 256
256enum wlr_edges find_resize_edge(struct sway_container *cont, 257enum wlr_edges find_resize_edge(struct sway_container *cont,
257 struct wlr_surface *surface, struct sway_cursor *cursor); 258 struct wlr_surface *surface, struct sway_cursor *cursor);
@@ -259,10 +260,13 @@ enum wlr_edges find_resize_edge(struct sway_container *cont,
259void seatop_begin_default(struct sway_seat *seat); 260void seatop_begin_default(struct sway_seat *seat);
260 261
261void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 262void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
262 uint32_t time_msec, double sx, double sy); 263 double sx, double sy);
263 264
264void seatop_begin_down_on_surface(struct sway_seat *seat, 265void seatop_begin_down_on_surface(struct sway_seat *seat,
265 struct wlr_surface *surface, uint32_t time_msec, double sx, double sy); 266 struct wlr_surface *surface, double sx, double sy);
267
268void seatop_begin_touch_down(struct sway_seat *seat, struct wlr_surface *surface,
269 struct wlr_touch_down_event *event, double sx, double sy, double lx, double ly);
266 270
267void seatop_begin_move_floating(struct sway_seat *seat, 271void seatop_begin_move_floating(struct sway_seat *seat,
268 struct sway_container *con); 272 struct sway_container *con);
@@ -283,13 +287,13 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
283 struct sway_workspace *workspace); 287 struct sway_workspace *workspace);
284 288
285void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, 289void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,
286 uint32_t button, enum wlr_button_state state); 290 uint32_t button, enum wl_pointer_button_state state);
287 291
288void seat_consider_warp_to_focus(struct sway_seat *seat); 292void seat_consider_warp_to_focus(struct sway_seat *seat);
289 293
290void seatop_button(struct sway_seat *seat, uint32_t time_msec, 294void seatop_button(struct sway_seat *seat, uint32_t time_msec,
291 struct wlr_input_device *device, uint32_t button, 295 struct wlr_input_device *device, uint32_t button,
292 enum wlr_button_state state); 296 enum wl_pointer_button_state state);
293 297
294void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); 298void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec);
295 299
@@ -322,6 +326,18 @@ void seatop_swipe_update(struct sway_seat *seat,
322void seatop_swipe_end(struct sway_seat *seat, 326void seatop_swipe_end(struct sway_seat *seat,
323 struct wlr_pointer_swipe_end_event *event); 327 struct wlr_pointer_swipe_end_event *event);
324 328
329void seatop_touch_motion(struct sway_seat *seat,
330 struct wlr_touch_motion_event *event, double lx, double ly);
331
332void seatop_touch_up(struct sway_seat *seat,
333 struct wlr_touch_up_event *event);
334
335void seatop_touch_down(struct sway_seat *seat,
336 struct wlr_touch_down_event *event, double lx, double ly);
337
338void seatop_touch_cancel(struct sway_seat *seat,
339 struct wlr_touch_cancel_event *event);
340
325void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); 341void seatop_rebase(struct sway_seat *seat, uint32_t time_msec);
326 342
327/** 343/**
@@ -336,13 +352,6 @@ void seatop_end(struct sway_seat *seat);
336 */ 352 */
337void seatop_unref(struct sway_seat *seat, struct sway_container *con); 353void seatop_unref(struct sway_seat *seat, struct sway_container *con);
338 354
339/**
340 * Instructs a seatop to render anything that it needs to render
341 * (eg. dropzone for move-tiling)
342 */
343void seatop_render(struct sway_seat *seat, struct sway_output *output,
344 pixman_region32_t *damage);
345
346bool seatop_allows_set_cursor(struct sway_seat *seat); 355bool seatop_allows_set_cursor(struct sway_seat *seat);
347 356
348/** 357/**
diff --git a/include/sway/input/tablet.h b/include/sway/input/tablet.h
index c0a5aff7..2fa5db6d 100644
--- a/include/sway/input/tablet.h
+++ b/include/sway/input/tablet.h
@@ -63,7 +63,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad);
63 63
64void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad); 64void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad);
65 65
66void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, 66void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad,
67 struct wlr_surface *surface); 67 struct wlr_surface *surface);
68 68
69#endif 69#endif
diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h
index c70fd935..1993f928 100644
--- a/include/sway/input/text_input.h
+++ b/include/sway/input/text_input.h
@@ -4,12 +4,11 @@
4#include <wlr/types/wlr_text_input_v3.h> 4#include <wlr/types/wlr_text_input_v3.h>
5#include <wlr/types/wlr_input_method_v2.h> 5#include <wlr/types/wlr_input_method_v2.h>
6#include <wlr/types/wlr_compositor.h> 6#include <wlr/types/wlr_compositor.h>
7#include "sway/input/seat.h"
8 7
9/** 8/**
10 * The relay structure manages the relationship between text-input and 9 * The relay structure manages the relationship between text-input and
11 * input_method interfaces on a given seat. Multiple text-input interfaces may 10 * input_method interfaces on a given seat. Multiple text-input interfaces may
12 * be bound to a relay, but at most one will be focused (reveiving events) at 11 * be bound to a relay, but at most one will be focused (receiving events) at
13 * a time. At most one input-method interface may be bound to the seat. The 12 * a time. At most one input-method interface may be bound to the seat. The
14 * relay manages life cycle of both sides. When both sides are present and 13 * relay manages life cycle of both sides. When both sides are present and
15 * focused, the relay passes messages between them. 14 * focused, the relay passes messages between them.
@@ -22,18 +21,21 @@ struct sway_input_method_relay {
22 struct sway_seat *seat; 21 struct sway_seat *seat;
23 22
24 struct wl_list text_inputs; // sway_text_input::link 23 struct wl_list text_inputs; // sway_text_input::link
24 struct wl_list input_popups; // sway_input_popup::link
25 struct wlr_input_method_v2 *input_method; // doesn't have to be present 25 struct wlr_input_method_v2 *input_method; // doesn't have to be present
26 26
27 struct wl_listener text_input_new; 27 struct wl_listener text_input_new;
28 28
29 struct wl_listener input_method_new; 29 struct wl_listener input_method_new;
30 struct wl_listener input_method_commit; 30 struct wl_listener input_method_commit;
31 struct wl_listener input_method_new_popup_surface;
31 struct wl_listener input_method_grab_keyboard; 32 struct wl_listener input_method_grab_keyboard;
32 struct wl_listener input_method_destroy; 33 struct wl_listener input_method_destroy;
33 34
34 struct wl_listener input_method_keyboard_grab_destroy; 35 struct wl_listener input_method_keyboard_grab_destroy;
35}; 36};
36 37
38
37struct sway_text_input { 39struct sway_text_input {
38 struct sway_input_method_relay *relay; 40 struct sway_input_method_relay *relay;
39 41
diff --git a/include/sway/input/text_input_popup.h b/include/sway/input/text_input_popup.h
new file mode 100644
index 00000000..e5f6ab8b
--- /dev/null
+++ b/include/sway/input/text_input_popup.h
@@ -0,0 +1,20 @@
1#ifndef _SWAY_INPUT_TEXT_INPUT_POPUP_H
2#define _SWAY_INPUT_TEXT_INPUT_POPUP_H
3
4#include "sway/tree/view.h"
5
6struct 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);
9json_object *ipc_json_get_binding_mode(void); 10json_object *ipc_json_get_binding_mode(void);
10 11
11json_object *ipc_json_describe_disabled_output(struct sway_output *o); 12json_object *ipc_json_describe_disabled_output(struct sway_output *o);
13json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *o);
12json_object *ipc_json_describe_node(struct sway_node *node); 14json_object *ipc_json_describe_node(struct sway_node *node);
13json_object *ipc_json_describe_node_recursive(struct sway_node *node); 15json_object *ipc_json_describe_node_recursive(struct sway_node *node);
14json_object *ipc_json_describe_input(struct sway_input_device *device); 16json_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);
21void ipc_event_shutdown(const char *reason); 21void ipc_event_shutdown(const char *reason);
22void ipc_event_binding(struct sway_binding *binding); 22void ipc_event_binding(struct sway_binding *binding);
23void ipc_event_input(const char *change, struct sway_input_device *device); 23void ipc_event_input(const char *change, struct sway_input_device *device);
24void ipc_event_output(void);
24 25
25#endif 26#endif
diff --git a/include/sway/layers.h b/include/sway/layers.h
index f8508493..fd6384e0 100644
--- a/include/sway/layers.h
+++ b/include/sway/layers.h
@@ -3,61 +3,42 @@
3#include <stdbool.h> 3#include <stdbool.h>
4#include <wlr/types/wlr_compositor.h> 4#include <wlr/types/wlr_compositor.h>
5#include <wlr/types/wlr_layer_shell_v1.h> 5#include <wlr/types/wlr_layer_shell_v1.h>
6 6#include "sway/tree/view.h"
7enum layer_parent {
8 LAYER_PARENT_LAYER,
9 LAYER_PARENT_POPUP,
10};
11 7
12struct sway_layer_surface { 8struct sway_layer_surface {
13 struct wlr_layer_surface_v1 *layer_surface;
14 struct wl_list link;
15
16 struct wl_listener destroy;
17 struct wl_listener map; 9 struct wl_listener map;
18 struct wl_listener unmap; 10 struct wl_listener unmap;
19 struct wl_listener surface_commit; 11 struct wl_listener surface_commit;
20 struct wl_listener output_destroy; 12 struct wl_listener output_destroy;
13 struct wl_listener node_destroy;
21 struct wl_listener new_popup; 14 struct wl_listener new_popup;
22 struct wl_listener new_subsurface;
23 15
24 struct wlr_box geo;
25 bool mapped; 16 bool mapped;
26 struct wlr_box extent;
27 enum zwlr_layer_shell_v1_layer layer;
28 17
29 struct wl_list subsurfaces; 18 struct wlr_scene_tree *popups;
19 struct sway_popup_desc desc;
20
21 struct sway_output *output;
22 struct wlr_scene_layer_surface_v1 *scene;
23 struct wlr_scene_tree *tree;
24 struct wlr_layer_surface_v1 *layer_surface;
30}; 25};
31 26
32struct sway_layer_popup { 27struct sway_layer_popup {
33 struct wlr_xdg_popup *wlr_popup; 28 struct wlr_xdg_popup *wlr_popup;
34 enum layer_parent parent_type; 29 struct wlr_scene_tree *scene;
35 union { 30 struct sway_layer_surface *toplevel;
36 struct sway_layer_surface *parent_layer;
37 struct sway_layer_popup *parent_popup;
38 };
39 struct wl_listener map;
40 struct wl_listener unmap;
41 struct wl_listener destroy;
42 struct wl_listener commit;
43 struct wl_listener new_popup;
44};
45 31
46struct sway_layer_subsurface {
47 struct wlr_subsurface *wlr_subsurface;
48 struct sway_layer_surface *layer_surface;
49 struct wl_list link;
50
51 struct wl_listener map;
52 struct wl_listener unmap;
53 struct wl_listener destroy; 32 struct wl_listener destroy;
33 struct wl_listener new_popup;
54 struct wl_listener commit; 34 struct wl_listener commit;
55}; 35};
56 36
57struct sway_output; 37struct sway_output;
58void arrange_layers(struct sway_output *output);
59 38
60struct sway_layer_surface *layer_from_wlr_layer_surface_v1( 39struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
61 struct wlr_layer_surface_v1 *layer_surface); 40 struct wlr_surface *surface);
41
42void arrange_layers(struct sway_output *output);
62 43
63#endif 44#endif
diff --git a/include/sway/output.h b/include/sway/output.h
index 6d8319bf..d546d488 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -3,7 +3,9 @@
3#include <time.h> 3#include <time.h>
4#include <unistd.h> 4#include <unistd.h>
5#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/types/wlr_damage_ring.h>
6#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
8#include <wlr/types/wlr_scene.h>
7#include "config.h" 9#include "config.h"
8#include "sway/tree/node.h" 10#include "sway/tree/node.h"
9#include "sway/tree/view.h" 11#include "sway/tree/view.h"
@@ -18,34 +20,47 @@ struct sway_output_state {
18 20
19struct sway_output { 21struct sway_output {
20 struct sway_node node; 22 struct sway_node node;
23
24 struct {
25 struct wlr_scene_tree *shell_background;
26 struct wlr_scene_tree *shell_bottom;
27 struct wlr_scene_tree *tiling;
28 struct wlr_scene_tree *fullscreen;
29 struct wlr_scene_tree *shell_top;
30 struct wlr_scene_tree *shell_overlay;
31 struct wlr_scene_tree *session_lock;
32 } layers;
33
34 // when a container is fullscreen, in case the fullscreen surface is
35 // translucent (can see behind) we must make sure that the background is a
36 // solid color in order to conform to the wayland protocol. This rect
37 // ensures that when looking through a surface, all that will be seen
38 // is black.
39 struct wlr_scene_rect *fullscreen_background;
40
21 struct wlr_output *wlr_output; 41 struct wlr_output *wlr_output;
42 struct wlr_scene_output *scene_output;
22 struct sway_server *server; 43 struct sway_server *server;
23 struct wl_list link; 44 struct wl_list link;
24 45
25 struct wl_list layers[4]; // sway_layer_surface::link
26 struct wlr_box usable_area; 46 struct wlr_box usable_area;
27 47
28 struct timespec last_frame;
29 struct wlr_output_damage *damage;
30
31 int lx, ly; // layout coords 48 int lx, ly; // layout coords
32 int width, height; // transformed buffer size 49 int width, height; // transformed buffer size
33 enum wl_output_subpixel detected_subpixel; 50 enum wl_output_subpixel detected_subpixel;
34 enum scale_filter_mode scale_filter; 51 enum scale_filter_mode scale_filter;
35 // last applied mode when the output is powered off
36 struct wlr_output_mode *current_mode;
37 52
38 bool enabling, enabled; 53 bool enabled;
39 list_t *workspaces; 54 list_t *workspaces;
40 55
41 struct sway_output_state current; 56 struct sway_output_state current;
42 57
58 struct wl_listener layout_destroy;
43 struct wl_listener destroy; 59 struct wl_listener destroy;
44 struct wl_listener commit; 60 struct wl_listener commit;
45 struct wl_listener mode;
46 struct wl_listener present; 61 struct wl_listener present;
47 struct wl_listener damage_destroy; 62 struct wl_listener frame;
48 struct wl_listener damage_frame; 63 struct wl_listener request_state;
49 64
50 struct { 65 struct {
51 struct wl_signal disable; 66 struct wl_signal disable;
@@ -55,6 +70,13 @@ struct sway_output {
55 uint32_t refresh_nsec; 70 uint32_t refresh_nsec;
56 int max_render_time; // In milliseconds 71 int max_render_time; // In milliseconds
57 struct wl_event_source *repaint_timer; 72 struct wl_event_source *repaint_timer;
73 bool gamma_lut_changed;
74};
75
76struct sway_output_non_desktop {
77 struct wlr_output *wlr_output;
78
79 struct wl_listener destroy;
58}; 80};
59 81
60struct sway_output *output_create(struct wlr_output *wlr_output); 82struct sway_output *output_create(struct wlr_output *wlr_output);
@@ -75,18 +97,8 @@ typedef void (*sway_surface_iterator_func_t)(struct sway_output *output,
75 struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box, 97 struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box,
76 void *user_data); 98 void *user_data);
77 99
78void output_damage_whole(struct sway_output *output); 100bool output_match_name_or_id(struct sway_output *output,
79 101 const char *name_or_id);
80void output_damage_surface(struct sway_output *output, double ox, double oy,
81 struct wlr_surface *surface, bool whole);
82
83void output_damage_from_view(struct sway_output *output,
84 struct sway_view *view);
85
86void output_damage_box(struct sway_output *output, struct wlr_box *box);
87
88void output_damage_whole_container(struct sway_output *output,
89 struct sway_container *con);
90 102
91// this ONLY includes the enabled outputs 103// this ONLY includes the enabled outputs
92struct sway_output *output_by_name_or_id(const char *name_or_id); 104struct sway_output *output_by_name_or_id(const char *name_or_id);
@@ -100,47 +112,8 @@ void output_enable(struct sway_output *output);
100 112
101void output_disable(struct sway_output *output); 113void output_disable(struct sway_output *output);
102 114
103bool output_has_opaque_overlay_layer_surface(struct sway_output *output);
104
105struct sway_workspace *output_get_active_workspace(struct sway_output *output); 115struct sway_workspace *output_get_active_workspace(struct sway_output *output);
106 116
107void output_render(struct sway_output *output, struct timespec *when,
108 pixman_region32_t *damage);
109
110void output_surface_for_each_surface(struct sway_output *output,
111 struct wlr_surface *surface, double ox, double oy,
112 sway_surface_iterator_func_t iterator, void *user_data);
113
114void output_view_for_each_surface(struct sway_output *output,
115 struct sway_view *view, sway_surface_iterator_func_t iterator,
116 void *user_data);
117
118void output_view_for_each_popup_surface(struct sway_output *output,
119 struct sway_view *view, sway_surface_iterator_func_t iterator,
120 void *user_data);
121
122void output_layer_for_each_surface(struct sway_output *output,
123 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator,
124 void *user_data);
125
126void output_layer_for_each_toplevel_surface(struct sway_output *output,
127 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator,
128 void *user_data);
129
130void output_layer_for_each_popup_surface(struct sway_output *output,
131 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator,
132 void *user_data);
133
134#if HAVE_XWAYLAND
135void output_unmanaged_for_each_surface(struct sway_output *output,
136 struct wl_list *unmanaged, sway_surface_iterator_func_t iterator,
137 void *user_data);
138#endif
139
140void output_drag_icons_for_each_surface(struct sway_output *output,
141 struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
142 void *user_data);
143
144void output_for_each_workspace(struct sway_output *output, 117void output_for_each_workspace(struct sway_output *output,
145 void (*f)(struct sway_workspace *ws, void *data), void *data); 118 void (*f)(struct sway_workspace *ws, void *data), void *data);
146 119
@@ -158,18 +131,12 @@ void output_get_box(struct sway_output *output, struct wlr_box *box);
158enum sway_container_layout output_get_default_layout( 131enum sway_container_layout output_get_default_layout(
159 struct sway_output *output); 132 struct sway_output *output);
160 133
161void render_rect(struct sway_output *output,
162 pixman_region32_t *output_damage, const struct wlr_box *_box,
163 float color[static 4]);
164
165void premultiply_alpha(float color[4], float opacity);
166
167void scale_box(struct wlr_box *box, float scale);
168
169enum wlr_direction opposite_direction(enum wlr_direction d); 134enum wlr_direction opposite_direction(enum wlr_direction d);
170 135
171void handle_output_layout_change(struct wl_listener *listener, void *data); 136void handle_output_layout_change(struct wl_listener *listener, void *data);
172 137
138void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data);
139
173void handle_output_manager_apply(struct wl_listener *listener, void *data); 140void handle_output_manager_apply(struct wl_listener *listener, void *data);
174 141
175void handle_output_manager_test(struct wl_listener *listener, void *data); 142void handle_output_manager_test(struct wl_listener *listener, void *data);
@@ -177,4 +144,6 @@ void handle_output_manager_test(struct wl_listener *listener, void *data);
177void handle_output_power_manager_set_mode(struct wl_listener *listener, 144void handle_output_power_manager_set_mode(struct wl_listener *listener,
178 void *data); 145 void *data);
179 146
147struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output);
148
180#endif 149#endif
diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h
new file mode 100644
index 00000000..2649d7c2
--- /dev/null
+++ b/include/sway/scene_descriptor.h
@@ -0,0 +1,33 @@
1/**
2 * Across a wayland compositor, there are multiple shells: It can be
3 * a toplevel, or a layer_shell, or even something more meta like a drag
4 * icon or highlight indicators when dragging windows around.
5 *
6 * This object lets us store values that represent these modes of operation
7 * and keep track of what object is being represented.
8 */
9#ifndef _SWAY_SCENE_DESCRIPTOR_H
10#define _SWAY_SCENE_DESCRIPTOR_H
11#include <wlr/types/wlr_scene.h>
12
13enum 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
24bool scene_descriptor_assign(struct wlr_scene_node *node,
25 enum sway_scene_descriptor_type type, void *data);
26
27void *scene_descriptor_try_get(struct wlr_scene_node *node,
28 enum sway_scene_descriptor_type type);
29
30void scene_descriptor_destroy(struct wlr_scene_node *node,
31 enum sway_scene_descriptor_type type);
32
33#endif
diff --git a/include/sway/server.h b/include/sway/server.h
index 4cce17cc..c71851f6 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -2,45 +2,41 @@
2#define _SWAY_SERVER_H 2#define _SWAY_SERVER_H
3#include <stdbool.h> 3#include <stdbool.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/backend.h>
6#include <wlr/backend/session.h>
7#include <wlr/render/allocator.h>
8#include <wlr/render/wlr_renderer.h>
9#include <wlr/types/wlr_compositor.h>
10#include <wlr/types/wlr_data_device.h>
11#include <wlr/types/wlr_input_method_v2.h>
12#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
13#include <wlr/types/wlr_drm_lease_v1.h>
14#include <wlr/types/wlr_layer_shell_v1.h>
15#include <wlr/types/wlr_output_management_v1.h>
16#include <wlr/types/wlr_output_power_management_v1.h>
17#include <wlr/types/wlr_presentation_time.h>
18#include <wlr/types/wlr_relative_pointer_v1.h>
19#include <wlr/types/wlr_session_lock_v1.h>
20#include <wlr/types/wlr_server_decoration.h>
21#include <wlr/types/wlr_text_input_v3.h>
22#include <wlr/types/wlr_xdg_shell.h>
23#include "config.h" 5#include "config.h"
24#include "list.h" 6#include "list.h"
7#include "sway/desktop/idle_inhibit_v1.h"
25#if HAVE_XWAYLAND 8#if HAVE_XWAYLAND
26#include "sway/xwayland.h" 9#include "sway/xwayland.h"
27#endif 10#endif
28 11
29struct sway_transaction; 12struct sway_transaction;
30 13
14struct 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
31struct sway_server { 27struct sway_server {
32 struct wl_display *wl_display; 28 struct wl_display *wl_display;
33 struct wl_event_loop *wl_event_loop; 29 struct wl_event_loop *wl_event_loop;
34 const char *socket; 30 const char *socket;
35 31
36 struct wlr_backend *backend; 32 struct wlr_backend *backend;
33 struct wlr_session *session;
37 // secondary headless backend used for creating virtual outputs on-the-fly 34 // secondary headless backend used for creating virtual outputs on-the-fly
38 struct wlr_backend *headless_backend; 35 struct wlr_backend *headless_backend;
39 struct wlr_renderer *renderer; 36 struct wlr_renderer *renderer;
40 struct wlr_allocator *allocator; 37 struct wlr_allocator *allocator;
41 38
42 struct wlr_compositor *compositor; 39 struct wlr_compositor *compositor;
43 struct wl_listener compositor_new_surface;
44 40
45 struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; 41 struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1;
46 42
@@ -50,15 +46,16 @@ struct sway_server {
50 46
51 struct wl_listener new_output; 47 struct wl_listener new_output;
52 struct wl_listener output_layout_change; 48 struct wl_listener output_layout_change;
49 struct wl_listener renderer_lost;
53 50
54 struct wlr_idle *idle; 51 struct wlr_idle_notifier_v1 *idle_notifier_v1;
55 struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1; 52 struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1;
56 53
57 struct wlr_layer_shell_v1 *layer_shell; 54 struct wlr_layer_shell_v1 *layer_shell;
58 struct wl_listener layer_shell_surface; 55 struct wl_listener layer_shell_surface;
59 56
60 struct wlr_xdg_shell *xdg_shell; 57 struct wlr_xdg_shell *xdg_shell;
61 struct wl_listener xdg_shell_surface; 58 struct wl_listener xdg_shell_toplevel;
62 59
63 struct wlr_tablet_manager_v2 *tablet_v2; 60 struct wlr_tablet_manager_v2 *tablet_v2;
64 61
@@ -81,8 +78,6 @@ struct sway_server {
81 struct wlr_drm_lease_v1_manager *drm_lease_manager; 78 struct wlr_drm_lease_v1_manager *drm_lease_manager;
82 struct wl_listener drm_lease_request; 79 struct wl_listener drm_lease_request;
83 80
84 struct wlr_presentation *presentation;
85
86 struct wlr_pointer_constraints_v1 *pointer_constraints; 81 struct wlr_pointer_constraints_v1 *pointer_constraints;
87 struct wl_listener pointer_constraint; 82 struct wl_listener pointer_constraint;
88 83
@@ -90,15 +85,13 @@ struct sway_server {
90 struct wl_listener output_manager_apply; 85 struct wl_listener output_manager_apply;
91 struct wl_listener output_manager_test; 86 struct wl_listener output_manager_test;
92 87
88 struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1;
89 struct wl_listener gamma_control_set_gamma;
90
93 struct { 91 struct {
94 bool locked; 92 struct sway_session_lock *lock;
95 struct wlr_session_lock_manager_v1 *manager; 93 struct wlr_session_lock_manager_v1 *manager;
96 94
97 struct wlr_session_lock_v1 *lock;
98 struct wl_listener lock_new_surface;
99 struct wl_listener lock_unlock;
100 struct wl_listener lock_destroy;
101
102 struct wl_listener new_lock; 95 struct wl_listener new_lock;
103 struct wl_listener manager_destroy; 96 struct wl_listener manager_destroy;
104 } session_lock; 97 } session_lock;
@@ -107,10 +100,21 @@ struct sway_server {
107 struct wl_listener output_power_manager_set_mode; 100 struct wl_listener output_power_manager_set_mode;
108 struct wlr_input_method_manager_v2 *input_method; 101 struct wlr_input_method_manager_v2 *input_method;
109 struct wlr_text_input_manager_v3 *text_input; 102 struct wlr_text_input_manager_v3 *text_input;
103 struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list;
110 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; 104 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
105 struct wlr_content_type_manager_v1 *content_type_manager_v1;
106 struct wlr_data_control_manager_v1 *data_control_manager_v1;
107 struct wlr_screencopy_manager_v1 *screencopy_manager_v1;
108 struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1;
109 struct wlr_security_context_manager_v1 *security_context_manager_v1;
111 110
112 struct wlr_xdg_activation_v1 *xdg_activation_v1; 111 struct wlr_xdg_activation_v1 *xdg_activation_v1;
113 struct wl_listener xdg_activation_v1_request_activate; 112 struct wl_listener xdg_activation_v1_request_activate;
113 struct wl_listener xdg_activation_v1_new_token;
114
115 struct wl_listener request_set_cursor_shape;
116
117 struct wl_list pending_launcher_ctxs; // launcher_ctx::link
114 118
115 // The timeout for transactions, after which a transaction is applied 119 // The timeout for transactions, after which a transaction is applied
116 // regardless of readiness. 120 // regardless of readiness.
@@ -137,17 +141,13 @@ struct sway_debug {
137 bool noatomic; // Ignore atomic layout updates 141 bool noatomic; // Ignore atomic layout updates
138 bool txn_timings; // Log verbose messages about transactions 142 bool txn_timings; // Log verbose messages about transactions
139 bool txn_wait; // Always wait for the timeout before applying 143 bool txn_wait; // Always wait for the timeout before applying
140 bool noscanout; // Disable direct scan-out 144 bool legacy_wl_drm; // Enable the legacy wl_drm interface
141
142 enum {
143 DAMAGE_DEFAULT, // Default behaviour
144 DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged
145 DAMAGE_RERENDER, // Render the full output when any damage occurs
146 } damage;
147}; 145};
148 146
149extern struct sway_debug debug; 147extern struct sway_debug debug;
150 148
149extern bool allow_unsupported_gpu;
150
151bool server_init(struct sway_server *server); 151bool server_init(struct sway_server *server);
152void server_fini(struct sway_server *server); 152void server_fini(struct sway_server *server);
153bool server_start(struct sway_server *server); 153bool server_start(struct sway_server *server);
@@ -155,13 +155,16 @@ void server_run(struct sway_server *server);
155 155
156void restore_nofile_limit(void); 156void restore_nofile_limit(void);
157 157
158void handle_compositor_new_surface(struct wl_listener *listener, void *data);
159void handle_new_output(struct wl_listener *listener, void *data); 158void handle_new_output(struct wl_listener *listener, void *data);
160 159
161void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); 160void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data);
162void handle_layer_shell_surface(struct wl_listener *listener, void *data); 161void handle_layer_shell_surface(struct wl_listener *listener, void *data);
163void sway_session_lock_init(void); 162void sway_session_lock_init(void);
164void handle_xdg_shell_surface(struct wl_listener *listener, void *data); 163void sway_session_lock_add_output(struct sway_session_lock *lock,
164 struct sway_output *output);
165bool sway_session_lock_has_surface(struct sway_session_lock *lock,
166 struct wlr_surface *surface);
167void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data);
165#if HAVE_XWAYLAND 168#if HAVE_XWAYLAND
166void handle_xwayland_surface(struct wl_listener *listener, void *data); 169void handle_xwayland_surface(struct wl_listener *listener, void *data);
167#endif 170#endif
@@ -170,6 +173,8 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data);
170void handle_pointer_constraint(struct wl_listener *listener, void *data); 173void handle_pointer_constraint(struct wl_listener *listener, void *data);
171void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, 174void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
172 void *data); 175 void *data);
176void xdg_activation_v1_handle_new_token(struct wl_listener *listener,
177 void *data);
173 178
174void set_rr_scheduling(void); 179void set_rr_scheduling(void);
175 180
diff --git a/include/sway/surface.h b/include/sway/surface.h
deleted file mode 100644
index fb1cd775..00000000
--- a/include/sway/surface.h
+++ /dev/null
@@ -1,18 +0,0 @@
1#ifndef _SWAY_SURFACE_H
2#define _SWAY_SURFACE_H
3#include <wlr/types/wlr_compositor.h>
4
5struct 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
5struct 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
17struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent,
18 char *text, float color[4], bool pango_markup);
19
20void sway_text_node_set_color(struct sway_text_node *node, float color[4]);
21
22void sway_text_node_set_text(struct sway_text_node *node, char *text);
23
24void sway_text_node_set_max_width(struct sway_text_node *node, int max_width);
25
26void 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
5struct swaynag_instance { 6struct 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.
23void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, 24void 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
27void swaynag_show(struct swaynag_instance *swaynag); 28void swaynag_show(struct swaynag_instance *swaynag);
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 751612e2..93f6bfbb 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -3,6 +3,7 @@
3#include <stdint.h> 3#include <stdint.h>
4#include <sys/types.h> 4#include <sys/types.h>
5#include <wlr/types/wlr_compositor.h> 5#include <wlr/types/wlr_compositor.h>
6#include <wlr/types/wlr_scene.h>
6#include "list.h" 7#include "list.h"
7#include "sway/tree/node.h" 8#include "sway/tree/node.h"
8 9
@@ -68,11 +69,39 @@ struct sway_container {
68 struct sway_node node; 69 struct sway_node node;
69 struct sway_view *view; 70 struct sway_view *view;
70 71
72 struct wlr_scene_tree *scene_tree;
73
74 struct {
75 struct wlr_scene_tree *tree;
76
77 struct wlr_scene_tree *border;
78 struct wlr_scene_tree *background;
79
80 struct sway_text_node *title_text;
81 struct sway_text_node *marks_text;
82 } title_bar;
83
84 struct {
85 struct wlr_scene_tree *tree;
86
87 struct wlr_scene_rect *top;
88 struct wlr_scene_rect *bottom;
89 struct wlr_scene_rect *left;
90 struct wlr_scene_rect *right;
91 } border;
92
93 struct wlr_scene_tree *content_tree;
94 struct wlr_scene_buffer *output_handler;
95
96 struct wl_listener output_enter;
97 struct wl_listener output_leave;
98
71 struct sway_container_state current; 99 struct sway_container_state current;
72 struct sway_container_state pending; 100 struct sway_container_state pending;
73 101
74 char *title; // The view's title (unformatted) 102 char *title; // The view's title (unformatted)
75 char *formatted_title; // The title displayed in the title bar 103 char *formatted_title; // The title displayed in the title bar
104 int title_width;
76 105
77 enum sway_container_layout prev_split_layout; 106 enum sway_container_layout prev_split_layout;
78 107
@@ -100,33 +129,19 @@ struct sway_container {
100 double child_total_width; 129 double child_total_width;
101 double child_total_height; 130 double child_total_height;
102 131
103 // In most cases this is the same as the content x and y, but if the view
104 // refuses to resize to the content dimensions then it can be smaller.
105 // These are in layout coordinates.
106 double surface_x, surface_y;
107
108 // Outputs currently being intersected
109 list_t *outputs; // struct sway_output
110
111 // Indicates that the container is a scratchpad container. 132 // Indicates that the container is a scratchpad container.
112 // Both hidden and visible scratchpad containers have scratchpad=true. 133 // Both hidden and visible scratchpad containers have scratchpad=true.
113 // Hidden scratchpad containers have a NULL parent. 134 // Hidden scratchpad containers have a NULL parent.
114 bool scratchpad; 135 bool scratchpad;
115 136
116 float alpha; 137 // Stores last output size and position for adjusting coordinates of
138 // scratchpad windows.
139 // Unused for non-scratchpad windows.
140 struct wlr_box transform;
117 141
118 struct wlr_texture *title_focused; 142 float alpha;
119 struct wlr_texture *title_focused_inactive;
120 struct wlr_texture *title_focused_tab_title;
121 struct wlr_texture *title_unfocused;
122 struct wlr_texture *title_urgent;
123 143
124 list_t *marks; // char * 144 list_t *marks; // char *
125 struct wlr_texture *marks_focused;
126 struct wlr_texture *marks_focused_inactive;
127 struct wlr_texture *marks_focused_tab_title;
128 struct wlr_texture *marks_unfocused;
129 struct wlr_texture *marks_urgent;
130 145
131 struct { 146 struct {
132 struct wl_signal destroy; 147 struct wl_signal destroy;
@@ -146,19 +161,6 @@ void container_begin_destroy(struct sway_container *con);
146struct sway_container *container_find_child(struct sway_container *container, 161struct sway_container *container_find_child(struct sway_container *container,
147 bool (*test)(struct sway_container *view, void *data), void *data); 162 bool (*test)(struct sway_container *view, void *data), void *data);
148 163
149/**
150 * Find a container at the given coordinates. Returns the surface and
151 * surface-local coordinates of the given layout coordinates if the container
152 * is a view and the view contains a surface at those coordinates.
153 */
154struct sway_container *container_at(struct sway_workspace *workspace,
155 double lx, double ly, struct wlr_surface **surface,
156 double *sx, double *sy);
157
158struct sway_container *tiling_container_at(
159 struct sway_node *parent, double lx, double ly,
160 struct wlr_surface **surface, double *sx, double *sy);
161
162void container_for_each_child(struct sway_container *container, 164void container_for_each_child(struct sway_container *container,
163 void (*f)(struct sway_container *container, void *data), void *data); 165 void (*f)(struct sway_container *container, void *data), void *data);
164 166
@@ -175,13 +177,13 @@ bool container_has_ancestor(struct sway_container *container,
175 177
176void container_update_textures_recursive(struct sway_container *con); 178void container_update_textures_recursive(struct sway_container *con);
177 179
178void container_damage_whole(struct sway_container *container);
179
180void container_reap_empty(struct sway_container *con); 180void container_reap_empty(struct sway_container *con);
181 181
182struct sway_container *container_flatten(struct sway_container *container); 182struct sway_container *container_flatten(struct sway_container *container);
183 183
184void container_update_title_textures(struct sway_container *container); 184void container_update_title_bar(struct sway_container *container);
185
186void container_update_marks(struct sway_container *container);
185 187
186size_t container_build_representation(enum sway_container_layout layout, 188size_t container_build_representation(enum sway_container_layout layout,
187 list_t *children, char *buffer); 189 list_t *children, char *buffer);
@@ -196,6 +198,9 @@ size_t container_titlebar_height(void);
196void floating_calculate_constraints(int *min_width, int *max_width, 198void floating_calculate_constraints(int *min_width, int *max_width,
197 int *min_height, int *max_height); 199 int *min_height, int *max_height);
198 200
201void floating_fix_coordinates(struct sway_container *con,
202 struct wlr_box *old, struct wlr_box *new);
203
199void container_floating_resize_and_center(struct sway_container *con); 204void container_floating_resize_and_center(struct sway_container *con);
200 205
201void container_floating_set_default_size(struct sway_container *con); 206void container_floating_set_default_size(struct sway_container *con);
@@ -215,11 +220,6 @@ void container_set_geometry_from_content(struct sway_container *con);
215bool container_is_floating(struct sway_container *container); 220bool container_is_floating(struct sway_container *container);
216 221
217/** 222/**
218 * Same as above, but for current container state.
219 */
220bool container_is_current_floating(struct sway_container *container);
221
222/**
223 * Get a container's box in layout coordinates. 223 * Get a container's box in layout coordinates.
224 */ 224 */
225void container_get_box(struct sway_container *container, struct wlr_box *box); 225void container_get_box(struct sway_container *container, struct wlr_box *box);
@@ -281,26 +281,12 @@ bool container_is_floating_or_child(struct sway_container *container);
281 */ 281 */
282bool container_is_fullscreen_or_child(struct sway_container *container); 282bool container_is_fullscreen_or_child(struct sway_container *container);
283 283
284/**
285 * Return the output which will be used for scale purposes.
286 * This is the most recently entered output.
287 * If the container is not on any output, return NULL.
288 */
289struct sway_output *container_get_effective_output(struct sway_container *con);
290
291void container_discover_outputs(struct sway_container *con);
292
293enum sway_container_layout container_parent_layout(struct sway_container *con); 284enum sway_container_layout container_parent_layout(struct sway_container *con);
294 285
295enum sway_container_layout container_current_parent_layout(
296 struct sway_container *con);
297
298list_t *container_get_siblings(struct sway_container *container); 286list_t *container_get_siblings(struct sway_container *container);
299 287
300int container_sibling_index(struct sway_container *child); 288int container_sibling_index(struct sway_container *child);
301 289
302list_t *container_get_current_siblings(struct sway_container *container);
303
304void container_handle_fullscreen_reparent(struct sway_container *con); 290void container_handle_fullscreen_reparent(struct sway_container *con);
305 291
306void container_add_child(struct sway_container *parent, 292void container_add_child(struct sway_container *parent,
@@ -348,8 +334,6 @@ bool container_has_mark(struct sway_container *container, char *mark);
348 334
349void container_add_mark(struct sway_container *container, char *mark); 335void container_add_mark(struct sway_container *container, char *mark);
350 336
351void container_update_marks_textures(struct sway_container *container);
352
353void container_raise_floating(struct sway_container *con); 337void container_raise_floating(struct sway_container *con);
354 338
355bool container_is_scratchpad_hidden(struct sway_container *con); 339bool container_is_scratchpad_hidden(struct sway_container *con);
@@ -373,4 +357,10 @@ bool container_is_sticky_or_child(struct sway_container *con);
373 */ 357 */
374int container_squash(struct sway_container *con); 358int container_squash(struct sway_container *con);
375 359
360void container_arrange_title_bar(struct sway_container *con);
361
362void container_update(struct sway_container *con);
363
364void container_update_itself_and_parents(struct sway_container *con);
365
376#endif 366#endif
diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h
index 470ee3b5..e2dbcdf0 100644
--- a/include/sway/tree/node.h
+++ b/include/sway/tree/node.h
@@ -1,6 +1,8 @@
1#ifndef _SWAY_NODE_H 1#ifndef _SWAY_NODE_H
2#define _SWAY_NODE_H 2#define _SWAY_NODE_H
3#include <wayland-server-core.h>
3#include <stdbool.h> 4#include <stdbool.h>
5#include <wlr/types/wlr_scene.h>
4#include "list.h" 6#include "list.h"
5 7
6#define MIN_SANE_W 100 8#define MIN_SANE_W 100
@@ -74,4 +76,15 @@ list_t *node_get_children(struct sway_node *node);
74 76
75bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor); 77bool 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.
82void 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.
87struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent,
88 bool *failed);
89
77#endif 90#endif
diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h
index 5d4a2f2d..15df0f55 100644
--- a/include/sway/tree/root.h
+++ b/include/sway/tree/root.h
@@ -3,6 +3,7 @@
3#include <wayland-server-core.h> 3#include <wayland-server-core.h>
4#include <wayland-util.h> 4#include <wayland-util.h>
5#include <wlr/types/wlr_output_layout.h> 5#include <wlr/types/wlr_output_layout.h>
6#include <wlr/types/wlr_scene.h>
6#include <wlr/render/wlr_texture.h> 7#include <wlr/render/wlr_texture.h>
7#include "sway/tree/container.h" 8#include "sway/tree/container.h"
8#include "sway/tree/node.h" 9#include "sway/tree/node.h"
@@ -16,10 +17,44 @@ struct sway_root {
16 struct wlr_output_layout *output_layout; 17 struct wlr_output_layout *output_layout;
17 18
18 struct wl_listener output_layout_change; 19 struct wl_listener output_layout_change;
20
21 // scene node layout:
22 // - root
23 // - staging
24 // - layer shell stuff
25 // - tiling
26 // - floating
27 // - fullscreen stuff
28 // - seat stuff
29 // - ext_session_lock
30 struct wlr_scene *root_scene;
31
32 // since wlr_scene nodes can't be orphaned and must always
33 // have a parent, use this staging scene_tree so that a
34 // node always have a valid parent. Nothing in this
35 // staging node will be visible.
36 struct wlr_scene_tree *staging;
37
38 // tree containing all layers the compositor will render. Cursor handling
39 // will end up iterating this tree.
40 struct wlr_scene_tree *layer_tree;
41
42 struct {
43 struct wlr_scene_tree *shell_background;
44 struct wlr_scene_tree *shell_bottom;
45 struct wlr_scene_tree *tiling;
46 struct wlr_scene_tree *floating;
47 struct wlr_scene_tree *shell_top;
48 struct wlr_scene_tree *fullscreen;
49 struct wlr_scene_tree *fullscreen_global;
19#if HAVE_XWAYLAND 50#if HAVE_XWAYLAND
20 struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link 51 struct wlr_scene_tree *unmanaged;
21#endif 52#endif
22 struct wl_list drag_icons; // sway_drag_icon::link 53 struct wlr_scene_tree *shell_overlay;
54 struct wlr_scene_tree *popup;
55 struct wlr_scene_tree *seat;
56 struct wlr_scene_tree *session_lock;
57 } layers;
23 58
24 // Includes disabled outputs 59 // Includes disabled outputs
25 struct wl_list all_outputs; // sway_output::link 60 struct wl_list all_outputs; // sway_output::link
@@ -28,6 +63,7 @@ struct sway_root {
28 double width, height; 63 double width, height;
29 64
30 list_t *outputs; // struct sway_output 65 list_t *outputs; // struct sway_output
66 list_t *non_desktop_outputs; // struct sway_output_non_desktop
31 list_t *scratchpad; // struct sway_container 67 list_t *scratchpad; // struct sway_container
32 68
33 // For when there's no connected outputs 69 // For when there's no connected outputs
@@ -40,7 +76,7 @@ struct sway_root {
40 } events; 76 } events;
41}; 77};
42 78
43struct sway_root *root_create(void); 79struct sway_root *root_create(struct wl_display *display);
44 80
45void root_destroy(struct sway_root *root); 81void root_destroy(struct sway_root *root);
46 82
@@ -68,12 +104,6 @@ void root_scratchpad_show(struct sway_container *con);
68 */ 104 */
69void root_scratchpad_hide(struct sway_container *con); 105void root_scratchpad_hide(struct sway_container *con);
70 106
71struct sway_workspace *root_workspace_for_pid(pid_t pid);
72
73void root_record_workspace_pid(pid_t pid);
74
75void root_remove_workspace_pid(pid_t pid);
76
77void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), 107void 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
92void root_get_box(struct sway_root *root, struct wlr_box *box); 122void root_get_box(struct sway_root *root, struct wlr_box *box);
93 123
94void root_rename_pid_workspaces(const char *old_name, const char *new_name);
95
96#endif 124#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 0dcbf1aa..7faacdcc 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -2,7 +2,8 @@
2#define _SWAY_VIEW_H 2#define _SWAY_VIEW_H
3#include <wayland-server-core.h> 3#include <wayland-server-core.h>
4#include <wlr/types/wlr_compositor.h> 4#include <wlr/types/wlr_compositor.h>
5#include "config.h" 5#include <wlr/types/wlr_scene.h>
6#include "sway/config.h"
6#if HAVE_XWAYLAND 7#if HAVE_XWAYLAND
7#include <wlr/xwayland.h> 8#include <wlr/xwayland.h>
8#endif 9#endif
@@ -45,10 +46,6 @@ struct sway_view_impl {
45 void (*set_fullscreen)(struct sway_view *view, bool fullscreen); 46 void (*set_fullscreen)(struct sway_view *view, bool fullscreen);
46 void (*set_resizing)(struct sway_view *view, bool resizing); 47 void (*set_resizing)(struct sway_view *view, bool resizing);
47 bool (*wants_floating)(struct sway_view *view); 48 bool (*wants_floating)(struct sway_view *view);
48 void (*for_each_surface)(struct sway_view *view,
49 wlr_surface_iterator_func_t iterator, void *user_data);
50 void (*for_each_popup_surface)(struct sway_view *view,
51 wlr_surface_iterator_func_t iterator, void *user_data);
52 bool (*is_transient_for)(struct sway_view *child, 49 bool (*is_transient_for)(struct sway_view *child,
53 struct sway_view *ancestor); 50 struct sway_view *ancestor);
54 void (*close)(struct sway_view *view); 51 void (*close)(struct sway_view *view);
@@ -56,24 +53,20 @@ struct sway_view_impl {
56 void (*destroy)(struct sway_view *view); 53 void (*destroy)(struct sway_view *view);
57}; 54};
58 55
59struct 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
68struct sway_view { 56struct sway_view {
69 enum sway_view_type type; 57 enum sway_view_type type;
70 const struct sway_view_impl *impl; 58 const struct sway_view_impl *impl;
71 59
60 struct wlr_scene_tree *scene_tree;
61 struct wlr_scene_tree *content_tree;
62 struct wlr_scene_tree *saved_surface_tree;
63
72 struct sway_container *container; // NULL if unmapped and transactions finished 64 struct sway_container *container; // NULL if unmapped and transactions finished
73 struct wlr_surface *surface; // NULL for unmapped views 65 struct wlr_surface *surface; // NULL for unmapped views
74 struct sway_xdg_decoration *xdg_decoration; 66 struct sway_xdg_decoration *xdg_decoration;
75 67
76 pid_t pid; 68 pid_t pid;
69 struct launcher_ctx *ctx;
77 70
78 // The size the view would want to be if it weren't tiled. 71 // The size the view would want to be if it weren't tiled.
79 // Used when changing a view from tiled to floating. 72 // Used when changing a view from tiled to floating.
@@ -87,15 +80,11 @@ struct sway_view {
87 bool allow_request_urgent; 80 bool allow_request_urgent;
88 struct wl_event_source *urgent_timer; 81 struct wl_event_source *urgent_timer;
89 82
90 struct wl_list saved_buffers; // sway_saved_buffer::link
91
92 // The geometry for whatever the client is committing, regardless of 83 // The geometry for whatever the client is committing, regardless of
93 // transaction state. Updated on every commit. 84 // transaction state. Updated on every commit.
94 struct wlr_box geometry; 85 struct wlr_box geometry;
95 86
96 // The "old" geometry during a transaction. Used to damage the old location 87 struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel;
97 // when a transaction is applied.
98 struct wlr_box saved_geometry;
99 88
100 struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; 89 struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel;
101 struct wl_listener foreign_activate_request; 90 struct wl_listener foreign_activate_request;
@@ -118,8 +107,6 @@ struct sway_view {
118 struct wl_signal unmap; 107 struct wl_signal unmap;
119 } events; 108 } events;
120 109
121 struct wl_listener surface_new_subsurface;
122
123 int max_render_time; // In milliseconds 110 int max_render_time; // In milliseconds
124 111
125 enum seat_config_shortcuts_inhibit shortcuts_inhibit; 112 enum seat_config_shortcuts_inhibit shortcuts_inhibit;
@@ -144,6 +131,8 @@ struct sway_xdg_shell_view {
144struct sway_xwayland_view { 131struct sway_xwayland_view {
145 struct sway_view view; 132 struct sway_view view;
146 133
134 struct wlr_scene_tree *surface_tree;
135
147 struct wl_listener commit; 136 struct wl_listener commit;
148 struct wl_listener request_move; 137 struct wl_listener request_move;
149 struct wl_listener request_resize; 138 struct wl_listener request_resize;
@@ -155,72 +144,55 @@ struct sway_xwayland_view {
155 struct wl_listener set_title; 144 struct wl_listener set_title;
156 struct wl_listener set_class; 145 struct wl_listener set_class;
157 struct wl_listener set_role; 146 struct wl_listener set_role;
147 struct wl_listener set_startup_id;
158 struct wl_listener set_window_type; 148 struct wl_listener set_window_type;
159 struct wl_listener set_hints; 149 struct wl_listener set_hints;
160 struct wl_listener set_decorations; 150 struct wl_listener set_decorations;
151 struct wl_listener associate;
152 struct wl_listener dissociate;
161 struct wl_listener map; 153 struct wl_listener map;
162 struct wl_listener unmap; 154 struct wl_listener unmap;
163 struct wl_listener destroy; 155 struct wl_listener destroy;
164 struct wl_listener override_redirect; 156 struct wl_listener override_redirect;
157
158 struct wl_listener surface_tree_destroy;
165}; 159};
166 160
167struct sway_xwayland_unmanaged { 161struct sway_xwayland_unmanaged {
168 struct wlr_xwayland_surface *wlr_xwayland_surface; 162 struct wlr_xwayland_surface *wlr_xwayland_surface;
169 struct wl_list link;
170 163
171 int lx, ly; 164 struct wlr_scene_surface *surface_scene;
172 165
173 struct wl_listener request_activate; 166 struct wl_listener request_activate;
174 struct wl_listener request_configure; 167 struct wl_listener request_configure;
175 struct wl_listener request_fullscreen; 168 struct wl_listener request_fullscreen;
176 struct wl_listener commit;
177 struct wl_listener set_geometry; 169 struct wl_listener set_geometry;
170 struct wl_listener associate;
171 struct wl_listener dissociate;
178 struct wl_listener map; 172 struct wl_listener map;
179 struct wl_listener unmap; 173 struct wl_listener unmap;
180 struct wl_listener destroy; 174 struct wl_listener destroy;
181 struct wl_listener override_redirect; 175 struct wl_listener override_redirect;
182}; 176};
183#endif 177#endif
184struct sway_view_child;
185
186struct sway_view_child_impl {
187 void (*get_view_coords)(struct sway_view_child *child, int *sx, int *sy);
188 void (*destroy)(struct sway_view_child *child);
189};
190
191/**
192 * A view child is a surface in the view tree, such as a subsurface or a popup.
193 */
194struct sway_view_child {
195 const struct sway_view_child_impl *impl;
196 struct wl_list link;
197 178
179struct 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
212struct sway_subsurface {
213 struct sway_view_child child;
214
215 struct wl_listener destroy;
216}; 182};
217 183
218struct sway_xdg_popup { 184struct sway_xdg_popup {
219 struct sway_view_child child; 185 struct sway_view *view;
220 186
187 struct wlr_scene_tree *scene_tree;
188 struct wlr_scene_tree *xdg_surface_tree;
221 struct wlr_xdg_popup *wlr_xdg_popup; 189 struct wlr_xdg_popup *wlr_xdg_popup;
222 190
191 struct sway_popup_desc desc;
192
193 struct wl_listener surface_commit;
223 struct wl_listener new_popup; 194 struct wl_listener new_popup;
195 struct wl_listener reposition;
224 struct wl_listener destroy; 196 struct wl_listener destroy;
225}; 197};
226 198
@@ -269,7 +241,12 @@ void view_set_activated(struct sway_view *view, bool activated);
269/** 241/**
270 * Called when the view requests to be focused. 242 * Called when the view requests to be focused.
271 */ 243 */
272void view_request_activate(struct sway_view *view); 244void view_request_activate(struct sway_view *view, struct sway_seat *seat);
245
246/*
247 * Called when the view requests urgent state
248 */
249void view_request_urgent(struct sway_view *view);
273 250
274/** 251/**
275 * If possible, instructs the client to change their decoration mode. 252 * If possible, instructs the client to change their decoration mode.
@@ -288,23 +265,9 @@ void view_close(struct sway_view *view);
288 265
289void view_close_popups(struct sway_view *view); 266void view_close_popups(struct sway_view *view);
290 267
291void view_damage_from(struct sway_view *view);
292
293/**
294 * Iterate all surfaces of a view (toplevels + popups).
295 */
296void 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 */
302void 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
307void view_init(struct sway_view *view, enum sway_view_type type, 270bool 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
310void view_destroy(struct sway_view *view); 273void view_destroy(struct sway_view *view);
@@ -326,14 +289,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
326void view_unmap(struct sway_view *view); 289void view_unmap(struct sway_view *view);
327 290
328void view_update_size(struct sway_view *view); 291void view_update_size(struct sway_view *view);
329void view_center_surface(struct sway_view *view); 292void view_center_and_clip_surface(struct sway_view *view);
330
331void view_child_init(struct sway_view_child *child,
332 const struct sway_view_child_impl *impl, struct sway_view *view,
333 struct wlr_surface *surface);
334
335void view_child_destroy(struct sway_view_child *child);
336
337 293
338struct sway_view *view_from_wlr_xdg_surface( 294struct sway_view *view_from_wlr_xdg_surface(
339 struct wlr_xdg_surface *xdg_surface); 295 struct wlr_xdg_surface *xdg_surface);
@@ -343,6 +299,8 @@ struct sway_view *view_from_wlr_xwayland_surface(
343#endif 299#endif
344struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); 300struct sway_view *view_from_wlr_surface(struct wlr_surface *surface);
345 301
302void view_update_app_id(struct sway_view *view);
303
346/** 304/**
347 * Re-read the view's title property and update any relevant title bars. 305 * Re-read the view's title property and update any relevant title bars.
348 * The force argument makes it recreate the title bars even if the title hasn't 306 * The force argument makes it recreate the title bars even if the title hasn't
@@ -372,4 +330,8 @@ void view_save_buffer(struct sway_view *view);
372 330
373bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); 331bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor);
374 332
333void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);
334
335void view_send_frame_done(struct sway_view *view);
336
375#endif 337#endif
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index b3d93a81..58bde20c 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -2,6 +2,8 @@
2#define _SWAY_WORKSPACE_H 2#define _SWAY_WORKSPACE_H
3 3
4#include <stdbool.h> 4#include <stdbool.h>
5#include <wlr/types/wlr_scene.h>
6#include "sway/config.h"
5#include "sway/tree/container.h" 7#include "sway/tree/container.h"
6#include "sway/tree/node.h" 8#include "sway/tree/node.h"
7 9
@@ -22,6 +24,12 @@ struct sway_workspace_state {
22 24
23struct sway_workspace { 25struct sway_workspace {
24 struct sway_node node; 26 struct sway_node node;
27
28 struct {
29 struct wlr_scene_tree *tiling;
30 struct wlr_scene_tree *fullscreen;
31 } layers;
32
25 struct sway_container *fullscreen; 33 struct sway_container *fullscreen;
26 34
27 char *name; 35 char *name;
diff --git a/include/sway/xdg_decoration.h b/include/sway/xdg_decoration.h
index 8bef4c6d..2388ebcb 100644
--- a/include/sway/xdg_decoration.h
+++ b/include/sway/xdg_decoration.h
@@ -16,4 +16,6 @@ struct sway_xdg_decoration {
16struct sway_xdg_decoration *xdg_decoration_from_surface( 16struct sway_xdg_decoration *xdg_decoration_from_surface(
17 struct wlr_surface *surface); 17 struct wlr_surface *surface);
18 18
19void set_xdg_decoration_mode(struct sway_xdg_decoration *deco);
20
19#endif 21#endif
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index 3ad0bdf3..197d2190 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -4,6 +4,7 @@
4#include "config.h" 4#include "config.h"
5#include "input.h" 5#include "input.h"
6#include "pool-buffer.h" 6#include "pool-buffer.h"
7#include "cursor-shape-v1-client-protocol.h"
7#include "wlr-layer-shell-unstable-v1-client-protocol.h" 8#include "wlr-layer-shell-unstable-v1-client-protocol.h"
8#include "xdg-output-unstable-v1-client-protocol.h" 9#include "xdg-output-unstable-v1-client-protocol.h"
9 10
@@ -30,6 +31,7 @@ struct swaybar {
30 struct wl_compositor *compositor; 31 struct wl_compositor *compositor;
31 struct zwlr_layer_shell_v1 *layer_shell; 32 struct zwlr_layer_shell_v1 *layer_shell;
32 struct zxdg_output_manager_v1 *xdg_output_manager; 33 struct zxdg_output_manager_v1 *xdg_output_manager;
34 struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
33 struct wl_shm *shm; 35 struct wl_shm *shm;
34 36
35 struct swaybar_config *config; 37 struct swaybar_config *config;
diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h
index 1aec6d6c..dced2a6c 100644
--- a/include/swaybar/i3bar.h
+++ b/include/swaybar/i3bar.h
@@ -30,6 +30,6 @@ void i3bar_block_unref(struct i3bar_block *block);
30bool i3bar_handle_readable(struct status_line *status); 30bool i3bar_handle_readable(struct status_line *status);
31enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, 31enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
32 struct i3bar_block *block, double x, double y, double rx, double ry, 32 struct i3bar_block *block, double x, double y, double rx, double ry,
33 double w, double h, int scale, uint32_t button); 33 double w, double h, int scale, uint32_t button, bool released);
34 34
35#endif 35#endif
diff --git a/include/swaybar/image.h b/include/swaybar/image.h
new file mode 100644
index 00000000..53a210dd
--- /dev/null
+++ b/include/swaybar/image.h
@@ -0,0 +1,7 @@
1#ifndef _SWAYBAR_IMAGE_H
2#define _SWAYBAR_IMAGE_H
3#include <cairo.h>
4
5cairo_surface_t *load_image(const char *path);
6
7#endif
diff --git a/include/swaybar/input.h b/include/swaybar/input.h
index e8735d88..8ea88a69 100644
--- a/include/swaybar/input.h
+++ b/include/swaybar/input.h
@@ -49,7 +49,7 @@ struct swaybar_hotspot {
49 int x, y, width, height; 49 int x, y, width, height;
50 enum hotspot_event_handling (*callback)(struct swaybar_output *output, 50 enum hotspot_event_handling (*callback)(struct swaybar_output *output,
51 struct swaybar_hotspot *hotspot, double x, double y, uint32_t button, 51 struct swaybar_hotspot *hotspot, double x, double y, uint32_t button,
52 void *data); 52 bool released, void *data);
53 void (*destroy)(void *data); 53 void (*destroy)(void *data);
54 void *data; 54 void *data;
55}; 55};
diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h
index c02a5582..73937a0c 100644
--- a/include/swaybar/tray/item.h
+++ b/include/swaybar/tray/item.h
@@ -4,6 +4,7 @@
4#include <cairo.h> 4#include <cairo.h>
5#include <stdbool.h> 5#include <stdbool.h>
6#include <stdint.h> 6#include <stdint.h>
7#include <wayland-util.h>
7#include "swaybar/tray/tray.h" 8#include "swaybar/tray/tray.h"
8#include "list.h" 9#include "list.h"
9 10
diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h
index 2d68b6c9..fb9e9c21 100644
--- a/include/swaynag/swaynag.h
+++ b/include/swaynag/swaynag.h
@@ -4,6 +4,8 @@
4#include <strings.h> 4#include <strings.h>
5#include "list.h" 5#include "list.h"
6#include "pool-buffer.h" 6#include "pool-buffer.h"
7#include "cursor-shape-v1-client-protocol.h"
8
7#include "swaynag/types.h" 9#include "swaynag/types.h"
8 10
9#define SWAYNAG_MAX_HEIGHT 500 11#define SWAYNAG_MAX_HEIGHT 500
@@ -58,6 +60,7 @@ struct swaynag_button {
58struct swaynag_details { 60struct swaynag_details {
59 bool visible; 61 bool visible;
60 char *message; 62 char *message;
63 char *details_text;
61 64
62 int x; 65 int x;
63 int y; 66 int y;
@@ -67,7 +70,7 @@ struct swaynag_details {
67 int offset; 70 int offset;
68 int visible_lines; 71 int visible_lines;
69 int total_lines; 72 int total_lines;
70 struct swaynag_button button_details; 73 struct swaynag_button *button_details;
71 struct swaynag_button button_up; 74 struct swaynag_button button_up;
72 struct swaynag_button button_down; 75 struct swaynag_button button_down;
73}; 76};
@@ -84,6 +87,7 @@ struct swaynag {
84 struct swaynag_output *output; 87 struct swaynag_output *output;
85 struct zwlr_layer_shell_v1 *layer_shell; 88 struct zwlr_layer_shell_v1 *layer_shell;
86 struct zwlr_layer_surface_v1 *layer_surface; 89 struct zwlr_layer_surface_v1 *layer_surface;
90 struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
87 struct wl_surface *surface; 91 struct wl_surface *surface;
88 92
89 uint32_t width; 93 uint32_t width;
diff --git a/include/swaynag/types.h b/include/swaynag/types.h
index 18f218e0..9c3c50db 100644
--- a/include/swaynag/types.h
+++ b/include/swaynag/types.h
@@ -1,10 +1,13 @@
1#ifndef _SWAYNAG_TYPES_H 1#ifndef _SWAYNAG_TYPES_H
2#define _SWAYNAG_TYPES_H 2#define _SWAYNAG_TYPES_H
3 3
4#include <stdint.h>
5#include <pango/pangocairo.h>
6#include "list.h"
7
4struct swaynag_type { 8struct swaynag_type {
5 char *name; 9 char *name;
6 10
7 char *font; // Used for debugging.
8 PangoFontDescription *font_description; 11 PangoFontDescription *font_description;
9 char *output; 12 char *output;
10 uint32_t anchors; 13 uint32_t anchors;
diff --git a/meson.build b/meson.build
index d23300be..1043e4ba 100644
--- a/meson.build
+++ b/meson.build
@@ -1,7 +1,7 @@
1project( 1project(
2 'sway', 2 'sway',
3 'c', 3 'c',
4 version: '1.8-dev', 4 version: '1.10-dev',
5 license: 'MIT', 5 license: 'MIT',
6 meson_version: '>=0.60.0', 6 meson_version: '>=0.60.0',
7 default_options: [ 7 default_options: [
@@ -14,10 +14,12 @@ project(
14add_project_arguments( 14add_project_arguments(
15 [ 15 [
16 '-DWLR_USE_UNSTABLE', 16 '-DWLR_USE_UNSTABLE',
17 '-D_POSIX_C_SOURCE=200809L',
17 18
18 '-Wno-unused-parameter', 19 '-Wno-unused-parameter',
19 '-Wno-unused-result', 20 '-Wno-unused-result',
20 '-Wno-missing-braces', 21 '-Wno-missing-braces',
22 '-Wno-format-zero-length',
21 '-Wundef', 23 '-Wundef',
22 '-Wvla', 24 '-Wvla',
23 ], 25 ],
@@ -36,55 +38,54 @@ if is_freebsd
36endif 38endif
37 39
38# Execute the wlroots subproject, if any 40# Execute the wlroots subproject, if any
39wlroots_version = ['>=0.16.0', '<0.17.0'] 41wlroots_version = ['>=0.18.0', '<0.19.0']
40subproject( 42subproject(
41 'wlroots', 43 'wlroots',
42 default_options: ['examples=false'], 44 default_options: ['examples=false'],
43 required: false, 45 required: false,
44 version: wlroots_version, 46 version: wlroots_version,
45) 47)
48wlroots = dependency('wlroots', version: wlroots_version)
49wlroots_features = {
50 'xwayland': false,
51 'libinput_backend': false,
52 'session': false,
53}
54foreach name, _ : wlroots_features
55 var_name = 'have_' + name.underscorify()
56 have = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true'
57 wlroots_features += { name: have }
58endforeach
59
60if get_option('xwayland').enabled() and not wlroots_features['xwayland']
61 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support')
62endif
63
64null_dep = dependency('', required: false)
46 65
47jsonc = dependency('json-c', version: '>=0.13') 66jsonc = dependency('json-c', version: '>=0.13')
48pcre2 = dependency('libpcre2-8') 67pcre2 = dependency('libpcre2-8')
49wayland_server = dependency('wayland-server', version: '>=1.20.0') 68wayland_server = dependency('wayland-server', version: '>=1.21.0')
50wayland_client = dependency('wayland-client') 69wayland_client = dependency('wayland-client')
51wayland_cursor = dependency('wayland-cursor') 70wayland_cursor = dependency('wayland-cursor')
52wayland_egl = dependency('wayland-egl')
53wayland_protos = dependency('wayland-protocols', version: '>=1.24') 71wayland_protos = dependency('wayland-protocols', version: '>=1.24')
54wlroots = dependency('wlroots', version: wlroots_version) 72xkbcommon = dependency('xkbcommon', version: '>=1.5.0')
55xkbcommon = dependency('xkbcommon')
56cairo = dependency('cairo') 73cairo = dependency('cairo')
57pango = dependency('pango') 74pango = dependency('pango')
58pangocairo = dependency('pangocairo') 75pangocairo = dependency('pangocairo')
59gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) 76gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
60pixman = dependency('pixman-1') 77pixman = dependency('pixman-1')
61glesv2 = dependency('glesv2')
62libevdev = dependency('libevdev') 78libevdev = dependency('libevdev')
63libinput = dependency('libinput', version: '>=1.6.0') 79libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.21.0') : null_dep
64xcb = dependency('xcb', required: get_option('xwayland')) 80xcb = dependency('xcb', required: get_option('xwayland'))
65drm_full = dependency('libdrm') # only needed for drm_fourcc.h 81drm = dependency('libdrm')
66drm = drm_full.partial_dependency(compile_args: true, includes: true) 82libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep
67libudev = dependency('libudev')
68bash_comp = dependency('bash-completion', required: false)
69fish_comp = dependency('fish', required: false)
70math = cc.find_library('m') 83math = cc.find_library('m')
71rt = cc.find_library('rt') 84rt = cc.find_library('rt')
72xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) 85xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland'))
73threads = dependency('threads') # for pthread_setschedparam 86threads = dependency('threads') # for pthread_setschedparam
74 87
75wlroots_features = { 88have_xwayland = xcb.found() and xcb_icccm.found() and wlroots_features['xwayland']
76 'xwayland': false,
77}
78foreach name, _ : wlroots_features
79 var_name = 'have_' + name.underscorify()
80 have = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true'
81 wlroots_features += { name: have }
82endforeach
83
84if get_option('xwayland').enabled() and not wlroots_features['xwayland']
85 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support')
86endif
87have_xwayland = xcb.found() and wlroots_features['xwayland']
88 89
89if get_option('sd-bus-provider') == 'auto' 90if get_option('sd-bus-provider') == 'auto'
90 if not get_option('tray').disabled() 91 if not get_option('tray').disabled()
@@ -115,6 +116,11 @@ conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd
115conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') 116conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind')
116conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') 117conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu')
117conf_data.set10('HAVE_TRAY', have_tray) 118conf_data.set10('HAVE_TRAY', have_tray)
119conf_data.set10('HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', cc.has_header_symbol(
120 'libinput.h',
121 'LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM',
122 dependencies: libinput,
123))
118 124
119scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) 125scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
120if scdoc.found() 126if scdoc.found()
@@ -262,59 +268,7 @@ if get_option('default-wallpaper')
262 install_data(wallpaper_files, install_dir: wallpaper_install_dir) 268 install_data(wallpaper_files, install_dir: wallpaper_install_dir)
263endif 269endif
264 270
265if get_option('zsh-completions') 271subdir('completions')
266 zsh_files = files(
267 'completions/zsh/_sway',
268 'completions/zsh/_swaymsg',
269 )
270 zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions')
271
272 install_data(zsh_files, install_dir: zsh_install_dir)
273endif
274
275if get_option('bash-completions')
276 bash_files = files(
277 'completions/bash/sway',
278 'completions/bash/swaymsg',
279 )
280
281 if get_option('swaybar')
282 bash_files += files('completions/bash/swaybar')
283 endif
284
285 if bash_comp.found()
286 bash_install_dir = bash_comp.get_variable(
287 pkgconfig: 'completionsdir',
288 pkgconfig_define: ['datadir', datadir]
289 )
290 else
291 bash_install_dir = join_paths(datadir, 'bash-completion', 'completions')
292 endif
293
294 install_data(bash_files, install_dir: bash_install_dir)
295endif
296
297if get_option('fish-completions')
298 fish_files = files(
299 'completions/fish/sway.fish',
300 'completions/fish/swaymsg.fish',
301 )
302
303 if get_option('swaynag')
304 fish_files += files('completions/fish/swaynag.fish')
305 endif
306
307 if fish_comp.found()
308 fish_install_dir = fish_comp.get_variable(
309 pkgconfig: 'completionsdir',
310 pkgconfig_define: ['datadir', datadir]
311 )
312 else
313 fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d')
314 endif
315
316 install_data(fish_files, install_dir: fish_install_dir)
317endif
318 272
319summary({ 273summary({
320 'xwayland': have_xwayland, 274 'xwayland': have_xwayland,
@@ -322,4 +276,3 @@ summary({
322 'tray': have_tray, 276 'tray': have_tray,
323 'man-pages': scdoc.found(), 277 'man-pages': scdoc.found(),
324}, bool_yn: true) 278}, bool_yn: true)
325
diff --git a/meson_options.txt b/meson_options.txt
index 6ba67554..8d0d6509 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -6,6 +6,6 @@ option('swaybar', type: 'boolean', value: true, description: 'Enable support for
6option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag') 6option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag')
7option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') 7option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
8option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray') 8option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray')
9option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybg') 9option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray')
10option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') 10option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
11option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library') 11option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library')
diff --git a/protocols/meson.build b/protocols/meson.build
index df24a4e5..81edb584 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -1,81 +1,43 @@
1wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') 1wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
2 2
3wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true) 3wayland_scanner_dep = dependency('wayland-scanner', native: true)
4if wayland_scanner_dep.found() 4wayland_scanner = find_program(
5 wayland_scanner = find_program( 5 wayland_scanner_dep.get_variable('wayland_scanner'),
6 wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'), 6 native: true,
7 native: true, 7)
8 )
9else
10 wayland_scanner = find_program('wayland-scanner', native: true)
11endif
12 8
13protocols = [ 9protocols = [
14 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 10 wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
15 [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], 11 wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
16 [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], 12 wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
17 [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'], 13 wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml',
18 [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], 14 wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
19 ['wlr-layer-shell-unstable-v1.xml'], 15 wl_protocol_dir / 'staging/content-type/content-type-v1.xml',
20 ['idle.xml'], 16 wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
21 ['wlr-input-inhibitor-unstable-v1.xml'], 17 'wlr-layer-shell-unstable-v1.xml',
22 ['wlr-output-power-management-unstable-v1.xml'], 18 'idle.xml',
23] 19 'wlr-output-power-management-unstable-v1.xml',
24
25client_protocols = [
26 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
27 [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
28 ['wlr-layer-shell-unstable-v1.xml'],
29 ['wlr-input-inhibitor-unstable-v1.xml'],
30] 20]
31 21
32wl_protos_src = [] 22wl_protos_src = []
33wl_protos_headers = []
34 23
35foreach p : protocols 24foreach xml : protocols
36 xml = join_paths(p)
37 wl_protos_src += custom_target( 25 wl_protos_src += custom_target(
38 xml.underscorify() + '_server_c', 26 xml.underscorify() + '_c',
39 input: xml, 27 input: xml,
40 output: '@BASENAME@-protocol.c', 28 output: '@BASENAME@-protocol.c',
41 command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], 29 command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
42 ) 30 )
43 wl_protos_headers += custom_target( 31 wl_protos_src += custom_target(
44 xml.underscorify() + '_server_h', 32 xml.underscorify() + '_server_h',
45 input: xml, 33 input: xml,
46 output: '@BASENAME@-protocol.h', 34 output: '@BASENAME@-protocol.h',
47 command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], 35 command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
48 ) 36 )
49endforeach 37 wl_protos_src += custom_target(
50
51foreach p : client_protocols
52 xml = join_paths(p)
53 wl_protos_headers += custom_target(
54 xml.underscorify() + '_client_h', 38 xml.underscorify() + '_client_h',
55 input: xml, 39 input: xml,
56 output: '@BASENAME@-client-protocol.h', 40 output: '@BASENAME@-client-protocol.h',
57 command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], 41 command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
58 ) 42 )
59endforeach 43endforeach
60
61lib_client_protos = static_library(
62 'client_protos',
63 wl_protos_src + wl_protos_headers,
64 dependencies: wayland_client.partial_dependency(compile_args: true),
65)
66
67client_protos = declare_dependency(
68 link_with: lib_client_protos,
69 sources: wl_protos_headers,
70)
71
72lib_server_protos = static_library(
73 'server_protos',
74 wl_protos_src + wl_protos_headers,
75 dependencies: wayland_server.partial_dependency(compile_args: true),
76)
77
78server_protos = declare_dependency(
79 link_with: lib_server_protos,
80 sources: wl_protos_headers,
81)
diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml
deleted file mode 100644
index b62d1bb4..00000000
--- a/protocols/wlr-input-inhibitor-unstable-v1.xml
+++ /dev/null
@@ -1,67 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="wlr_input_inhibit_unstable_v1">
3 <copyright>
4 Copyright © 2018 Drew DeVault
5
6 Permission to use, copy, modify, distribute, and sell this
7 software and its documentation for any purpose is hereby granted
8 without fee, provided that the above copyright notice appear in
9 all copies and that both that copyright notice and this permission
10 notice appear in supporting documentation, and that the name of
11 the copyright holders not be used in advertising or publicity
12 pertaining to distribution of the software without specific,
13 written prior permission. The copyright holders make no
14 representations about the suitability of this software for any
15 purpose. It is provided "as is" without express or implied
16 warranty.
17
18 THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
19 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
23 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
25 THIS SOFTWARE.
26 </copyright>
27
28 <interface name="zwlr_input_inhibit_manager_v1" version="1">
29 <description summary="inhibits input events to other clients">
30 Clients can use this interface to prevent input events from being sent to
31 any surfaces but its own, which is useful for example in lock screen
32 software. It is assumed that access to this interface will be locked down
33 to whitelisted clients by the compositor.
34 </description>
35
36 <request name="get_inhibitor">
37 <description summary="inhibit input to other clients">
38 Activates the input inhibitor. As long as the inhibitor is active, the
39 compositor will not send input events to other clients.
40 </description>
41 <arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/>
42 </request>
43
44 <enum name="error">
45 <entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/>
46 </enum>
47 </interface>
48
49 <interface name="zwlr_input_inhibitor_v1" version="1">
50 <description summary="inhibits input to other clients">
51 While this resource exists, input to clients other than the owner of the
52 inhibitor resource will not receive input events. The client that owns
53 this resource will receive all input events normally. The compositor will
54 also disable all of its own input processing (such as keyboard shortcuts)
55 while the inhibitor is active.
56
57 The compositor may continue to send input events to selected clients,
58 such as an on-screen keyboard (via the input-method protocol).
59 </description>
60
61 <request name="destroy" type="destructor">
62 <description summary="destroy the input inhibitor object">
63 Destroy the inhibitor and allow other clients to receive input.
64 </description>
65 </request>
66 </interface>
67</protocol>
diff --git a/release.sh b/release.sh
new file mode 100755
index 00000000..62baf415
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,31 @@
1#!/bin/sh -eu
2
3prev=$(git describe --tags --abbrev=0)
4next=$(meson rewrite kwargs info project / 2>&1 >/dev/null | jq -r '.kwargs["project#/"].version')
5
6case "$next" in
7*-dev)
8 echo "This is a development version"
9 exit 1
10 ;;
11esac
12
13if [ "$prev" = "$next" ]; then
14 echo "Version not bumped in meson.build"
15 exit 1
16fi
17
18if ! git diff-index --quiet HEAD -- meson.build; then
19 echo "meson.build not committed"
20 exit 1
21fi
22
23shortlog="$(git shortlog --no-merges "$prev..")"
24(echo "sway $next"; echo ""; echo "$shortlog") | git tag "$next" -ase -F -
25
26prefix=sway-$next
27archive=$prefix.tar.gz
28git archive --prefix="$prefix/" -o "$archive" "$next"
29gpg --output "$archive".sig --detach-sig "$archive"
30
31gh release create "sway $next" -t "$next" -n "" -d "$archive" "$archive.sig"
diff --git a/sway/commands.c b/sway/commands.c
index 2160a970..8d003dfa 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <ctype.h> 1#include <ctype.h>
3#include <stdarg.h> 2#include <stdarg.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -104,6 +103,7 @@ static const struct cmd_handler handlers[] = {
104static const struct cmd_handler config_handlers[] = { 103static const struct cmd_handler config_handlers[] = {
105 { "default_orientation", cmd_default_orientation }, 104 { "default_orientation", cmd_default_orientation },
106 { "include", cmd_include }, 105 { "include", cmd_include },
106 { "primary_selection", cmd_primary_selection },
107 { "swaybg_command", cmd_swaybg_command }, 107 { "swaybg_command", cmd_swaybg_command },
108 { "swaynag_command", cmd_swaynag_command }, 108 { "swaynag_command", cmd_swaynag_command },
109 { "workspace_layout", cmd_workspace_layout }, 109 { "workspace_layout", cmd_workspace_layout },
@@ -147,7 +147,7 @@ static int handler_compare(const void *_a, const void *_b) {
147 return strcasecmp(a->command, b->command); 147 return strcasecmp(a->command, b->command);
148} 148}
149 149
150const struct cmd_handler *find_handler(char *line, 150const struct cmd_handler *find_handler(const char *line,
151 const struct cmd_handler *handlers, size_t handlers_size) { 151 const struct cmd_handler *handlers, size_t handlers_size) {
152 if (!handlers || !handlers_size) { 152 if (!handlers || !handlers_size) {
153 return NULL; 153 return NULL;
@@ -380,10 +380,13 @@ struct cmd_results *config_command(char *exec, char **new_block) {
380 sway_log(SWAY_INFO, "Config command: %s", exec); 380 sway_log(SWAY_INFO, "Config command: %s", exec);
381 const struct cmd_handler *handler = find_core_handler(argv[0]); 381 const struct cmd_handler *handler = find_core_handler(argv[0]);
382 if (!handler || !handler->handle) { 382 if (!handler || !handler->handle) {
383 const char *error = handler 383 if (handler) {
384 ? "Command '%s' is shimmed, but unimplemented" 384 results = cmd_results_new(CMD_INVALID,
385 : "Unknown/invalid command '%s'"; 385 "Command '%s' is shimmed, but unimplemented", argv[0]);
386 results = cmd_results_new(CMD_INVALID, error, argv[0]); 386 } else {
387 results = cmd_results_new(CMD_INVALID,
388 "Unknown/invalid command '%s'", argv[0]);
389 }
387 goto cleanup; 390 goto cleanup;
388 } 391 }
389 392
@@ -485,20 +488,10 @@ struct cmd_results *cmd_results_new(enum cmd_status status,
485 } 488 }
486 results->status = status; 489 results->status = status;
487 if (format) { 490 if (format) {
488 char *error = NULL;
489 va_list args; 491 va_list args;
490 va_start(args, format); 492 va_start(args, format);
491 int slen = vsnprintf(NULL, 0, format, args); 493 results->error = vformat_str(format, args);
492 va_end(args); 494 va_end(args);
493 if (slen > 0) {
494 error = malloc(slen + 1);
495 if (error != NULL) {
496 va_start(args, format);
497 vsnprintf(error, slen + 1, format, args);
498 va_end(args);
499 }
500 }
501 results->error = error;
502 } else { 495 } else {
503 results->error = NULL; 496 results->error = NULL;
504 } 497 }
diff --git a/sway/commands/assign.c b/sway/commands/assign.c
index 976bc3cc..bf95cf00 100644
--- a/sway/commands/assign.c
+++ b/sway/commands/assign.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -17,7 +16,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) {
17 char *err_str = NULL; 16 char *err_str = NULL;
18 struct criteria *criteria = criteria_parse(argv[0], &err_str); 17 struct criteria *criteria = criteria_parse(argv[0], &err_str);
19 if (!criteria) { 18 if (!criteria) {
20 error = cmd_results_new(CMD_INVALID, err_str); 19 error = cmd_results_new(CMD_INVALID, "%s", err_str);
21 free(err_str); 20 free(err_str);
22 return error; 21 return error;
23 } 22 }
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index 8571d282..635e895b 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
@@ -73,12 +72,10 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
73 } 72 }
74 ++argv; --argc; 73 ++argv; --argc;
75 } else if (config->reading && !config->current_bar) { 74 } else if (config->reading && !config->current_bar) {
76 int len = snprintf(NULL, 0, "bar-%d", config->bars->length) + 1; 75 id = format_str("bar-%d", config->bars->length);
77 id = malloc(len * sizeof(char));
78 if (!id) { 76 if (!id) {
79 return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id"); 77 return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id");
80 } 78 }
81 snprintf(id, len, "bar-%d", config->bars->length);
82 } else if (!config->reading && strcmp(argv[0], "mode") != 0 && 79 } else if (!config->reading && strcmp(argv[0], "mode") != 0 &&
83 strcmp(argv[0], "hidden_state") != 0) { 80 strcmp(argv[0], "hidden_state") != 0) {
84 if (is_subcommand(argv[0])) { 81 if (is_subcommand(argv[0])) {
diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c
index b4b5bc45..8a837e3f 100644
--- a/sway/commands/bar/bind.c
+++ b/sway/commands/bar/bind.c
@@ -96,7 +96,7 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code,
96 } 96 }
97 if (message) { 97 if (message) {
98 free_bar_binding(binding); 98 free_bar_binding(binding);
99 error = cmd_results_new(CMD_INVALID, message); 99 error = cmd_results_new(CMD_INVALID, "%s", message);
100 free(message); 100 free(message);
101 return error; 101 return error;
102 } else if (!binding->button) { 102 } else if (!binding->button) {
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c
index 891c87af..0c074679 100644
--- a/sway/commands/bar/font.c
+++ b/sway/commands/bar/font.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 8b661e3a..7b38831e 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c
index 6ac07843..fee21709 100644
--- a/sway/commands/bar/icon_theme.c
+++ b/sway/commands/bar/icon_theme.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "config.h" 2#include "config.h"
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
index a9a61743..46cf4ca9 100644
--- a/sway/commands/bar/id.c
+++ b/sway/commands/bar/id.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 7c2f423b..d69e910b 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c
index cac1d056..51730176 100644
--- a/sway/commands/bar/output.c
+++ b/sway/commands/bar/output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c
index b207de0b..94f530ec 100644
--- a/sway/commands/bar/position.c
+++ b/sway/commands/bar/position.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c
index 6737d4d2..50e9a873 100644
--- a/sway/commands/bar/separator_symbol.c
+++ b/sway/commands/bar/separator_symbol.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/tray_bind.c b/sway/commands/bar/tray_bind.c
index 243834ba..3dc9bc4c 100644
--- a/sway/commands/bar/tray_bind.c
+++ b/sway/commands/bar/tray_bind.c
@@ -26,7 +26,7 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) {
26 } 26 }
27 if (message) { 27 if (message) {
28 free(binding); 28 free(binding);
29 error = cmd_results_new(CMD_INVALID, message); 29 error = cmd_results_new(CMD_INVALID, "%s", message);
30 free(message); 30 free(message);
31 return error; 31 return error;
32 } else if (!binding->button) { 32 } else if (!binding->button) {
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c
index eb3b486e..679facf7 100644
--- a/sway/commands/bar/tray_output.c
+++ b/sway/commands/bar/tray_output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "config.h" 2#include "config.h"
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index c0b383db..268f2855 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <libevdev/libevdev.h> 1#include <libevdev/libevdev.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <string.h> 3#include <string.h>
@@ -127,7 +126,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
127 if (!button) { 126 if (!button) {
128 if (message) { 127 if (message) {
129 struct cmd_results *error = 128 struct cmd_results *error =
130 cmd_results_new(CMD_INVALID, message); 129 cmd_results_new(CMD_INVALID, "%s", message);
131 free(message); 130 free(message);
132 return error; 131 return error;
133 } else { 132 } else {
@@ -143,7 +142,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
143 if (!button) { 142 if (!button) {
144 if (message) { 143 if (message) {
145 struct cmd_results *error = 144 struct cmd_results *error =
146 cmd_results_new(CMD_INVALID, message); 145 cmd_results_new(CMD_INVALID, "%s", message);
147 free(message); 146 free(message);
148 return error; 147 return error;
149 } else { 148 } else {
@@ -182,7 +181,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
182 uint32_t button = get_mouse_bindsym(name, &message); 181 uint32_t button = get_mouse_bindsym(name, &message);
183 if (message) { 182 if (message) {
184 struct cmd_results *error = 183 struct cmd_results *error =
185 cmd_results_new(CMD_INVALID, message); 184 cmd_results_new(CMD_INVALID, "%s", message);
186 free(message); 185 free(message);
187 return error; 186 return error;
188 } else if (button) { 187 } else if (button) {
@@ -539,7 +538,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
539 free_switch_binding(binding); 538 free_switch_binding(binding);
540 return cmd_results_new(CMD_FAILURE, 539 return cmd_results_new(CMD_FAILURE,
541 "Invalid %s command (expected binding with the form " 540 "Invalid %s command (expected binding with the form "
542 "<switch>:<state>)", bindtype, argc); 541 "<switch>:<state>)", bindtype);
543 } 542 }
544 if (strcmp(split->items[0], "tablet") == 0) { 543 if (strcmp(split->items[0], "tablet") == 0) {
545 binding->type = WLR_SWITCH_TYPE_TABLET_MODE; 544 binding->type = WLR_SWITCH_TYPE_TABLET_MODE;
@@ -549,7 +548,8 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
549 free_switch_binding(binding); 548 free_switch_binding(binding);
550 return cmd_results_new(CMD_FAILURE, 549 return cmd_results_new(CMD_FAILURE,
551 "Invalid %s command (expected switch binding: " 550 "Invalid %s command (expected switch binding: "
552 "unknown switch %s)", bindtype, split->items[0]); 551 "unknown switch %s)", bindtype,
552 (const char *)split->items[0]);
553 } 553 }
554 if (strcmp(split->items[1], "on") == 0) { 554 if (strcmp(split->items[1], "on") == 0) {
555 binding->trigger = SWAY_SWITCH_TRIGGER_ON; 555 binding->trigger = SWAY_SWITCH_TRIGGER_ON;
@@ -562,7 +562,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
562 return cmd_results_new(CMD_FAILURE, 562 return cmd_results_new(CMD_FAILURE,
563 "Invalid %s command " 563 "Invalid %s command "
564 "(expected switch state: unknown state %s)", 564 "(expected switch state: unknown state %s)",
565 bindtype, split->items[1]); 565 bindtype, (const char *)split->items[1]);
566 } 566 }
567 list_free_items_and_destroy(split); 567 list_free_items_and_destroy(split);
568 568
diff --git a/sway/commands/client.c b/sway/commands/client.c
index 77263145..fd2ac7a8 100644
--- a/sway/commands/client.c
+++ b/sway/commands/client.c
@@ -5,9 +5,8 @@
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "util.h" 6#include "util.h"
7 7
8static void rebuild_textures_iterator(struct sway_container *con, void *data) { 8static 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
13static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, 12static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
@@ -51,12 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
51 memcpy(class, &colors, sizeof(struct border_colors)); 50 memcpy(class, &colors, sizeof(struct border_colors));
52 51
53 if (config->active) { 52 if (config->active) {
54 root_for_each_container(rebuild_textures_iterator, NULL); 53 root_for_each_container(container_update_iterator, NULL);
55
56 for (int i = 0; i < root->outputs->length; ++i) {
57 struct sway_output *output = root->outputs->items[i];
58 output_damage_whole(output);
59 }
60 } 54 }
61 55
62 return cmd_results_new(CMD_SUCCESS, NULL); 56 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index b35065c1..8bc1048c 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <stdint.h> 2#include <stdint.h>
4#include <string.h> 3#include <string.h>
@@ -8,6 +7,7 @@
8#include "sway/commands.h" 7#include "sway/commands.h"
9#include "sway/config.h" 8#include "sway/config.h"
10#include "sway/server.h" 9#include "sway/server.h"
10#include "sway/desktop/launcher.h"
11#include "sway/tree/container.h" 11#include "sway/tree/container.h"
12#include "sway/tree/root.h" 12#include "sway/tree/root.h"
13#include "sway/tree/workspace.h" 13#include "sway/tree/workspace.h"
@@ -25,11 +25,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
25 return error; 25 return error;
26} 26}
27 27
28static 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
33static 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
28struct cmd_results *cmd_exec_process(int argc, char **argv) { 38struct cmd_results *cmd_exec_process(int argc, char **argv) {
29 struct cmd_results *error = NULL; 39 struct cmd_results *error = NULL;
30 char *cmd = NULL; 40 char *cmd = NULL;
41 bool no_startup_id = false;
31 if (strcmp(argv[0], "--no-startup-id") == 0) { 42 if (strcmp(argv[0], "--no-startup-id") == 0) {
32 sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); 43 no_startup_id = true;
33 --argc; ++argv; 44 --argc; ++argv;
34 if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { 45 if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) {
35 return error; 46 return error;
@@ -51,6 +62,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
51 } 62 }
52 63
53 pid_t pid, child; 64 pid_t pid, child;
65 struct launcher_ctx *ctx = launcher_ctx_create_internal();
54 // Fork process 66 // Fork process
55 if ((pid = fork()) == 0) { 67 if ((pid = fork()) == 0) {
56 // Fork child process again 68 // Fork child process again
@@ -63,6 +75,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
63 close(fd[0]); 75 close(fd[0]);
64 if ((child = fork()) == 0) { 76 if ((child = fork()) == 0) {
65 close(fd[1]); 77 close(fd[1]);
78 if (ctx) {
79 export_xdga_token(ctx);
80 }
81 if (ctx && !no_startup_id) {
82 export_startup_id(ctx);
83 }
66 execlp("sh", "sh", "-c", cmd, (void *)NULL); 84 execlp("sh", "sh", "-c", cmd, (void *)NULL);
67 sway_log_errno(SWAY_ERROR, "execlp failed"); 85 sway_log_errno(SWAY_ERROR, "execlp failed");
68 _exit(1); 86 _exit(1);
@@ -90,8 +108,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
90 waitpid(pid, NULL, 0); 108 waitpid(pid, NULL, 0);
91 if (child > 0) { 109 if (child > 0) {
92 sway_log(SWAY_DEBUG, "Child process created with pid %d", child); 110 sway_log(SWAY_DEBUG, "Child process created with pid %d", child);
93 root_record_workspace_pid(child); 111 if (ctx != NULL) {
112 sway_log(SWAY_DEBUG, "Recording workspace for process %d", child);
113 ctx->pid = child;
114 }
94 } else { 115 } else {
116 launcher_ctx_destroy(ctx);
95 return cmd_results_new(CMD_FAILURE, "Second fork() failed"); 117 return cmd_results_new(CMD_FAILURE, "Second fork() failed");
96 } 118 }
97 119
diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c
index 3a1d606a..e8c24ace 100644
--- a/sway/commands/floating_minmax_size.c
+++ b/sway/commands/floating_minmax_size.c
@@ -23,16 +23,16 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
23 char *err; 23 char *err;
24 int width = (int)strtol(argv[0], &err, 10); 24 int width = (int)strtol(argv[0], &err, 10);
25 if (*err) { 25 if (*err) {
26 return cmd_results_new(CMD_INVALID, cmd_name, usage); 26 return cmd_results_new(CMD_INVALID, "%s", usage);
27 } 27 }
28 28
29 if (strcmp(argv[1], "x") != 0) { 29 if (strcmp(argv[1], "x") != 0) {
30 return cmd_results_new(CMD_INVALID, cmd_name, usage); 30 return cmd_results_new(CMD_INVALID, "%s", usage);
31 } 31 }
32 32
33 int height = (int)strtol(argv[2], &err, 10); 33 int height = (int)strtol(argv[2], &err, 10);
34 if (*err) { 34 if (*err) {
35 return cmd_results_new(CMD_INVALID, cmd_name, usage); 35 return cmd_results_new(CMD_INVALID, "%s", usage);
36 } 36 }
37 37
38 *config_width = width; 38 *config_width = width;
diff --git a/sway/commands/font.c b/sway/commands/font.c
index 74bb6b9f..9920d03e 100644
--- a/sway/commands/font.c
+++ b/sway/commands/font.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c
index ee9f4647..905e6776 100644
--- a/sway/commands/for_window.c
+++ b/sway/commands/for_window.c
@@ -14,7 +14,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) {
14 char *err_str = NULL; 14 char *err_str = NULL;
15 struct criteria *criteria = criteria_parse(argv[0], &err_str); 15 struct criteria *criteria = criteria_parse(argv[0], &err_str);
16 if (!criteria) { 16 if (!criteria) {
17 error = cmd_results_new(CMD_INVALID, err_str); 17 error = cmd_results_new(CMD_INVALID, "%s", err_str);
18 free(err_str); 18 free(err_str);
19 return error; 19 return error;
20 } 20 }
diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c
index d4442cc3..90a20716 100644
--- a/sway/commands/gesture.c
+++ b/sway/commands/gesture.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3 2
4#include "gesture.h" 3#include "gesture.h"
diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c
index 9a1d8445..43bd6dc8 100644
--- a/sway/commands/hide_edge_borders.c
+++ b/sway/commands/hide_edge_borders.c
@@ -20,7 +20,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
20 } 20 }
21 21
22 if (!argc) { 22 if (!argc) {
23 return cmd_results_new(CMD_INVALID, expected_syntax); 23 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
24 } 24 }
25 25
26 if (strcmp(argv[0], "none") == 0) { 26 if (strcmp(argv[0], "none") == 0) {
@@ -38,7 +38,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
38 config->hide_edge_borders = E_NONE; 38 config->hide_edge_borders = E_NONE;
39 config->hide_edge_borders_smart = ESMART_NO_GAPS; 39 config->hide_edge_borders_smart = ESMART_NO_GAPS;
40 } else { 40 } else {
41 return cmd_results_new(CMD_INVALID, expected_syntax); 41 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
42 } 42 }
43 config->hide_lone_tab = hide_lone_tab; 43 config->hide_lone_tab = hide_lone_tab;
44 44
diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c
index aebc2bf9..6125736a 100644
--- a/sway/commands/inhibit_idle.c
+++ b/sway/commands/inhibit_idle.c
@@ -41,7 +41,7 @@ struct cmd_results *cmd_inhibit_idle(int argc, char **argv) {
41 sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); 41 sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor);
42 } else { 42 } else {
43 inhibitor->mode = mode; 43 inhibitor->mode = mode;
44 sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); 44 sway_idle_inhibit_v1_check_active();
45 } 45 }
46 } else if (!clear) { 46 } else if (!clear) {
47 sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); 47 sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode);
diff --git a/sway/commands/input.c b/sway/commands/input.c
index 77acb671..306c40f7 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -14,6 +14,7 @@ static const struct cmd_handler input_handlers[] = {
14 { "drag", input_cmd_drag }, 14 { "drag", input_cmd_drag },
15 { "drag_lock", input_cmd_drag_lock }, 15 { "drag_lock", input_cmd_drag_lock },
16 { "dwt", input_cmd_dwt }, 16 { "dwt", input_cmd_dwt },
17 { "dwtp", input_cmd_dwtp },
17 { "events", input_cmd_events }, 18 { "events", input_cmd_events },
18 { "left_handed", input_cmd_left_handed }, 19 { "left_handed", input_cmd_left_handed },
19 { "map_from_region", input_cmd_map_from_region }, 20 { "map_from_region", input_cmd_map_from_region },
@@ -24,7 +25,9 @@ static const struct cmd_handler input_handlers[] = {
24 { "pointer_accel", input_cmd_pointer_accel }, 25 { "pointer_accel", input_cmd_pointer_accel },
25 { "repeat_delay", input_cmd_repeat_delay }, 26 { "repeat_delay", input_cmd_repeat_delay },
26 { "repeat_rate", input_cmd_repeat_rate }, 27 { "repeat_rate", input_cmd_repeat_rate },
28 { "rotation_angle", input_cmd_rotation_angle },
27 { "scroll_button", input_cmd_scroll_button }, 29 { "scroll_button", input_cmd_scroll_button },
30 { "scroll_button_lock", input_cmd_scroll_button_lock },
28 { "scroll_factor", input_cmd_scroll_factor }, 31 { "scroll_factor", input_cmd_scroll_factor },
29 { "scroll_method", input_cmd_scroll_method }, 32 { "scroll_method", input_cmd_scroll_method },
30 { "tap", input_cmd_tap }, 33 { "tap", input_cmd_tap },
diff --git a/sway/commands/input/calibration_matrix.c b/sway/commands/input/calibration_matrix.c
index 38749fbb..53fe2c35 100644
--- a/sway/commands/input/calibration_matrix.c
+++ b/sway/commands/input/calibration_matrix.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/dwtp.c b/sway/commands/input/dwtp.c
new file mode 100644
index 00000000..232e2b26
--- /dev/null
+++ b/sway/commands/input/dwtp.c
@@ -0,0 +1,25 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/config.h"
4#include "sway/commands.h"
5#include "sway/input/input-manager.h"
6#include "util.h"
7
8struct 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
10static void toggle_supported_send_events_for_device(struct input_config *ic, 14static 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
46static int mode_for_name(const char *name) { 52static int mode_for_name(const char *name) {
@@ -56,6 +62,7 @@ static int mode_for_name(const char *name) {
56 62
57static void toggle_select_send_events_for_device(struct input_config *ic, 63static 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
77static void toggle_send_events(int argc, char **argv) { 85static void toggle_send_events(int argc, char **argv) {
diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c
index de00b714..2f8f753d 100644
--- a/sway/commands/input/map_from_region.c
+++ b/sway/commands/input/map_from_region.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
@@ -11,11 +10,21 @@ static bool parse_coords(const char *str, double *x, double *y, bool *mm) {
11 *mm = false; 10 *mm = false;
12 11
13 char *end; 12 char *end;
14 *x = strtod(str, &end); 13
15 if (end[0] != 'x') { 14 // Check for "0x" prefix to avoid strtod treating the string as hex
16 return false; 15 if (str[0] == '0' && str[1] == 'x') {
16 if (strlen(str) < 3) {
17 return false;
18 }
19 *x = 0;
20 end = (char *)str + 2;
21 } else {
22 *x = strtod(str, &end);
23 if (end[0] != 'x') {
24 return false;
25 }
26 ++end;
17 } 27 }
18 ++end;
19 28
20 *y = strtod(end, &end); 29 *y = strtod(end, &end);
21 if (end[0] == 'm') { 30 if (end[0] == 'm') {
diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c
index f60fb7d5..a7266baa 100644
--- a/sway/commands/input/map_to_output.c
+++ b/sway/commands/input/map_to_output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c
index 284b57d0..9087c589 100644
--- a/sway/commands/input/map_to_region.c
+++ b/sway/commands/input/map_to_region.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -49,5 +48,5 @@ struct cmd_results *input_cmd_map_to_region(int argc, char **argv) {
49error: 48error:
50 free(ic->mapped_to_region); 49 free(ic->mapped_to_region);
51 ic->mapped_to_region = NULL; 50 ic->mapped_to_region = NULL;
52 return cmd_results_new(CMD_FAILURE, errstr); 51 return cmd_results_new(CMD_FAILURE, "%s", errstr);
53} 52}
diff --git a/sway/commands/input/rotation_angle.c b/sway/commands/input/rotation_angle.c
new file mode 100644
index 00000000..5e278fff
--- /dev/null
+++ b/sway/commands/input/rotation_angle.c
@@ -0,0 +1,29 @@
1#include <math.h>
2#include <stdlib.h>
3#include <string.h>
4#include "sway/config.h"
5#include "sway/commands.h"
6#include "sway/input/input-manager.h"
7#include "util.h"
8
9struct 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
9struct cmd_results *input_cmd_scroll_button_lock(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "scroll_button_lock", EXPECTED_AT_LEAST, 1))) {
12 return error;
13 }
14 struct input_config *ic = config->handler_context.input_config;
15 if (!ic) {
16 return cmd_results_new(CMD_FAILURE, "No input device defined.");
17 }
18
19 if (parse_boolean(argv[0], true)) {
20 ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED;
21 } else {
22 ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED;
23 }
24
25 return cmd_results_new(CMD_SUCCESS, NULL);
26}
diff --git a/sway/commands/input/xkb_file.c b/sway/commands/input/xkb_file.c
index 493f94fb..056f00e5 100644
--- a/sway/commands/input/xkb_file.c
+++ b/sway/commands/input/xkb_file.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <unistd.h> 1#include <unistd.h>
3#include <errno.h> 2#include <errno.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 22626517..1d01886c 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index f4a33de3..a9144a8a 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c
index 87d3e60c..bbe848fe 100644
--- a/sway/commands/input/xkb_numlock.c
+++ b/sway/commands/input/xkb_numlock.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "util.h" 3#include "util.h"
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index d609293f..7ca20777 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index 3b59622c..8fbd26fb 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c
index 3cce4ec8..ecac8e6c 100644
--- a/sway/commands/input/xkb_switch_layout.c
+++ b/sway/commands/input/xkb_switch_layout.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <wlr/interfaces/wlr_keyboard.h> 2#include <wlr/interfaces/wlr_keyboard.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index d0e21d77..2d14ea9c 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 2ba61b38..12ce4839 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -153,7 +153,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
153 workspace->output); 153 workspace->output);
154 } 154 }
155 if (new_layout == L_NONE) { 155 if (new_layout == L_NONE) {
156 return cmd_results_new(CMD_INVALID, expected_syntax); 156 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
157 } 157 }
158 if (new_layout != old_layout) { 158 if (new_layout != old_layout) {
159 if (container) { 159 if (container) {
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
index aa5f185c..2bfc86b3 100644
--- a/sway/commands/mark.c
+++ b/sway/commands/mark.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -59,7 +58,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
59 } 58 }
60 59
61 free(mark); 60 free(mark);
62 container_update_marks_textures(container); 61 container_update_marks(container);
63 if (container->view) { 62 if (container->view) {
64 view_execute_criteria(container->view); 63 view_execute_criteria(container->view);
65 } 64 }
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index 7263efcb..b3216967 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 0d0d9727..8addf26e 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <math.h> 2#include <math.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -206,9 +205,17 @@ static void container_move_to_workspace(struct sway_container *container,
206 container_detach(container); 205 container_detach(container);
207 workspace_add_floating(workspace, container); 206 workspace_add_floating(workspace, container);
208 container_handle_fullscreen_reparent(container); 207 container_handle_fullscreen_reparent(container);
209 // If changing output, center it within the workspace 208 // If changing output, adjust the coordinates of the window.
210 if (old_output != workspace->output && !container->pending.fullscreen_mode) { 209 if (old_output != workspace->output && !container->pending.fullscreen_mode) {
211 container_floating_move_to_center(container); 210 struct wlr_box workspace_box, old_workspace_box;
211 workspace_get_box(workspace, &workspace_box);
212 workspace_get_box(old_workspace, &old_workspace_box);
213 floating_fix_coordinates(container, &old_workspace_box, &workspace_box);
214 if (container->scratchpad && workspace->output) {
215 struct wlr_box output_box;
216 output_get_box(workspace->output, &output_box);
217 container->transform = workspace_box;
218 }
212 } 219 }
213 } else { 220 } else {
214 container_detach(container); 221 container_detach(container);
@@ -462,7 +469,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
462 if (strcasecmp(argv[1], "number") == 0) { 469 if (strcasecmp(argv[1], "number") == 0) {
463 // move [window|container] [to] "workspace number x" 470 // move [window|container] [to] "workspace number x"
464 if (argc < 3) { 471 if (argc < 3) {
465 return cmd_results_new(CMD_INVALID, expected_syntax); 472 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
466 } 473 }
467 if (!isdigit(argv[2][0])) { 474 if (!isdigit(argv[2][0])) {
468 return cmd_results_new(CMD_INVALID, 475 return cmd_results_new(CMD_INVALID,
@@ -522,7 +529,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
522 } 529 }
523 destination = &dest_con->node; 530 destination = &dest_con->node;
524 } else { 531 } else {
525 return cmd_results_new(CMD_INVALID, expected_syntax); 532 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
526 } 533 }
527 534
528 if (destination->type == N_CONTAINER && 535 if (destination->type == N_CONTAINER &&
@@ -686,6 +693,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
686 arrange_output(old_output); 693 arrange_output(old_output);
687 arrange_output(new_output); 694 arrange_output(new_output);
688 695
696 struct sway_seat *seat = config->handler_context.seat;
697 seat_consider_warp_to_focus(seat);
698
689 return cmd_results_new(CMD_SUCCESS, NULL); 699 return cmd_results_new(CMD_SUCCESS, NULL);
690} 700}
691 701
@@ -759,15 +769,6 @@ static struct cmd_results *cmd_move_in_direction(
759 ipc_event_window(container, "move"); 769 ipc_event_window(container, "move");
760 } 770 }
761 771
762 // Hack to re-focus container
763 seat_set_raw_focus(config->handler_context.seat, &new_ws->node);
764 seat_set_focus_container(config->handler_context.seat, container);
765
766 if (old_ws != new_ws) {
767 ipc_event_workspace(old_ws, new_ws, "focus");
768 workspace_detect_urgent(old_ws);
769 workspace_detect_urgent(new_ws);
770 }
771 container_end_mouse_operation(container); 772 container_end_mouse_operation(container);
772 773
773 return cmd_results_new(CMD_SUCCESS, NULL); 774 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -818,7 +819,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
818 } 819 }
819 820
820 if (!argc) { 821 if (!argc) {
821 return cmd_results_new(CMD_INVALID, expected_position_syntax); 822 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
822 } 823 }
823 824
824 bool absolute = false; 825 bool absolute = false;
@@ -828,19 +829,19 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
828 ++argv; 829 ++argv;
829 } 830 }
830 if (!argc) { 831 if (!argc) {
831 return cmd_results_new(CMD_INVALID, expected_position_syntax); 832 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
832 } 833 }
833 if (strcmp(argv[0], "position") == 0) { 834 if (strcmp(argv[0], "position") == 0) {
834 --argc; 835 --argc;
835 ++argv; 836 ++argv;
836 } 837 }
837 if (!argc) { 838 if (!argc) {
838 return cmd_results_new(CMD_INVALID, expected_position_syntax); 839 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
839 } 840 }
840 if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || 841 if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 ||
841 strcmp(argv[0], "pointer") == 0) { 842 strcmp(argv[0], "pointer") == 0) {
842 if (absolute) { 843 if (absolute) {
843 return cmd_results_new(CMD_INVALID, expected_position_syntax); 844 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
844 } 845 }
845 return cmd_move_to_position_pointer(container); 846 return cmd_move_to_position_pointer(container);
846 } else if (strcmp(argv[0], "center") == 0) { 847 } else if (strcmp(argv[0], "center") == 0) {
@@ -862,7 +863,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
862 } 863 }
863 864
864 if (argc < 2) { 865 if (argc < 2) {
865 return cmd_results_new(CMD_FAILURE, expected_position_syntax); 866 return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax);
866 } 867 }
867 868
868 struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 869 struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
@@ -875,7 +876,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
875 } 876 }
876 877
877 if (argc < 1) { 878 if (argc < 1) {
878 return cmd_results_new(CMD_FAILURE, expected_position_syntax); 879 return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax);
879 } 880 }
880 881
881 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 882 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
@@ -884,7 +885,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
884 argc -= num_consumed_args; 885 argc -= num_consumed_args;
885 argv += num_consumed_args; 886 argv += num_consumed_args;
886 if (argc > 0) { 887 if (argc > 0) {
887 return cmd_results_new(CMD_INVALID, expected_position_syntax); 888 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
888 } 889 }
889 if (ly.unit == MOVEMENT_UNIT_INVALID) { 890 if (ly.unit == MOVEMENT_UNIT_INVALID) {
890 return cmd_results_new(CMD_INVALID, "Invalid y position specified"); 891 return cmd_results_new(CMD_INVALID, "Invalid y position specified");
@@ -1030,13 +1031,13 @@ struct cmd_results *cmd_move(int argc, char **argv) {
1030 } 1031 }
1031 1032
1032 if (!argc) { 1033 if (!argc) {
1033 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1034 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1034 } 1035 }
1035 1036
1036 // Only `move [window|container] [to] workspace` supports 1037 // Only `move [window|container] [to] workspace` supports
1037 // `--no-auto-back-and-forth` so treat others as invalid syntax 1038 // `--no-auto-back-and-forth` so treat others as invalid syntax
1038 if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { 1039 if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) {
1039 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1040 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1040 } 1041 }
1041 1042
1042 if (strcasecmp(argv[0], "workspace") == 0 || 1043 if (strcasecmp(argv[0], "workspace") == 0 ||
@@ -1050,5 +1051,5 @@ struct cmd_results *cmd_move(int argc, char **argv) {
1050 strcasecmp(argv[1], "position") == 0)) { 1051 strcasecmp(argv[1], "position") == 0)) {
1051 return cmd_move_to_position(argc, argv); 1052 return cmd_move_to_position(argc, argv);
1052 } 1053 }
1053 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1054 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1054} 1055}
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c
index 2001e04f..ccfdec82 100644
--- a/sway/commands/no_focus.c
+++ b/sway/commands/no_focus.c
@@ -13,7 +13,7 @@ struct cmd_results *cmd_no_focus(int argc, char **argv) {
13 char *err_str = NULL; 13 char *err_str = NULL;
14 struct criteria *criteria = criteria_parse(argv[0], &err_str); 14 struct criteria *criteria = criteria_parse(argv[0], &err_str);
15 if (!criteria) { 15 if (!criteria) {
16 error = cmd_results_new(CMD_INVALID, err_str); 16 error = cmd_results_new(CMD_INVALID, "%s", err_str);
17 free(err_str); 17 free(err_str);
18 return error; 18 return error;
19 } 19 }
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c
index 96e6228e..610cecc6 100644
--- a/sway/commands/opacity.c
+++ b/sway/commands/opacity.c
@@ -37,6 +37,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) {
37 } 37 }
38 38
39 con->alpha = val; 39 con->alpha = val;
40 container_damage_whole(con); 40 container_update(con);
41
41 return cmd_results_new(CMD_SUCCESS, NULL); 42 return cmd_results_new(CMD_SUCCESS, NULL);
42} 43}
diff --git a/sway/commands/output.c b/sway/commands/output.c
index c102344d..5e5d31b3 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -27,6 +27,7 @@ static const struct cmd_handler output_handlers[] = {
27 { "subpixel", output_cmd_subpixel }, 27 { "subpixel", output_cmd_subpixel },
28 { "toggle", output_cmd_toggle }, 28 { "toggle", output_cmd_toggle },
29 { "transform", output_cmd_transform }, 29 { "transform", output_cmd_transform },
30 { "unplug", output_cmd_unplug },
30}; 31};
31 32
32struct cmd_results *cmd_output(int argc, char **argv) { 33struct cmd_results *cmd_output(int argc, char **argv) {
@@ -102,15 +103,18 @@ struct cmd_results *cmd_output(int argc, char **argv) {
102 103
103 bool background = output->background; 104 bool background = output->background;
104 105
105 output = store_output_config(output); 106 store_output_config(output);
106 107
107 // If reloading, the output configs will be applied after reading the 108 // If reloading, the output configs will be applied after reading the
108 // entire config and before the deferred commands so that an auto generated 109 // entire config and before the deferred commands so that an auto generated
109 // workspace name is not given to re-enabled outputs. 110 // workspace name is not given to re-enabled outputs.
110 if (!config->reloading && !config->validating) { 111 if (!config->reloading && !config->validating) {
111 apply_output_config_to_outputs(output); 112 apply_all_output_configs();
112 if (background) { 113 if (background) {
113 spawn_swaybg(); 114 if (!spawn_swaybg()) {
115 return cmd_results_new(CMD_FAILURE,
116 "Failed to apply background configuration");
117 }
114 } 118 }
115 } 119 }
116 120
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 67f212ff..55bd7671 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <libgen.h> 1#include <libgen.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <string.h> 3#include <string.h>
@@ -123,7 +122,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
123 src); 122 src);
124 config_add_swaynag_warning("Unable to access background file '%s'", 123 config_add_swaynag_warning("Unable to access background file '%s'",
125 src); 124 src);
125 struct cmd_results *result = cmd_results_new(CMD_FAILURE,
126 "unable to access background file '%s'", src);
126 free(src); 127 free(src);
128 return result;
127 } else { 129 } else {
128 output->background = src; 130 output->background = src;
129 output->background_option = strdup(mode); 131 output->background_option = strdup(mode);
diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c
index 6342d526..c6b72845 100644
--- a/sway/commands/output/toggle.c
+++ b/sway/commands/output/toggle.c
@@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) {
29 config->handler_context.output_config->enabled = 1; 29 config->handler_context.output_config->enabled = 1;
30 } 30 }
31 31
32 free(oc); 32 free_output_config(oc);
33 config->handler_context.leftovers.argc = argc; 33 config->handler_context.leftovers.argc = argc;
34 config->handler_context.leftovers.argv = argv; 34 config->handler_context.leftovers.argv = argv;
35 return NULL; 35 return NULL;
diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c
index f4fcc8c9..8db71bb3 100644
--- a/sway/commands/output/transform.c
+++ b/sway/commands/output/transform.c
@@ -1,4 +1,5 @@
1#include <string.h> 1#include <string.h>
2#include <wlr/util/transform.h>
2#include "sway/commands.h" 3#include "sway/commands.h"
3#include "sway/config.h" 4#include "sway/config.h"
4#include "log.h" 5#include "log.h"
diff --git a/sway/commands/output/unplug.c b/sway/commands/output/unplug.c
new file mode 100644
index 00000000..dfef626f
--- /dev/null
+++ b/sway/commands/output/unplug.c
@@ -0,0 +1,54 @@
1#include <strings.h>
2#include <wlr/config.h>
3#include <wlr/backend/headless.h>
4#include <wlr/backend/wayland.h>
5#if WLR_HAS_X11_BACKEND
6#include <wlr/backend/x11.h>
7#endif
8#include "sway/commands.h"
9#include "sway/config.h"
10#include "sway/output.h"
11
12static 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 */
30struct 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
7struct cmd_results *cmd_primary_selection(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "primary_selection", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12
13 bool primary_selection = parse_boolean(argv[0], true);
14
15 // config->primary_selection is reset to the previous value on reload in
16 // load_main_config()
17 if (config->reloading && config->primary_selection != primary_selection) {
18 return cmd_results_new(CMD_FAILURE,
19 "primary_selection can only be enabled/disabled at launch");
20 }
21
22 config->primary_selection = primary_selection;
23
24 return cmd_results_new(CMD_SUCCESS, NULL);
25}
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 76f14bba..6c0aac26 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -9,9 +8,8 @@
9#include "list.h" 8#include "list.h"
10#include "log.h" 9#include "log.h"
11 10
12static void rebuild_textures_iterator(struct sway_container *con, void *data) { 11static 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
17static void do_reload(void *data) { 15static void do_reload(void *data) {
@@ -48,7 +46,7 @@ static void do_reload(void *data) {
48 } 46 }
49 list_free_items_and_destroy(bar_ids); 47 list_free_items_and_destroy(bar_ids);
50 48
51 root_for_each_container(rebuild_textures_iterator, NULL); 49 root_for_each_container(title_bar_update_iterator, NULL);
52 50
53 arrange_root(); 51 arrange_root();
54} 52}
diff --git a/sway/commands/rename.c b/sway/commands/rename.c
index 3b855fdf..0d36cc21 100644
--- a/sway/commands/rename.c
+++ b/sway/commands/rename.c
@@ -7,6 +7,7 @@
7#include "sway/config.h" 7#include "sway/config.h"
8#include "sway/ipc-server.h" 8#include "sway/ipc-server.h"
9#include "sway/output.h" 9#include "sway/output.h"
10#include "sway/desktop/launcher.h"
10#include "sway/tree/container.h" 11#include "sway/tree/container.h"
11#include "sway/tree/workspace.h" 12#include "sway/tree/workspace.h"
12#include "sway/tree/root.h" 13#include "sway/tree/root.h"
@@ -25,7 +26,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
25 "Can't run this command while there's no outputs connected."); 26 "Can't run this command while there's no outputs connected.");
26 } 27 }
27 if (strcasecmp(argv[0], "workspace") != 0) { 28 if (strcasecmp(argv[0], "workspace") != 0) {
28 return cmd_results_new(CMD_INVALID, expected_syntax); 29 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
29 } 30 }
30 31
31 int argn = 1; 32 int argn = 1;
@@ -64,7 +65,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
64 ++argn; // move past "to" 65 ++argn; // move past "to"
65 66
66 if (argn >= argc) { 67 if (argn >= argc) {
67 return cmd_results_new(CMD_INVALID, expected_syntax); 68 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
68 } 69 }
69 70
70 char *new_name = join_args(argv + argn, argc - argn); 71 char *new_name = join_args(argv + argn, argc - argn);
@@ -91,8 +92,6 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
91 92
92 sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); 93 sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name);
93 94
94 root_rename_pid_workspaces(workspace->name, new_name);
95
96 free(workspace->name); 95 free(workspace->name);
97 workspace->name = new_name; 96 workspace->name = new_name;
98 97
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 425069de..32b746ea 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -75,6 +75,10 @@ void container_resize_tiled(struct sway_container *con,
75 return; 75 return;
76 } 76 }
77 77
78 if (container_is_scratchpad_hidden_or_child(con)) {
79 return;
80 }
81
78 // For HORIZONTAL or VERTICAL, we are growing in two directions so select 82 // For HORIZONTAL or VERTICAL, we are growing in two directions so select
79 // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. 83 // both adjacent siblings. For RIGHT or DOWN, just select the next sibling.
80 // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to 84 // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to
@@ -249,16 +253,35 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis,
249 struct movement_amount *amount) { 253 struct movement_amount *amount) {
250 struct sway_container *current = config->handler_context.container; 254 struct sway_container *current = config->handler_context.container;
251 255
256 if (container_is_scratchpad_hidden_or_child(current)) {
257 return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container");
258 }
259
252 if (amount->unit == MOVEMENT_UNIT_DEFAULT) { 260 if (amount->unit == MOVEMENT_UNIT_DEFAULT) {
253 amount->unit = MOVEMENT_UNIT_PPT; 261 amount->unit = MOVEMENT_UNIT_PPT;
254 } 262 }
255 if (amount->unit == MOVEMENT_UNIT_PPT) { 263 if (amount->unit == MOVEMENT_UNIT_PPT) {
264 struct sway_container *parent = current->pending.parent;
256 float pct = amount->amount / 100.0f; 265 float pct = amount->amount / 100.0f;
257 266
258 if (is_horizontal(axis)) { 267 if (is_horizontal(axis)) {
259 amount->amount = (float)current->pending.width * pct; 268 while (parent && parent->pending.layout != L_HORIZ) {
269 parent = parent->pending.parent;
270 }
271 if (parent) {
272 amount->amount = (float)parent->pending.width * pct;
273 } else {
274 amount->amount = (float)current->pending.workspace->width * pct;
275 }
260 } else { 276 } else {
261 amount->amount = (float)current->pending.height * pct; 277 while (parent && parent->pending.layout != L_VERT) {
278 parent = parent->pending.parent;
279 }
280 if (parent) {
281 amount->amount = (float)parent->pending.height * pct;
282 } else {
283 amount->amount = (float)current->pending.workspace->height * pct;
284 }
262 } 285 }
263 } 286 }
264 287
@@ -277,6 +300,11 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis,
277 */ 300 */
278static struct cmd_results *resize_set_tiled(struct sway_container *con, 301static struct cmd_results *resize_set_tiled(struct sway_container *con,
279 struct movement_amount *width, struct movement_amount *height) { 302 struct movement_amount *width, struct movement_amount *height) {
303
304 if (container_is_scratchpad_hidden_or_child(con)) {
305 return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container");
306 }
307
280 if (width->amount) { 308 if (width->amount) {
281 if (width->unit == MOVEMENT_UNIT_PPT || 309 if (width->unit == MOVEMENT_UNIT_PPT ||
282 width->unit == MOVEMENT_UNIT_DEFAULT) { 310 width->unit == MOVEMENT_UNIT_DEFAULT) {
@@ -415,7 +443,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
415 argc -= num_consumed_args; 443 argc -= num_consumed_args;
416 argv += num_consumed_args; 444 argv += num_consumed_args;
417 if (width.unit == MOVEMENT_UNIT_INVALID) { 445 if (width.unit == MOVEMENT_UNIT_INVALID) {
418 return cmd_results_new(CMD_INVALID, usage); 446 return cmd_results_new(CMD_INVALID, "%s", usage);
419 } 447 }
420 } 448 }
421 449
@@ -427,10 +455,10 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
427 } 455 }
428 int num_consumed_args = parse_movement_amount(argc, argv, &height); 456 int num_consumed_args = parse_movement_amount(argc, argv, &height);
429 if (argc > num_consumed_args) { 457 if (argc > num_consumed_args) {
430 return cmd_results_new(CMD_INVALID, usage); 458 return cmd_results_new(CMD_INVALID, "%s", usage);
431 } 459 }
432 if (width.unit == MOVEMENT_UNIT_INVALID) { 460 if (width.unit == MOVEMENT_UNIT_INVALID) {
433 return cmd_results_new(CMD_INVALID, usage); 461 return cmd_results_new(CMD_INVALID, "%s", usage);
434 } 462 }
435 } 463 }
436 464
@@ -462,7 +490,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
462 "[<amount> px|ppt [or <amount> px|ppt]]'"; 490 "[<amount> px|ppt [or <amount> px|ppt]]'";
463 uint32_t axis = parse_resize_axis(*argv); 491 uint32_t axis = parse_resize_axis(*argv);
464 if (axis == WLR_EDGE_NONE) { 492 if (axis == WLR_EDGE_NONE) {
465 return cmd_results_new(CMD_INVALID, usage); 493 return cmd_results_new(CMD_INVALID, "%s", usage);
466 } 494 }
467 --argc; ++argv; 495 --argc; ++argv;
468 496
@@ -473,7 +501,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
473 argc -= num_consumed_args; 501 argc -= num_consumed_args;
474 argv += num_consumed_args; 502 argv += num_consumed_args;
475 if (first_amount.unit == MOVEMENT_UNIT_INVALID) { 503 if (first_amount.unit == MOVEMENT_UNIT_INVALID) {
476 return cmd_results_new(CMD_INVALID, usage); 504 return cmd_results_new(CMD_INVALID, "%s", usage);
477 } 505 }
478 } else { 506 } else {
479 first_amount.amount = 10; 507 first_amount.amount = 10;
@@ -483,7 +511,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
483 // "or" 511 // "or"
484 if (argc) { 512 if (argc) {
485 if (strcmp(*argv, "or") != 0) { 513 if (strcmp(*argv, "or") != 0) {
486 return cmd_results_new(CMD_INVALID, usage); 514 return cmd_results_new(CMD_INVALID, "%s", usage);
487 } 515 }
488 --argc; ++argv; 516 --argc; ++argv;
489 } 517 }
@@ -493,10 +521,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
493 if (argc) { 521 if (argc) {
494 int num_consumed_args = parse_movement_amount(argc, argv, &second_amount); 522 int num_consumed_args = parse_movement_amount(argc, argv, &second_amount);
495 if (argc > num_consumed_args) { 523 if (argc > num_consumed_args) {
496 return cmd_results_new(CMD_INVALID, usage); 524 return cmd_results_new(CMD_INVALID, "%s", usage);
497 } 525 }
498 if (second_amount.unit == MOVEMENT_UNIT_INVALID) { 526 if (second_amount.unit == MOVEMENT_UNIT_INVALID) {
499 return cmd_results_new(CMD_INVALID, usage); 527 return cmd_results_new(CMD_INVALID, "%s", usage);
500 } 528 }
501 } else { 529 } else {
502 second_amount.amount = 0; 530 second_amount.amount = 0;
@@ -566,5 +594,5 @@ struct cmd_results *cmd_resize(int argc, char **argv) {
566 const char usage[] = "Expected 'resize <shrink|grow> " 594 const char usage[] = "Expected 'resize <shrink|grow> "
567 "<width|height|up|down|left|right> [<amount>] [px|ppt]'"; 595 "<width|height|up|down|left|right> [<amount>] [px|ppt]'";
568 596
569 return cmd_results_new(CMD_INVALID, usage); 597 return cmd_results_new(CMD_INVALID, "%s", usage);
570} 598}
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c
index 00bfdab6..47d18546 100644
--- a/sway/commands/seat/attach.c
+++ b/sway/commands/seat/attach.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 504a9f5e..df7c379d 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <linux/input-event-codes.h> 1#include <linux/input-event-codes.h>
3 2
4#include <strings.h> 3#include <strings.h>
@@ -18,7 +17,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
18 int argc, char **argv) { 17 int argc, char **argv) {
19 if (strcasecmp(argv[0], "move") == 0) { 18 if (strcasecmp(argv[0], "move") == 0) {
20 if (argc < 3) { 19 if (argc < 3) {
21 return cmd_results_new(CMD_INVALID, expected_syntax); 20 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
22 } 21 }
23 int delta_x = strtol(argv[1], NULL, 10); 22 int delta_x = strtol(argv[1], NULL, 10);
24 int delta_y = strtol(argv[2], NULL, 10); 23 int delta_y = strtol(argv[2], NULL, 10);
@@ -27,7 +26,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
27 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 26 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
28 } else if (strcasecmp(argv[0], "set") == 0) { 27 } else if (strcasecmp(argv[0], "set") == 0) {
29 if (argc < 3) { 28 if (argc < 3) {
30 return cmd_results_new(CMD_INVALID, expected_syntax); 29 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
31 } 30 }
32 // map absolute coords (0..1,0..1) to root container coords 31 // map absolute coords (0..1,0..1) to root container coords
33 float x = strtof(argv[1], NULL) / root->width; 32 float x = strtof(argv[1], NULL) / root->width;
@@ -37,7 +36,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
37 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 36 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
38 } else { 37 } else {
39 if (argc < 2) { 38 if (argc < 2) {
40 return cmd_results_new(CMD_INVALID, expected_syntax); 39 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
41 } 40 }
42 struct cmd_results *error = NULL; 41 struct cmd_results *error = NULL;
43 if ((error = press_or_release(cursor, argv[0], argv[1]))) { 42 if ((error = press_or_release(cursor, argv[0], argv[1]))) {
@@ -85,36 +84,36 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
85 84
86static struct cmd_results *press_or_release(struct sway_cursor *cursor, 85static struct cmd_results *press_or_release(struct sway_cursor *cursor,
87 char *action, char *button_str) { 86 char *action, char *button_str) {
88 enum wlr_button_state state; 87 enum wl_pointer_button_state state;
89 uint32_t button; 88 uint32_t button;
90 if (strcasecmp(action, "press") == 0) { 89 if (strcasecmp(action, "press") == 0) {
91 state = WLR_BUTTON_PRESSED; 90 state = WL_POINTER_BUTTON_STATE_PRESSED;
92 } else if (strcasecmp(action, "release") == 0) { 91 } else if (strcasecmp(action, "release") == 0) {
93 state = WLR_BUTTON_RELEASED; 92 state = WL_POINTER_BUTTON_STATE_RELEASED;
94 } else { 93 } else {
95 return cmd_results_new(CMD_INVALID, expected_syntax); 94 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
96 } 95 }
97 96
98 char *message = NULL; 97 char *message = NULL;
99 button = get_mouse_button(button_str, &message); 98 button = get_mouse_button(button_str, &message);
100 if (message) { 99 if (message) {
101 struct cmd_results *error = 100 struct cmd_results *error =
102 cmd_results_new(CMD_INVALID, message); 101 cmd_results_new(CMD_INVALID, "%s", message);
103 free(message); 102 free(message);
104 return error; 103 return error;
105 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN 104 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
106 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { 105 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {
107 // Dispatch axis event 106 // Dispatch axis event
108 enum wlr_axis_orientation orientation = 107 enum wl_pointer_axis orientation =
109 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) 108 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN)
110 ? WLR_AXIS_ORIENTATION_VERTICAL 109 ? WL_POINTER_AXIS_VERTICAL_SCROLL
111 : WLR_AXIS_ORIENTATION_HORIZONTAL; 110 : WL_POINTER_AXIS_HORIZONTAL_SCROLL;
112 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) 111 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)
113 ? -1 : 1; 112 ? -1 : 1;
114 struct wlr_pointer_axis_event event = { 113 struct wlr_pointer_axis_event event = {
115 .pointer = NULL, 114 .pointer = NULL,
116 .time_msec = 0, 115 .time_msec = 0,
117 .source = WLR_AXIS_SOURCE_WHEEL, 116 .source = WL_POINTER_AXIS_SOURCE_WHEEL,
118 .orientation = orientation, 117 .orientation = orientation,
119 .delta = delta * 15, 118 .delta = delta * 15,
120 .delta_discrete = delta 119 .delta_discrete = delta
diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c
index e09b82d9..f5177a47 100644
--- a/sway/commands/seat/hide_cursor.c
+++ b/sway/commands/seat/hide_cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c
index 82428f2c..2974453e 100644
--- a/sway/commands/seat/idle.c
+++ b/sway/commands/seat/idle.c
@@ -1,8 +1,8 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
5#include <stdint.h> 4#include <stdint.h>
5#include "log.h"
6#include "sway/commands.h" 6#include "sway/commands.h"
7#include "sway/config.h" 7#include "sway/config.h"
8#include "sway/input/seat.h" 8#include "sway/input/seat.h"
@@ -69,5 +69,10 @@ struct cmd_results *seat_cmd_idle_wake(int argc, char **argv) {
69 return cmd_results_new(CMD_FAILURE, "Invalid idle source"); 69 return cmd_results_new(CMD_FAILURE, "Invalid idle source");
70 } 70 }
71 config->handler_context.seat_config->idle_wake_sources = sources; 71 config->handler_context.seat_config->idle_wake_sources = sources;
72 sway_log(SWAY_INFO, "Warning: seat idle_wake is deprecated");
73 if (config->reading) {
74 config_add_swaynag_warning("seat idle_wake is deprecated. "
75 "Only seat idle_inhibit is supported.");
76 }
72 return cmd_results_new(CMD_SUCCESS, NULL); 77 return cmd_results_new(CMD_SUCCESS, NULL);
73} 78}
diff --git a/sway/commands/seat/xcursor_theme.c b/sway/commands/seat/xcursor_theme.c
index 202f35b9..61322a57 100644
--- a/sway/commands/seat/xcursor_theme.c
+++ b/sway/commands/seat/xcursor_theme.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/set.c b/sway/commands/set.c
index c539e9fc..ba384c7c 100644
--- a/sway/commands/set.c
+++ b/sway/commands/set.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
index 0d373b80..60cef9fa 100644
--- a/sway/commands/show_marks.c
+++ b/sway/commands/show_marks.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -10,8 +9,8 @@
10#include "stringop.h" 9#include "stringop.h"
11#include "util.h" 10#include "util.h"
12 11
13static void rebuild_marks_iterator(struct sway_container *con, void *data) { 12static 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
17struct cmd_results *cmd_show_marks(int argc, char **argv) { 16struct cmd_results *cmd_show_marks(int argc, char **argv) {
@@ -23,12 +22,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) {
23 config->show_marks = parse_boolean(argv[0], config->show_marks); 22 config->show_marks = parse_boolean(argv[0], config->show_marks);
24 23
25 if (config->show_marks) { 24 if (config->show_marks) {
26 root_for_each_container(rebuild_marks_iterator, NULL); 25 root_for_each_container(title_bar_update_iterator, NULL);
27 }
28
29 for (int i = 0; i < root->outputs->length; ++i) {
30 struct sway_output *output = root->outputs->items[i];
31 output_damage_whole(output);
32 } 26 }
33 27
34 return cmd_results_new(CMD_SUCCESS, NULL); 28 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/split.c b/sway/commands/split.c
index c8a2cfc1..500a497d 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -32,7 +32,7 @@ static struct cmd_results *do_split(int layout) {
32 return cmd_results_new(CMD_SUCCESS, NULL); 32 return cmd_results_new(CMD_SUCCESS, NULL);
33} 33}
34 34
35static struct cmd_results *do_unsplit() { 35static struct cmd_results *do_unsplit(void) {
36 struct sway_container *con = config->handler_context.container; 36 struct sway_container *con = config->handler_context.container;
37 struct sway_workspace *ws = config->handler_context.workspace; 37 struct sway_workspace *ws = config->handler_context.workspace;
38 38
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index 9355944d..e142eede 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -1,10 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <strings.h> 1#include <strings.h>
3#include "config.h" 2#include "config.h"
4#include "log.h" 3#include "log.h"
5#include "sway/commands.h" 4#include "sway/commands.h"
6#include "sway/output.h" 5#include "sway/output.h"
7#include "sway/tree/arrange.h" 6#include "sway/tree/arrange.h"
7#include "sway/tree/container.h"
8#include "sway/tree/root.h" 8#include "sway/tree/root.h"
9#include "sway/tree/view.h" 9#include "sway/tree/view.h"
10#include "sway/tree/workspace.h" 10#include "sway/tree/workspace.h"
@@ -13,180 +13,6 @@
13static const char expected_syntax[] = 13static 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
16static void swap_places(struct sway_container *con1,
17 struct sway_container *con2) {
18 struct sway_container *temp = malloc(sizeof(struct sway_container));
19 temp->pending.x = con1->pending.x;
20 temp->pending.y = con1->pending.y;
21 temp->pending.width = con1->pending.width;
22 temp->pending.height = con1->pending.height;
23 temp->width_fraction = con1->width_fraction;
24 temp->height_fraction = con1->height_fraction;
25 temp->pending.parent = con1->pending.parent;
26 temp->pending.workspace = con1->pending.workspace;
27 bool temp_floating = container_is_floating(con1);
28
29 con1->pending.x = con2->pending.x;
30 con1->pending.y = con2->pending.y;
31 con1->pending.width = con2->pending.width;
32 con1->pending.height = con2->pending.height;
33 con1->width_fraction = con2->width_fraction;
34 con1->height_fraction = con2->height_fraction;
35
36 con2->pending.x = temp->pending.x;
37 con2->pending.y = temp->pending.y;
38 con2->pending.width = temp->pending.width;
39 con2->pending.height = temp->pending.height;
40 con2->width_fraction = temp->width_fraction;
41 con2->height_fraction = temp->height_fraction;
42
43 int temp_index = container_sibling_index(con1);
44 if (con2->pending.parent) {
45 container_insert_child(con2->pending.parent, con1,
46 container_sibling_index(con2));
47 } else if (container_is_floating(con2)) {
48 workspace_add_floating(con2->pending.workspace, con1);
49 } else {
50 workspace_insert_tiling(con2->pending.workspace, con1,
51 container_sibling_index(con2));
52 }
53 if (temp->pending.parent) {
54 container_insert_child(temp->pending.parent, con2, temp_index);
55 } else if (temp_floating) {
56 workspace_add_floating(temp->pending.workspace, con2);
57 } else {
58 workspace_insert_tiling(temp->pending.workspace, con2, temp_index);
59 }
60
61 free(temp);
62}
63
64static void swap_focus(struct sway_container *con1,
65 struct sway_container *con2, struct sway_seat *seat,
66 struct sway_container *focus) {
67 if (focus == con1 || focus == con2) {
68 struct sway_workspace *ws1 = con1->pending.workspace;
69 struct sway_workspace *ws2 = con2->pending.workspace;
70 enum sway_container_layout layout1 = container_parent_layout(con1);
71 enum sway_container_layout layout2 = container_parent_layout(con2);
72 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
73 if (workspace_is_visible(ws2)) {
74 seat_set_focus(seat, &con2->node);
75 }
76 seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1);
77 } else if (focus == con2 && (layout1 == L_TABBED
78 || layout1 == L_STACKED)) {
79 if (workspace_is_visible(ws1)) {
80 seat_set_focus(seat, &con1->node);
81 }
82 seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2);
83 } else if (ws1 != ws2) {
84 seat_set_focus_container(seat, focus == con1 ? con2 : con1);
85 } else {
86 seat_set_focus_container(seat, focus);
87 }
88 } else {
89 seat_set_focus_container(seat, focus);
90 }
91
92 if (root->fullscreen_global) {
93 seat_set_focus(seat,
94 seat_get_focus_inactive(seat, &root->fullscreen_global->node));
95 }
96}
97
98void container_swap(struct sway_container *con1, struct sway_container *con2) {
99 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
100 return;
101 }
102 if (!sway_assert(!container_has_ancestor(con1, con2)
103 && !container_has_ancestor(con2, con1),
104 "Cannot swap ancestor and descendant")) {
105 return;
106 }
107
108 sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu",
109 con1->node.id, con2->node.id);
110
111 bool scratch1 = con1->scratchpad;
112 bool hidden1 = container_is_scratchpad_hidden(con1);
113 bool scratch2 = con2->scratchpad;
114 bool hidden2 = container_is_scratchpad_hidden(con2);
115 if (scratch1) {
116 if (hidden1) {
117 root_scratchpad_show(con1);
118 }
119 root_scratchpad_remove_container(con1);
120 }
121 if (scratch2) {
122 if (hidden2) {
123 root_scratchpad_show(con2);
124 }
125 root_scratchpad_remove_container(con2);
126 }
127
128 enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode;
129 if (fs1) {
130 container_fullscreen_disable(con1);
131 }
132 enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
133 if (fs2) {
134 container_fullscreen_disable(con2);
135 }
136
137 struct sway_seat *seat = config->handler_context.seat;
138 struct sway_container *focus = seat_get_focused_container(seat);
139 struct sway_workspace *vis1 =
140 output_get_active_workspace(con1->pending.workspace->output);
141 struct sway_workspace *vis2 =
142 output_get_active_workspace(con2->pending.workspace->output);
143 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a"
144 "workspace. This should not happen")) {
145 return;
146 }
147
148 char *stored_prev_name = NULL;
149 if (seat->prev_workspace_name) {
150 stored_prev_name = strdup(seat->prev_workspace_name);
151 }
152
153 swap_places(con1, con2);
154
155 if (!workspace_is_visible(vis1)) {
156 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));
157 }
158 if (!workspace_is_visible(vis2)) {
159 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));
160 }
161
162 swap_focus(con1, con2, seat, focus);
163
164 if (stored_prev_name) {
165 free(seat->prev_workspace_name);
166 seat->prev_workspace_name = stored_prev_name;
167 }
168
169 if (scratch1) {
170 root_scratchpad_add_container(con2, NULL);
171 if (!hidden1) {
172 root_scratchpad_show(con2);
173 }
174 }
175 if (scratch2) {
176 root_scratchpad_add_container(con1, NULL);
177 if (!hidden2) {
178 root_scratchpad_show(con1);
179 }
180 }
181
182 if (fs1) {
183 container_set_fullscreen(con2, fs1);
184 }
185 if (fs2) {
186 container_set_fullscreen(con1, fs2);
187 }
188}
189
190static bool test_con_id(struct sway_container *container, void *data) { 16static bool test_con_id(struct sway_container *container, void *data) {
191 size_t *con_id = data; 17 size_t *con_id = data;
192 return container->node.id == *con_id; 18 return container->node.id == *con_id;
@@ -219,7 +45,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
219 } 45 }
220 46
221 if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { 47 if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) {
222 return cmd_results_new(CMD_INVALID, expected_syntax); 48 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
223 } 49 }
224 50
225 struct sway_container *current = config->handler_context.container; 51 struct sway_container *current = config->handler_context.container;
@@ -238,7 +64,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
238 other = root_find_container(test_mark, value); 64 other = root_find_container(test_mark, value);
239 } else { 65 } else {
240 free(value); 66 free(value);
241 return cmd_results_new(CMD_INVALID, expected_syntax); 67 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
242 } 68 }
243 69
244 if (!other) { 70 if (!other) {
diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c
index c30355de..be298a29 100644
--- a/sway/commands/title_align.c
+++ b/sway/commands/title_align.c
@@ -4,6 +4,10 @@
4#include "sway/tree/container.h" 4#include "sway/tree/container.h"
5#include "sway/tree/root.h" 5#include "sway/tree/root.h"
6 6
7static void arrange_title_bar_iterator(struct sway_container *con, void *data) {
8 container_arrange_title_bar(con);
9}
10
7struct cmd_results *cmd_title_align(int argc, char **argv) { 11struct cmd_results *cmd_title_align(int argc, char **argv) {
8 struct cmd_results *error = NULL; 12 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { 13 if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) {
@@ -21,10 +25,7 @@ struct cmd_results *cmd_title_align(int argc, char **argv) {
21 "Expected 'title_align left|center|right'"); 25 "Expected 'title_align left|center|right'");
22 } 26 }
23 27
24 for (int i = 0; i < root->outputs->length; ++i) { 28 root_for_each_container(arrange_title_bar_iterator, NULL);
25 struct sway_output *output = root->outputs->items[i];
26 output_damage_whole(output);
27 }
28 29
29 return cmd_results_new(CMD_SUCCESS, NULL); 30 return cmd_results_new(CMD_SUCCESS, NULL);
30} 31}
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c
index a2446b7e..0b2ea265 100644
--- a/sway/commands/title_format.c
+++ b/sway/commands/title_format.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c
index 7c27c163..fa3db3c5 100644
--- a/sway/commands/titlebar_border_thickness.c
+++ b/sway/commands/titlebar_border_thickness.c
@@ -27,7 +27,6 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) {
27 "Expected output to have a workspace"); 27 "Expected output to have a workspace");
28 } 28 }
29 arrange_workspace(ws); 29 arrange_workspace(ws);
30 output_damage_whole(output);
31 } 30 }
32 31
33 return cmd_results_new(CMD_SUCCESS, NULL); 32 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c
index 29ce59ff..6999f7a2 100644
--- a/sway/commands/titlebar_padding.c
+++ b/sway/commands/titlebar_padding.c
@@ -33,7 +33,6 @@ struct cmd_results *cmd_titlebar_padding(int argc, char **argv) {
33 for (int i = 0; i < root->outputs->length; ++i) { 33 for (int i = 0; i < root->outputs->length; ++i) {
34 struct sway_output *output = root->outputs->items[i]; 34 struct sway_output *output = root->outputs->items[i];
35 arrange_workspace(output_get_active_workspace(output)); 35 arrange_workspace(output_get_active_workspace(output));
36 output_damage_whole(output);
37 } 36 }
38 37
39 return cmd_results_new(CMD_SUCCESS, NULL); 38 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
index 19274dfb..4aba5bae 100644
--- a/sway/commands/unmark.c
+++ b/sway/commands/unmark.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -8,9 +7,13 @@
8#include "log.h" 7#include "log.h"
9#include "stringop.h" 8#include "stringop.h"
10 9
11static void remove_all_marks_iterator(struct sway_container *con, void *data) { 10static 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
15static void remove_all_marks_iterator(struct sway_container *con, void *data) {
16 remove_mark(con);
14} 17}
15 18
16// unmark Remove all marks from all views 19// unmark Remove all marks from all views
@@ -38,8 +41,7 @@ struct cmd_results *cmd_unmark(int argc, char **argv) {
38 } 41 }
39 } else if (con && !mark) { 42 } else if (con && !mark) {
40 // Clear all marks from the given container 43 // Clear all marks from the given container
41 container_clear_marks(con); 44 remove_mark(con);
42 container_update_marks_textures(con);
43 } else if (!con && mark) { 45 } else if (!con && mark) {
44 // Remove mark from whichever container has it 46 // Remove mark from whichever container has it
45 container_find_and_unmark(mark); 47 container_find_and_unmark(mark);
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index a6a0beda..37a201b4 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <limits.h> 2#include <limits.h>
4#include <string.h> 3#include <string.h>
@@ -61,7 +60,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv,
61 const char expected[] = "Expected 'workspace <name> gaps " 60 const char expected[] = "Expected 'workspace <name> gaps "
62 "inner|outer|horizontal|vertical|top|right|bottom|left <px>'"; 61 "inner|outer|horizontal|vertical|top|right|bottom|left <px>'";
63 if (gaps_location == 0) { 62 if (gaps_location == 0) {
64 return cmd_results_new(CMD_INVALID, expected); 63 return cmd_results_new(CMD_INVALID, "%s", expected);
65 } 64 }
66 struct cmd_results *error = NULL; 65 struct cmd_results *error = NULL;
67 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 66 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO,
@@ -79,7 +78,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv,
79 char *end; 78 char *end;
80 int amount = strtol(argv[gaps_location + 2], &end, 10); 79 int amount = strtol(argv[gaps_location + 2], &end, 10);
81 if (strlen(end)) { 80 if (strlen(end)) {
82 return cmd_results_new(CMD_FAILURE, expected); 81 return cmd_results_new(CMD_FAILURE, "%s", expected);
83 } 82 }
84 83
85 bool valid = false; 84 bool valid = false;
@@ -110,7 +109,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv,
110 } 109 }
111 } 110 }
112 if (!valid) { 111 if (!valid) {
113 return cmd_results_new(CMD_INVALID, expected); 112 return cmd_results_new(CMD_INVALID, "%s", expected);
114 } 113 }
115 114
116 // Prevent invalid gaps configurations. 115 // Prevent invalid gaps configurations.
@@ -158,6 +157,10 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
158 return cmd_results_new(CMD_FAILURE, 157 return cmd_results_new(CMD_FAILURE,
159 "Unable to allocate workspace output"); 158 "Unable to allocate workspace output");
160 } 159 }
160 if (output_location + 1 < argc) {
161 list_free_items_and_destroy(wsc->outputs);
162 wsc->outputs = create_list();
163 }
161 for (int i = output_location + 1; i < argc; ++i) { 164 for (int i = output_location + 1; i < argc; ++i) {
162 list_add(wsc->outputs, strdup(argv[i])); 165 list_add(wsc->outputs, strdup(argv[i]));
163 } 166 }
@@ -174,7 +177,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
174 } 177 }
175 178
176 if (root->fullscreen_global) { 179 if (root->fullscreen_global) {
177 return cmd_results_new(CMD_FAILURE, "workspace", 180 return cmd_results_new(CMD_FAILURE,
178 "Can't switch workspaces while fullscreen global"); 181 "Can't switch workspaces while fullscreen global");
179 } 182 }
180 183
diff --git a/sway/commands/xwayland.c b/sway/commands/xwayland.c
index 6ca26923..584a8e3a 100644
--- a/sway/commands/xwayland.c
+++ b/sway/commands/xwayland.c
@@ -20,6 +20,8 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) {
20 xwayland = XWAYLAND_MODE_DISABLED; 20 xwayland = XWAYLAND_MODE_DISABLED;
21 } 21 }
22 22
23 // config->xwayland is reset to the previous value on reload in
24 // load_main_config()
23 if (config->reloading && config->xwayland != xwayland) { 25 if (config->reloading && config->xwayland != xwayland) {
24 return cmd_results_new(CMD_FAILURE, 26 return cmd_results_new(CMD_FAILURE,
25 "xwayland can only be enabled/disabled at launch"); 27 "xwayland can only be enabled/disabled at launch");
diff --git a/sway/config.c b/sway/config.c
index b41dd871..f9131e0f 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -1,3 +1,4 @@
1#undef _POSIX_C_SOURCE
1#define _XOPEN_SOURCE 700 // for realpath 2#define _XOPEN_SOURCE 700 // for realpath
2#include <stdio.h> 3#include <stdio.h>
3#include <stdbool.h> 4#include <stdbool.h>
@@ -36,19 +37,26 @@
36struct sway_config *config = NULL; 37struct sway_config *config = NULL;
37 38
38static struct xkb_state *keysym_translation_state_create( 39static 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
50static void keysym_translation_state_destroy( 55static void keysym_translation_state_destroy(
51 struct xkb_state *state) { 56 struct xkb_state *state) {
57 if (state == NULL) {
58 return;
59 }
52 xkb_keymap_unref(xkb_state_get_keymap(state)); 60 xkb_keymap_unref(xkb_state_get_keymap(state));
53 xkb_state_unref(state); 61 xkb_state_unref(state);
54} 62}
@@ -273,6 +281,7 @@ static void config_defaults(struct sway_config *config) {
273 config->title_align = ALIGN_LEFT; 281 config->title_align = ALIGN_LEFT;
274 config->tiling_drag = true; 282 config->tiling_drag = true;
275 config->tiling_drag_threshold = 9; 283 config->tiling_drag_threshold = 9;
284 config->primary_selection = true;
276 285
277 config->smart_gaps = SMART_GAPS_OFF; 286 config->smart_gaps = SMART_GAPS_OFF;
278 config->gaps_inner = 0; 287 config->gaps_inner = 0;
@@ -335,8 +344,14 @@ static void config_defaults(struct sway_config *config) {
335 344
336 // The keysym to keycode translation 345 // The keysym to keycode translation
337 struct xkb_rule_names rules = {0}; 346 struct xkb_rule_names rules = {0};
338 config->keysym_translation_state = 347 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
339 keysym_translation_state_create(rules); 348 if (config->keysym_translation_state == NULL) {
349 config->keysym_translation_state = keysym_translation_state_create(rules,
350 XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
351 }
352 if (config->keysym_translation_state == NULL) {
353 goto cleanup;
354 }
340 355
341 return; 356 return;
342cleanup: 357cleanup:
@@ -351,13 +366,7 @@ static char *config_path(const char *prefix, const char *config_folder) {
351 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { 366 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
352 return NULL; 367 return NULL;
353 } 368 }
354 369 return format_str("%s/%s/config", prefix, config_folder);
355 const char *filename = "config";
356
357 size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
358 char *path = calloc(size, sizeof(char));
359 snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
360 return path;
361} 370}
362 371
363static char *get_config_path(void) { 372static char *get_config_path(void) {
@@ -367,10 +376,7 @@ static char *get_config_path(void) {
367 376
368 const char *config_home = getenv("XDG_CONFIG_HOME"); 377 const char *config_home = getenv("XDG_CONFIG_HOME");
369 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { 378 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) {
370 size_t size_fallback = 1 + strlen(home) + strlen("/.config"); 379 config_home_fallback = format_str("%s/.config", home);
371 config_home_fallback = calloc(size_fallback, sizeof(char));
372 if (config_home_fallback != NULL)
373 snprintf(config_home_fallback, size_fallback, "%s/.config", home);
374 config_home = config_home_fallback; 380 config_home = config_home_fallback;
375 } 381 }
376 382
@@ -474,6 +480,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
474 old_config->xwayland ? "enabled" : "disabled"); 480 old_config->xwayland ? "enabled" : "disabled");
475 config->xwayland = old_config->xwayland; 481 config->xwayland = old_config->xwayland;
476 482
483 // primary_selection can only be enabled/disabled at launch
484 sway_log(SWAY_DEBUG, "primary_selection will remain %s",
485 old_config->primary_selection ? "enabled" : "disabled");
486 config->primary_selection = old_config->primary_selection;
487
477 if (!config->validating) { 488 if (!config->validating) {
478 if (old_config->swaybg_client != NULL) { 489 if (old_config->swaybg_client != NULL) {
479 wl_client_destroy(old_config->swaybg_client); 490 wl_client_destroy(old_config->swaybg_client);
@@ -493,56 +504,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
493 504
494 config->reading = true; 505 config->reading = true;
495 506
496 // Read security configs 507 bool success = load_config(path, config, &config->swaynag_config_errors);
497 // TODO: Security
498 bool success = true;
499 /*
500 DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
501 if (!dir) {
502 sway_log(SWAY_ERROR,
503 "%s does not exist, sway will have no security configuration"
504 " and will probably be broken", SYSCONFDIR "/sway/security.d");
505 } else {
506 list_t *secconfigs = create_list();
507 char *base = SYSCONFDIR "/sway/security.d/";
508 struct dirent *ent = readdir(dir);
509 struct stat s;
510 while (ent != NULL) {
511 char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1);
512 strcpy(_path, base);
513 strcat(_path, ent->d_name);
514 lstat(_path, &s);
515 if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') {
516 list_add(secconfigs, _path);
517 }
518 else {
519 free(_path);
520 }
521 ent = readdir(dir);
522 }
523 closedir(dir);
524
525 list_qsort(secconfigs, qstrcmp);
526 for (int i = 0; i < secconfigs->length; ++i) {
527 char *_path = secconfigs->items[i];
528 if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||
529 (((s.st_mode & 0777) != 0644) &&
530 (s.st_mode & 0777) != 0444)) {
531 sway_log(SWAY_ERROR,
532 "Refusing to load %s - it must be owned by root "
533 "and mode 644 or 444", _path);
534 success = false;
535 } else {
536 success = success && load_config(_path, config);
537 }
538 }
539
540 list_free_items_and_destroy(secconfigs);
541 }
542 */
543
544 success = success && load_config(path, config,
545 &config->swaynag_config_errors);
546 508
547 if (validating) { 509 if (validating) {
548 free_config(config); 510 free_config(config);
@@ -570,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
570 } 532 }
571 sway_switch_retrigger_bindings_for_all(); 533 sway_switch_retrigger_bindings_for_all();
572 534
573 reset_outputs(); 535 apply_all_output_configs();
574 spawn_swaybg(); 536 spawn_swaybg();
575 537
576 config->reloading = false; 538 config->reloading = false;
@@ -923,23 +885,18 @@ void config_add_swaynag_warning(char *fmt, ...) {
923 if (config->reading && !config->validating) { 885 if (config->reading && !config->validating) {
924 va_list args; 886 va_list args;
925 va_start(args, fmt); 887 va_start(args, fmt);
926 size_t length = vsnprintf(NULL, 0, fmt, args) + 1; 888 char *str = vformat_str(fmt, args);
927 va_end(args); 889 va_end(args);
928 890 if (str == NULL) {
929 char *temp = malloc(length + 1);
930 if (!temp) {
931 sway_log(SWAY_ERROR, "Failed to allocate buffer for warning.");
932 return; 891 return;
933 } 892 }
934 893
935 va_start(args, fmt);
936 vsnprintf(temp, length, fmt, args);
937 va_end(args);
938
939 swaynag_log(config->swaynag_command, &config->swaynag_config_errors, 894 swaynag_log(config->swaynag_command, &config->swaynag_config_errors,
940 "Warning on line %i (%s) '%s': %s", 895 "Warning on line %i (%s) '%s': %s",
941 config->current_config_line_number, config->current_config_path, 896 config->current_config_line_number, config->current_config_path,
942 config->current_config_line, temp); 897 config->current_config_line, str);
898
899 free(str);
943 } 900 }
944} 901}
945 902
@@ -979,7 +936,7 @@ char *do_var_replacement(char *str) {
979 int offset = find - str; 936 int offset = find - str;
980 strncpy(newptr, str, offset); 937 strncpy(newptr, str, offset);
981 newptr += offset; 938 newptr += offset;
982 strncpy(newptr, var->value, vvlen); 939 memcpy(newptr, var->value, vvlen);
983 newptr += vvlen; 940 newptr += vvlen;
984 strcpy(newptr, find + vnlen); 941 strcpy(newptr, find + vnlen);
985 free(str); 942 free(str);
@@ -1041,8 +998,12 @@ void translate_keysyms(struct input_config *input_config) {
1041 998
1042 struct xkb_rule_names rules = {0}; 999 struct xkb_rule_names rules = {0};
1043 input_config_fill_rule_names(input_config, &rules); 1000 input_config_fill_rule_names(input_config, &rules);
1044 config->keysym_translation_state = 1001 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
1045 keysym_translation_state_create(rules); 1002 if (config->keysym_translation_state == NULL) {
1003 sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state "
1004 "for device '%s'", input_config->identifier);
1005 return;
1006 }
1046 1007
1047 for (int i = 0; i < config->modes->length; ++i) { 1008 for (int i = 0; i < config->modes->length; ++i) {
1048 struct sway_mode *mode = config->modes->items[i]; 1009 struct sway_mode *mode = config->modes->items[i];
diff --git a/sway/config/bar.c b/sway/config/bar.c
index d1b342e6..908b2865 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <signal.h> 1#include <signal.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdio.h> 3#include <stdio.h>
@@ -256,7 +255,6 @@ static void invoke_swaybar(struct bar_config *bar) {
256 } 255 }
257 256
258 sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); 257 sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id);
259 return;
260} 258}
261 259
262void load_swaybar(struct bar_config *bar) { 260void load_swaybar(struct bar_config *bar) {
diff --git a/sway/config/input.c b/sway/config/input.c
index a998e170..de3b21ed 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <limits.h> 2#include <limits.h>
4#include <float.h> 3#include <float.h>
@@ -25,14 +24,17 @@ struct input_config *new_input_config(const char* identifier) {
25 input->drag = INT_MIN; 24 input->drag = INT_MIN;
26 input->drag_lock = INT_MIN; 25 input->drag_lock = INT_MIN;
27 input->dwt = INT_MIN; 26 input->dwt = INT_MIN;
27 input->dwtp = INT_MIN;
28 input->send_events = INT_MIN; 28 input->send_events = INT_MIN;
29 input->click_method = INT_MIN; 29 input->click_method = INT_MIN;
30 input->middle_emulation = INT_MIN; 30 input->middle_emulation = INT_MIN;
31 input->natural_scroll = INT_MIN; 31 input->natural_scroll = INT_MIN;
32 input->accel_profile = INT_MIN; 32 input->accel_profile = INT_MIN;
33 input->rotation_angle = FLT_MIN;
33 input->pointer_accel = FLT_MIN; 34 input->pointer_accel = FLT_MIN;
34 input->scroll_factor = FLT_MIN; 35 input->scroll_factor = FLT_MIN;
35 input->scroll_button = INT_MIN; 36 input->scroll_button = INT_MIN;
37 input->scroll_button_lock = INT_MIN;
36 input->scroll_method = INT_MIN; 38 input->scroll_method = INT_MIN;
37 input->left_handed = INT_MIN; 39 input->left_handed = INT_MIN;
38 input->repeat_delay = INT_MIN; 40 input->repeat_delay = INT_MIN;
@@ -61,6 +63,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
61 if (src->dwt != INT_MIN) { 63 if (src->dwt != INT_MIN) {
62 dst->dwt = src->dwt; 64 dst->dwt = src->dwt;
63 } 65 }
66 if (src->dwtp != INT_MIN) {
67 dst->dwtp = src->dwtp;
68 }
64 if (src->left_handed != INT_MIN) { 69 if (src->left_handed != INT_MIN) {
65 dst->left_handed = src->left_handed; 70 dst->left_handed = src->left_handed;
66 } 71 }
@@ -70,6 +75,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
70 if (src->natural_scroll != INT_MIN) { 75 if (src->natural_scroll != INT_MIN) {
71 dst->natural_scroll = src->natural_scroll; 76 dst->natural_scroll = src->natural_scroll;
72 } 77 }
78 if (src->rotation_angle != FLT_MIN) {
79 dst->rotation_angle = src->rotation_angle;
80 }
73 if (src->pointer_accel != FLT_MIN) { 81 if (src->pointer_accel != FLT_MIN) {
74 dst->pointer_accel = src->pointer_accel; 82 dst->pointer_accel = src->pointer_accel;
75 } 83 }
@@ -88,6 +96,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
88 if (src->scroll_button != INT_MIN) { 96 if (src->scroll_button != INT_MIN) {
89 dst->scroll_button = src->scroll_button; 97 dst->scroll_button = src->scroll_button;
90 } 98 }
99 if (src->scroll_button_lock != INT_MIN) {
100 dst->scroll_button_lock = src->scroll_button_lock;
101 }
91 if (src->send_events != INT_MIN) { 102 if (src->send_events != INT_MIN) {
92 dst->send_events = src->send_events; 103 dst->send_events = src->send_events;
93 } 104 }
diff --git a/sway/config/output.c b/sway/config/output.c
index b3e8371e..54af5d8e 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <drm_fourcc.h> 2#include <drm_fourcc.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -6,10 +5,11 @@
6#include <sys/socket.h> 5#include <sys/socket.h>
7#include <sys/wait.h> 6#include <sys/wait.h>
8#include <unistd.h> 7#include <unistd.h>
8#include <wlr/config.h>
9#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
10#include <wlr/types/wlr_output_layout.h> 10#include <wlr/types/wlr_output_layout.h>
11#include <wlr/types/wlr_output.h> 11#include <wlr/types/wlr_output.h>
12#include <wlr/backend/drm.h> 12#include <wlr/types/wlr_output_swapchain_manager.h>
13#include "sway/config.h" 13#include "sway/config.h"
14#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
15#include "sway/output.h" 15#include "sway/output.h"
@@ -17,6 +17,10 @@
17#include "log.h" 17#include "log.h"
18#include "util.h" 18#include "util.h"
19 19
20#if WLR_HAS_DRM_BACKEND
21#include <wlr/backend/drm.h>
22#endif
23
20int output_name_cmp(const void *item, const void *data) { 24int output_name_cmp(const void *item, const void *data) {
21 const struct output_config *output = item; 25 const struct output_config *output = item;
22 const char *name = data; 26 const char *name = data;
@@ -75,7 +79,72 @@ struct output_config *new_output_config(const char *name) {
75 return oc; 79 return oc;
76} 80}
77 81
78void 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
83static 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
147static void merge_output_config(struct output_config *dst, struct output_config *src) {
79 if (src->enabled != -1) { 148 if (src->enabled != -1) {
80 dst->enabled = src->enabled; 149 dst->enabled = src->enabled;
81 } 150 }
@@ -138,103 +207,42 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
138 } 207 }
139} 208}
140 209
141static void merge_wildcard_on_all(struct output_config *wildcard) { 210void store_output_config(struct output_config *oc) {
142 for (int i = 0; i < config->output_configs->length; i++) { 211 bool merged = false;
143 struct output_config *oc = config->output_configs->items[i]; 212 bool wildcard = strcmp(oc->name, "*") == 0;
144 if (strcmp(wildcard->name, oc->name) != 0) { 213 struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name);
145 sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name);
146 merge_output_config(oc, wildcard);
147 }
148 }
149}
150 214
151static void merge_id_on_name(struct output_config *oc) {
152 char *id_on_name = NULL;
153 char id[128]; 215 char id[128];
154 char *name = NULL; 216 if (output) {
155 struct sway_output *output;
156 wl_list_for_each(output, &root->all_outputs, link) {
157 name = output->wlr_output->name;
158 output_get_identifier(id, sizeof(id), output); 217 output_get_identifier(id, sizeof(id), output);
159 if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) {
160 size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1;
161 id_on_name = malloc(length);
162 if (!id_on_name) {
163 sway_log(SWAY_ERROR, "Failed to allocate id on name string");
164 return;
165 }
166 snprintf(id_on_name, length, "%s on %s", id, name);
167 break;
168 }
169 } 218 }
170 219
171 if (!id_on_name) { 220 for (int i = 0; i < config->output_configs->length; i++) {
172 return; 221 struct output_config *old = config->output_configs->items[i];
173 } 222
174 223 // If the old config matches the new config's name, regardless of
175 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 224 // whether it was name or identifier, merge on top of the existing
176 if (i >= 0) { 225 // config. If the new config is a wildcard, this also merges on top of
177 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); 226 // old wildcard configs.
178 merge_output_config(config->output_configs->items[i], oc); 227 if (strcmp(old->name, oc->name) == 0) {
179 } else { 228 merge_output_config(old, oc);
180 // If both a name and identifier config, exist generate an id on name 229 merged = true;
181 int ni = list_seq_find(config->output_configs, output_name_cmp, name); 230 continue;
182 int ii = list_seq_find(config->output_configs, output_name_cmp, id);
183 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0)
184 || (ii >= 0 && strcmp(oc->name, name) == 0)) {
185 struct output_config *ion_oc = new_output_config(id_on_name);
186 if (ni >= 0) {
187 merge_output_config(ion_oc, config->output_configs->items[ni]);
188 }
189 if (ii >= 0) {
190 merge_output_config(ion_oc, config->output_configs->items[ii]);
191 }
192 merge_output_config(ion_oc, oc);
193 list_add(config->output_configs, ion_oc);
194 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
195 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
196 "transform %d) (bg %s %s) (power %d) (max render time: %d)",
197 ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
198 ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
199 ion_oc->transform, ion_oc->background,
200 ion_oc->background_option, ion_oc->power,
201 ion_oc->max_render_time);
202 } 231 }
203 }
204 free(id_on_name);
205}
206 232
207struct output_config *store_output_config(struct output_config *oc) { 233 // If the new config is a wildcard config we supersede all non-wildcard
208 bool wildcard = strcmp(oc->name, "*") == 0; 234 // configs. Old wildcard configs have already been handled above.
209 if (wildcard) { 235 if (wildcard) {
210 merge_wildcard_on_all(oc); 236 supersede_output_config(old, oc);
211 } else { 237 continue;
212 merge_id_on_name(oc); 238 }
213 }
214 239
215 int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); 240 // If the new config matches an output's name, and the old config
216 if (i >= 0) { 241 // matches on that output's identifier, supersede it.
217 sway_log(SWAY_DEBUG, "Merging on top of existing output config"); 242 if (output && strcmp(old->name, id) == 0 &&
218 struct output_config *current = config->output_configs->items[i]; 243 strcmp(oc->name, output->wlr_output->name) == 0) {
219 merge_output_config(current, oc); 244 supersede_output_config(old, oc);
220 free_output_config(oc);
221 oc = current;
222 } else if (!wildcard) {
223 sway_log(SWAY_DEBUG, "Adding non-wildcard output config");
224 i = list_seq_find(config->output_configs, output_name_cmp, "*");
225 if (i >= 0) {
226 sway_log(SWAY_DEBUG, "Merging on top of output * config");
227 struct output_config *current = new_output_config(oc->name);
228 merge_output_config(current, config->output_configs->items[i]);
229 merge_output_config(current, oc);
230 free_output_config(oc);
231 oc = current;
232 } 245 }
233 list_add(config->output_configs, oc);
234 } else {
235 // New wildcard config. Just add it
236 sway_log(SWAY_DEBUG, "Adding output * config");
237 list_add(config->output_configs, oc);
238 } 246 }
239 247
240 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 248 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
@@ -245,7 +253,13 @@ struct output_config *store_output_config(struct output_config *oc) {
245 oc->transform, oc->background, oc->background_option, oc->power, 253 oc->transform, oc->background, oc->background_option, oc->power,
246 oc->max_render_time); 254 oc->max_render_time);
247 255
248 return oc; 256 // If the configuration was not merged into an existing configuration, add
257 // it to the list. Otherwise we're done with it and can free it.
258 if (!merged) {
259 list_add(config->output_configs, oc);
260 } else {
261 free_output_config(oc);
262 }
249} 263}
250 264
251static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, 265static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
@@ -253,7 +267,9 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending
253 // Not all floating point integers can be represented exactly 267 // Not all floating point integers can be represented exactly
254 // as (int)(1000 * mHz / 1000.f) 268 // as (int)(1000 * mHz / 1000.f)
255 // round() the result to avoid any error 269 // round() the result to avoid any error
256 int mhz = (int)round(refresh_rate * 1000); 270 int mhz = (int)roundf(refresh_rate * 1000);
271 // If no target refresh rate is given, match highest available
272 mhz = mhz <= 0 ? INT_MAX : mhz;
257 273
258 if (wl_list_empty(&output->modes) || custom) { 274 if (wl_list_empty(&output->modes) || custom) {
259 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); 275 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
@@ -263,29 +279,35 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending
263 } 279 }
264 280
265 struct wlr_output_mode *mode, *best = NULL; 281 struct wlr_output_mode *mode, *best = NULL;
282 int best_diff_mhz = INT_MAX;
266 wl_list_for_each(mode, &output->modes, link) { 283 wl_list_for_each(mode, &output->modes, link) {
267 if (mode->width == width && mode->height == height) { 284 if (mode->width == width && mode->height == height) {
268 if (mode->refresh == mhz) { 285 int diff_mhz = abs(mode->refresh - mhz);
269 best = mode; 286 if (diff_mhz < best_diff_mhz) {
270 break; 287 best_diff_mhz = diff_mhz;
271 }
272 if (best == NULL || mode->refresh > best->refresh) {
273 best = mode; 288 best = mode;
289 if (best_diff_mhz == 0) {
290 break;
291 }
274 } 292 }
275 } 293 }
276 } 294 }
277 if (!best) { 295 if (best) {
278 sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); 296 sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s",
279 sway_log(SWAY_INFO, "Picking preferred mode instead"); 297 best->width, best->height, best->refresh / 1000.f, output->name);
280 best = wlr_output_preferred_mode(output);
281 } else { 298 } else {
282 sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); 299 best = wlr_output_preferred_mode(output);
300 sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, "
301 "applying preferred mode (%dx%d@%.3fHz)",
302 width, height, refresh_rate,
303 best->width, best->height, best->refresh / 1000.f);
283 } 304 }
284 wlr_output_state_set_mode(pending, best); 305 wlr_output_state_set_mode(pending, best);
285} 306}
286 307
287static void set_modeline(struct wlr_output *output, 308static void set_modeline(struct wlr_output *output,
288 struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { 309 struct wlr_output_state *pending, drmModeModeInfo *drm_mode) {
310#if WLR_HAS_DRM_BACKEND
289 if (!wlr_output_is_drm(output)) { 311 if (!wlr_output_is_drm(output)) {
290 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); 312 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
291 return; 313 return;
@@ -295,6 +317,9 @@ static void set_modeline(struct wlr_output *output,
295 if (mode) { 317 if (mode) {
296 wlr_output_state_set_mode(pending, mode); 318 wlr_output_state_set_mode(pending, mode);
297 } 319 }
320#else
321 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
322#endif
298} 323}
299 324
300/* Some manufacturers hardcode the aspect-ratio of the output in the physical 325/* Some manufacturers hardcode the aspect-ratio of the output in the physical
@@ -436,9 +461,11 @@ static void queue_output_config(struct output_config *oc,
436 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; 461 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
437 if (oc && oc->transform >= 0) { 462 if (oc && oc->transform >= 0) {
438 tr = oc->transform; 463 tr = oc->transform;
464#if WLR_HAS_DRM_BACKEND
439 } else if (wlr_output_is_drm(wlr_output)) { 465 } else if (wlr_output_is_drm(wlr_output)) {
440 tr = wlr_drm_connector_get_panel_orientation(wlr_output); 466 tr = wlr_drm_connector_get_panel_orientation(wlr_output);
441 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); 467 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr);
468#endif
442 } 469 }
443 if (wlr_output->transform != tr) { 470 if (wlr_output->transform != tr) {
444 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); 471 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr);
@@ -450,6 +477,16 @@ static void queue_output_config(struct output_config *oc,
450 float scale; 477 float scale;
451 if (oc && oc->scale > 0) { 478 if (oc && oc->scale > 0) {
452 scale = oc->scale; 479 scale = oc->scale;
480
481 // The factional-scale-v1 protocol uses increments of 120ths to send
482 // the scale factor to the client. Adjust the scale so that we use the
483 // same value as the clients'.
484 float adjusted_scale = round(scale * 120) / 120;
485 if (scale != adjusted_scale) {
486 sway_log(SWAY_INFO, "Adjusting output scale from %f to %f",
487 scale, adjusted_scale);
488 scale = adjusted_scale;
489 }
453 } else { 490 } else {
454 scale = compute_default_scale(wlr_output, pending); 491 scale = compute_default_scale(wlr_output, pending);
455 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); 492 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
@@ -463,6 +500,10 @@ static void queue_output_config(struct output_config *oc,
463 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 500 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
464 oc->adaptive_sync); 501 oc->adaptive_sync);
465 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); 502 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
503 if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) {
504 sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring");
505 wlr_output_state_set_adaptive_sync_enabled(pending, false);
506 }
466 } 507 }
467 508
468 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 509 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
@@ -482,35 +523,12 @@ static void queue_output_config(struct output_config *oc,
482 } 523 }
483} 524}
484 525
485bool apply_output_config(struct output_config *oc, struct sway_output *output) { 526static bool finalize_output_config(struct output_config *oc, struct sway_output *output) {
486 if (output == root->fallback_output) { 527 if (output == root->fallback_output) {
487 return false; 528 return false;
488 } 529 }
489 530
490 struct wlr_output *wlr_output = output->wlr_output; 531 struct wlr_output *wlr_output = output->wlr_output;
491
492 // Flag to prevent the output mode event handler from calling us
493 output->enabling = (!oc || oc->enabled);
494
495 struct wlr_output_state pending = {0};
496 queue_output_config(oc, output, &pending);
497
498 if (!oc || oc->power != 0) {
499 output->current_mode = pending.mode;
500 }
501
502 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name);
503 if (!wlr_output_commit_state(wlr_output, &pending)) {
504 // Failed to commit output changes, maybe the output is missing a CRTC.
505 // Leave the output disabled for now and try again when the output gets
506 // the mode we asked for.
507 sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name);
508 output->enabling = false;
509 return false;
510 }
511
512 output->enabling = false;
513
514 if (oc && !oc->enabled) { 532 if (oc && !oc->enabled) {
515 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); 533 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
516 if (output->enabled) { 534 if (output->enabled) {
@@ -520,10 +538,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
520 return true; 538 return true;
521 } 539 }
522 540
523 if (config->reloading) {
524 output_damage_whole(output);
525 }
526
527 if (oc) { 541 if (oc) {
528 enum scale_filter_mode scale_filter_old = output->scale_filter; 542 enum scale_filter_mode scale_filter_old = output->scale_filter;
529 switch (oc->scale_filter) { 543 switch (oc->scale_filter) {
@@ -540,6 +554,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
540 if (scale_filter_old != output->scale_filter) { 554 if (scale_filter_old != output->scale_filter) {
541 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, 555 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name,
542 sway_output_scale_filter_to_string(output->scale_filter)); 556 sway_output_scale_filter_to_string(output->scale_filter));
557 wlr_damage_ring_add_whole(&output->scene_output->damage_ring);
543 } 558 }
544 } 559 }
545 560
@@ -569,25 +584,9 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
569 output->max_render_time = oc->max_render_time; 584 output->max_render_time = oc->max_render_time;
570 } 585 }
571 586
572 // Reconfigure all devices, since input config may have been applied before
573 // this output came online, and some config items (like map_to_output) are
574 // dependent on an output being present.
575 input_manager_configure_all_inputs();
576 // Reconfigure the cursor images, since the scale may have changed.
577 input_manager_configure_xcursor();
578 return true; 587 return true;
579} 588}
580 589
581bool test_output_config(struct output_config *oc, struct sway_output *output) {
582 if (output == root->fallback_output) {
583 return false;
584 }
585
586 struct wlr_output_state pending = {0};
587 queue_output_config(oc, output, &pending);
588 return wlr_output_test_state(output->wlr_output, &pending);
589}
590
591static void default_output_config(struct output_config *oc, 590static void default_output_config(struct output_config *oc,
592 struct wlr_output *wlr_output) { 591 struct wlr_output *wlr_output) {
593 oc->enabled = 1; 592 oc->enabled = 1;
@@ -607,143 +606,167 @@ static void default_output_config(struct output_config *oc,
607 oc->max_render_time = 0; 606 oc->max_render_time = 0;
608} 607}
609 608
610static struct output_config *get_output_config(char *identifier, 609// find_output_config returns a merged output_config containing all stored
611 struct sway_output *sway_output) { 610// configuration that applies to the specified output.
611struct output_config *find_output_config(struct sway_output *sway_output) {
612 const char *name = sway_output->wlr_output->name; 612 const char *name = sway_output->wlr_output->name;
613 struct output_config *oc = NULL;
613 614
614 struct output_config *oc_id_on_name = NULL; 615 struct output_config *result = new_output_config(name);
615 struct output_config *oc_name = NULL; 616 if (config->reloading) {
616 struct output_config *oc_id = NULL; 617 default_output_config(result, sway_output->wlr_output);
618 }
617 619
618 size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; 620 char id[128];
619 char *id_on_name = malloc(length); 621 output_get_identifier(id, sizeof(id), sway_output);
620 snprintf(id_on_name, length, "%s on %s", identifier, name);
621 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name);
622 if (i >= 0) {
623 oc_id_on_name = config->output_configs->items[i];
624 } else {
625 i = list_seq_find(config->output_configs, output_name_cmp, name);
626 if (i >= 0) {
627 oc_name = config->output_configs->items[i];
628 }
629 622
630 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 623 int i;
631 if (i >= 0) { 624 bool match = false;
632 oc_id = config->output_configs->items[i]; 625 if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
633 } 626 match = true;
627 oc = config->output_configs->items[i];
628 merge_output_config(result, oc);
634 } 629 }
635 630 if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
636 struct output_config *result = new_output_config("temp"); 631 match = true;
637 if (config->reloading) { 632 oc = config->output_configs->items[i];
638 default_output_config(result, sway_output->wlr_output); 633 merge_output_config(result, oc);
639 } 634 }
640 if (oc_id_on_name) { 635 if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
641 // Already have an identifier on name config, use that 636 match = true;
642 free(result->name); 637 oc = config->output_configs->items[i];
643 result->name = strdup(id_on_name); 638 merge_output_config(result, oc);
644 merge_output_config(result, oc_id_on_name); 639 }
645 } else if (oc_name && oc_id) { 640
646 // Generate a config named `<identifier> on <name>` which contains a 641 if (!match && !config->reloading) {
647 // merged copy of the identifier on name. This will make sure that both 642 // No name, identifier, or wildcard config. Since we are not
648 // identifier and name configs are respected, with identifier getting 643 // reloading with defaults, the output config will be empty, so
649 // priority 644 // just return NULL
650 struct output_config *temp = new_output_config(id_on_name); 645 free_output_config(result);
651 merge_output_config(temp, oc_name); 646 return NULL;
652 merge_output_config(temp, oc_id);
653 list_add(config->output_configs, temp);
654
655 free(result->name);
656 result->name = strdup(id_on_name);
657 merge_output_config(result, temp);
658
659 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
660 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
661 " (power %d) (max render time: %d)", result->name, result->enabled,
662 result->width, result->height, result->refresh_rate,
663 result->x, result->y, result->scale, result->transform,
664 result->background, result->background_option, result->power,
665 result->max_render_time);
666 } else if (oc_name) {
667 // No identifier config, just return a copy of the name config
668 free(result->name);
669 result->name = strdup(name);
670 merge_output_config(result, oc_name);
671 } else if (oc_id) {
672 // No name config, just return a copy of the identifier config
673 free(result->name);
674 result->name = strdup(identifier);
675 merge_output_config(result, oc_id);
676 } else {
677 i = list_seq_find(config->output_configs, output_name_cmp, "*");
678 if (i >= 0) {
679 // No name or identifier config, but there is a wildcard config
680 free(result->name);
681 result->name = strdup("*");
682 merge_output_config(result, config->output_configs->items[i]);
683 } else if (!config->reloading) {
684 // No name, identifier, or wildcard config. Since we are not
685 // reloading with defaults, the output config will be empty, so
686 // just return NULL
687 free_output_config(result);
688 result = NULL;
689 }
690 } 647 }
691 648
692 free(id_on_name);
693 return result; 649 return result;
694} 650}
695 651
696struct output_config *find_output_config(struct sway_output *output) { 652bool apply_output_configs(struct matched_output_config *configs,
697 char id[128]; 653 size_t configs_len, bool test_only) {
698 output_get_identifier(id, sizeof(id), output); 654 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
699 return get_output_config(id, output); 655 if (!states) {
700} 656 return false;
657 }
701 658
702void apply_output_config_to_outputs(struct output_config *oc) { 659 sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len);
703 // Try to find the output container and apply configuration now. If 660 for (size_t idx = 0; idx < configs_len; idx++) {
704 // this is during startup then there will be no container and config 661 struct matched_output_config *cfg = &configs[idx];
705 // will be applied during normal "new output" event from wlroots. 662 struct wlr_backend_output_state *backend_state = &states[idx];
706 bool wildcard = strcmp(oc->name, "*") == 0;
707 char id[128];
708 struct sway_output *sway_output, *tmp;
709 wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) {
710 char *name = sway_output->wlr_output->name;
711 output_get_identifier(id, sizeof(id), sway_output);
712 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
713 struct output_config *current = get_output_config(id, sway_output);
714 if (!current) {
715 // No stored output config matched, apply oc directly
716 sway_log(SWAY_DEBUG, "Applying oc directly");
717 current = new_output_config(oc->name);
718 merge_output_config(current, oc);
719 }
720 apply_output_config(current, sway_output);
721 free_output_config(current);
722 663
723 if (!wildcard) { 664 backend_state->output = cfg->output->wlr_output;
724 // Stop looking if the output config isn't applicable to all 665 wlr_output_state_init(&backend_state->base);
725 // outputs 666
726 break; 667 sway_log(SWAY_DEBUG, "Preparing config for %s",
727 } 668 cfg->output->wlr_output->name);
669 queue_output_config(cfg->config, cfg->output, &backend_state->base);
670 }
671
672 struct wlr_output_swapchain_manager swapchain_mgr;
673 wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend);
674
675 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);
676 if (!ok) {
677 sway_log(SWAY_ERROR, "Swapchain prepare failed");
678 goto out;
679 }
680
681 if (test_only) {
682 // The swapchain manager already did a test for us
683 goto out;
684 }
685
686 for (size_t idx = 0; idx < configs_len; idx++) {
687 struct matched_output_config *cfg = &configs[idx];
688 struct wlr_backend_output_state *backend_state = &states[idx];
689
690 struct wlr_scene_output_state_options opts = {
691 .swapchain = wlr_output_swapchain_manager_get_swapchain(
692 &swapchain_mgr, backend_state->output),
693 };
694 struct wlr_scene_output *scene_output = cfg->output->scene_output;
695 struct wlr_output_state *state = &backend_state->base;
696 if (!wlr_scene_output_build_state(scene_output, state, &opts)) {
697 sway_log(SWAY_ERROR, "Building output state for '%s' failed",
698 backend_state->output->name);
699 goto out;
728 } 700 }
729 } 701 }
730 702
703 ok = wlr_backend_commit(server.backend, states, configs_len);
704 if (!ok) {
705 sway_log(SWAY_ERROR, "Backend commit failed");
706 goto out;
707 }
708
709 sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len);
710
711 wlr_output_swapchain_manager_apply(&swapchain_mgr);
712
713 for (size_t idx = 0; idx < configs_len; idx++) {
714 struct matched_output_config *cfg = &configs[idx];
715 sway_log(SWAY_DEBUG, "Finalizing config for %s",
716 cfg->output->wlr_output->name);
717 finalize_output_config(cfg->config, cfg->output);
718 }
719
720out:
721 wlr_output_swapchain_manager_finish(&swapchain_mgr);
722 for (size_t idx = 0; idx < configs_len; idx++) {
723 struct wlr_backend_output_state *backend_state = &states[idx];
724 wlr_output_state_finish(&backend_state->base);
725 }
726 free(states);
727
728 // Reconfigure all devices, since input config may have been applied before
729 // this output came online, and some config items (like map_to_output) are
730 // dependent on an output being present.
731 input_manager_configure_all_input_mappings();
732 // Reconfigure the cursor images, since the scale may have changed.
733 input_manager_configure_xcursor();
734
731 struct sway_seat *seat; 735 struct sway_seat *seat;
732 wl_list_for_each(seat, &server.input->seats, link) { 736 wl_list_for_each(seat, &server.input->seats, link) {
733 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 737 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
734 cursor_rebase(seat->cursor); 738 cursor_rebase(seat->cursor);
735 } 739 }
740
741 return ok;
736} 742}
737 743
738void reset_outputs(void) { 744void apply_all_output_configs(void) {
739 struct output_config *oc = NULL; 745 size_t configs_len = wl_list_length(&root->all_outputs);
740 int i = list_seq_find(config->output_configs, output_name_cmp, "*"); 746 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
741 if (i >= 0) { 747 if (!configs) {
742 oc = config->output_configs->items[i]; 748 return;
743 } else {
744 oc = store_output_config(new_output_config("*"));
745 } 749 }
746 apply_output_config_to_outputs(oc); 750
751 int config_idx = 0;
752 struct sway_output *sway_output;
753 wl_list_for_each(sway_output, &root->all_outputs, link) {
754 if (sway_output == root->fallback_output) {
755 configs_len--;
756 continue;
757 }
758
759 struct matched_output_config *config = &configs[config_idx++];
760 config->output = sway_output;
761 config->config = find_output_config(sway_output);
762 }
763
764 apply_output_configs(configs, configs_len, false);
765 for (size_t idx = 0; idx < configs_len; idx++) {
766 struct matched_output_config *cfg = &configs[idx];
767 free_output_config(cfg->config);
768 }
769 free(configs);
747} 770}
748 771
749void free_output_config(struct output_config *oc) { 772void free_output_config(struct output_config *oc) {
@@ -810,7 +833,9 @@ static bool _spawn_swaybg(char **command) {
810 setenv("WAYLAND_SOCKET", wayland_socket_str, true); 833 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
811 834
812 execvp(command[0], command); 835 execvp(command[0], command);
813 sway_log_errno(SWAY_ERROR, "execvp failed"); 836 sway_log_errno(SWAY_ERROR, "failed to execute '%s' "
837 "(background configuration probably not applied)",
838 command[0]);
814 _exit(EXIT_FAILURE); 839 _exit(EXIT_FAILURE);
815 } 840 }
816 _exit(EXIT_SUCCESS); 841 _exit(EXIT_SUCCESS);
@@ -820,12 +845,13 @@ static bool _spawn_swaybg(char **command) {
820 sway_log_errno(SWAY_ERROR, "close failed"); 845 sway_log_errno(SWAY_ERROR, "close failed");
821 return false; 846 return false;
822 } 847 }
823 if (waitpid(pid, NULL, 0) < 0) { 848 int fork_status = 0;
849 if (waitpid(pid, &fork_status, 0) < 0) {
824 sway_log_errno(SWAY_ERROR, "waitpid failed"); 850 sway_log_errno(SWAY_ERROR, "waitpid failed");
825 return false; 851 return false;
826 } 852 }
827 853
828 return true; 854 return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS;
829} 855}
830 856
831bool spawn_swaybg(void) { 857bool 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
105static struct seat_attachment_config *seat_attachment_config_copy( 103static struct seat_attachment_config *seat_attachment_config_copy(
diff --git a/sway/criteria.c b/sway/criteria.c
index 97cf667e..e16b4fa8 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -19,6 +18,7 @@
19bool criteria_is_empty(struct criteria *criteria) { 18bool criteria_is_empty(struct criteria *criteria) {
20 return !criteria->title 19 return !criteria->title
21 && !criteria->shell 20 && !criteria->shell
21 && !criteria->all
22 && !criteria->app_id 22 && !criteria->app_id
23 && !criteria->con_mark 23 && !criteria->con_mark
24 && !criteria->con_id 24 && !criteria->con_id
@@ -96,7 +96,8 @@ void criteria_destroy(struct criteria *criteria) {
96 pattern_destroy(criteria->window_role); 96 pattern_destroy(criteria->window_role);
97#endif 97#endif
98 pattern_destroy(criteria->con_mark); 98 pattern_destroy(criteria->con_mark);
99 free(criteria->workspace); 99 pattern_destroy(criteria->workspace);
100 free(criteria->target);
100 free(criteria->cmdlist); 101 free(criteria->cmdlist);
101 free(criteria->raw); 102 free(criteria->raw);
102 free(criteria); 103 free(criteria);
@@ -189,7 +190,7 @@ static bool criteria_matches_view(struct criteria *criteria,
189 if (criteria->title) { 190 if (criteria->title) {
190 const char *title = view_get_title(view); 191 const char *title = view_get_title(view);
191 if (!title) { 192 if (!title) {
192 return false; 193 title = "";
193 } 194 }
194 195
195 switch (criteria->title->match_type) { 196 switch (criteria->title->match_type) {
@@ -209,7 +210,7 @@ static bool criteria_matches_view(struct criteria *criteria,
209 if (criteria->shell) { 210 if (criteria->shell) {
210 const char *shell = view_get_shell(view); 211 const char *shell = view_get_shell(view);
211 if (!shell) { 212 if (!shell) {
212 return false; 213 shell = "";
213 } 214 }
214 215
215 switch (criteria->shell->match_type) { 216 switch (criteria->shell->match_type) {
@@ -229,7 +230,7 @@ static bool criteria_matches_view(struct criteria *criteria,
229 if (criteria->app_id) { 230 if (criteria->app_id) {
230 const char *app_id = view_get_app_id(view); 231 const char *app_id = view_get_app_id(view);
231 if (!app_id) { 232 if (!app_id) {
232 return false; 233 app_id = "";
233 } 234 }
234 235
235 switch (criteria->app_id->match_type) { 236 switch (criteria->app_id->match_type) {
@@ -261,7 +262,7 @@ static bool criteria_matches_view(struct criteria *criteria,
261 if (criteria->class) { 262 if (criteria->class) {
262 const char *class = view_get_class(view); 263 const char *class = view_get_class(view);
263 if (!class) { 264 if (!class) {
264 return false; 265 class = "";
265 } 266 }
266 267
267 switch (criteria->class->match_type) { 268 switch (criteria->class->match_type) {
@@ -281,12 +282,12 @@ static bool criteria_matches_view(struct criteria *criteria,
281 if (criteria->instance) { 282 if (criteria->instance) {
282 const char *instance = view_get_instance(view); 283 const char *instance = view_get_instance(view);
283 if (!instance) { 284 if (!instance) {
284 return false; 285 instance = "";
285 } 286 }
286 287
287 switch (criteria->instance->match_type) { 288 switch (criteria->instance->match_type) {
288 case PATTERN_FOCUSED: 289 case PATTERN_FOCUSED:
289 if (focused && strcmp(instance, view_get_instance(focused))) { 290 if (focused && lenient_strcmp(instance, view_get_instance(focused))) {
290 return false; 291 return false;
291 } 292 }
292 break; 293 break;
@@ -301,12 +302,12 @@ static bool criteria_matches_view(struct criteria *criteria,
301 if (criteria->window_role) { 302 if (criteria->window_role) {
302 const char *window_role = view_get_window_role(view); 303 const char *window_role = view_get_window_role(view);
303 if (!window_role) { 304 if (!window_role) {
304 return false; 305 window_role = "";
305 } 306 }
306 307
307 switch (criteria->window_role->match_type) { 308 switch (criteria->window_role->match_type) {
308 case PATTERN_FOCUSED: 309 case PATTERN_FOCUSED:
309 if (focused && strcmp(window_role, view_get_window_role(focused))) { 310 if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) {
310 return false; 311 return false;
311 } 312 }
312 break; 313 break;
@@ -455,6 +456,7 @@ static enum atom_name parse_window_type(const char *type) {
455#endif 456#endif
456 457
457enum criteria_token { 458enum criteria_token {
459 T_ALL,
458 T_APP_ID, 460 T_APP_ID,
459 T_CON_ID, 461 T_CON_ID,
460 T_CON_MARK, 462 T_CON_MARK,
@@ -477,7 +479,9 @@ enum criteria_token {
477}; 479};
478 480
479static enum criteria_token token_from_name(char *name) { 481static enum criteria_token token_from_name(char *name) {
480 if (strcmp(name, "app_id") == 0) { 482 if (strcmp(name, "all") == 0) {
483 return T_ALL;
484 } else if (strcmp(name, "app_id") == 0) {
481 return T_APP_ID; 485 return T_APP_ID;
482 } else if (strcmp(name, "con_id") == 0) { 486 } else if (strcmp(name, "con_id") == 0) {
483 return T_CON_ID; 487 return T_CON_ID;
@@ -523,8 +527,8 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
523 return false; 527 return false;
524 } 528 }
525 529
526 // Require value, unless token is floating or tiled 530 // Require value, unless token is all, floating or tiled
527 if (!value && token != T_FLOATING && token != T_TILING) { 531 if (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) {
528 const char *fmt = "Token '%s' requires a value"; 532 const char *fmt = "Token '%s' requires a value";
529 int len = strlen(fmt) + strlen(name) - 1; 533 int len = strlen(fmt) + strlen(name) - 1;
530 error = malloc(len); 534 error = malloc(len);
@@ -534,6 +538,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
534 538
535 char *endptr = NULL; 539 char *endptr = NULL;
536 switch (token) { 540 switch (token) {
541 case T_ALL:
542 criteria->all = true;
543 break;
537 case T_TITLE: 544 case T_TITLE:
538 pattern_create(&criteria->title, value); 545 pattern_create(&criteria->title, value);
539 break; 546 break;
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
deleted file mode 100644
index c8d4502c..00000000
--- a/sway/desktop/desktop.c
+++ /dev/null
@@ -1,40 +0,0 @@
1#include "sway/tree/container.h"
2#include "sway/desktop.h"
3#include "sway/output.h"
4
5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
6 bool whole) {
7 for (int i = 0; i < root->outputs->length; ++i) {
8 struct sway_output *output = root->outputs->items[i];
9 struct wlr_box output_box;
10 wlr_output_layout_get_box(root->output_layout,
11 output->wlr_output, &output_box);
12 output_damage_surface(output, lx - output_box.x,
13 ly - output_box.y, surface, whole);
14 }
15}
16
17void desktop_damage_whole_container(struct sway_container *con) {
18 for (int i = 0; i < root->outputs->length; ++i) {
19 struct sway_output *output = root->outputs->items[i];
20 output_damage_whole_container(output, con);
21 }
22}
23
24void desktop_damage_box(struct wlr_box *box) {
25 for (int i = 0; i < root->outputs->length; ++i) {
26 struct sway_output *output = root->outputs->items[i];
27 output_damage_box(output, box);
28 }
29}
30
31void desktop_damage_view(struct sway_view *view) {
32 desktop_damage_whole_container(view->container);
33 struct wlr_box box = {
34 .x = view->container->current.content_x - view->geometry.x,
35 .y = view->container->current.content_y - view->geometry.y,
36 .width = view->surface->current.width,
37 .height = view->surface->current.height,
38 };
39 desktop_damage_box(&box);
40}
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c
index 82353038..f3af7aa1 100644
--- a/sway/desktop/idle_inhibit_v1.c
+++ b/sway/desktop/idle_inhibit_v1.c
@@ -1,5 +1,5 @@
1#include <stdlib.h> 1#include <stdlib.h>
2#include <wlr/types/wlr_idle.h> 2#include <wlr/types/wlr_idle_notify_v1.h>
3#include "log.h" 3#include "log.h"
4#include "sway/desktop/idle_inhibit_v1.h" 4#include "sway/desktop/idle_inhibit_v1.h"
5#include "sway/input/seat.h" 5#include "sway/input/seat.h"
@@ -11,7 +11,7 @@
11static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { 11static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) {
12 wl_list_remove(&inhibitor->link); 12 wl_list_remove(&inhibitor->link);
13 wl_list_remove(&inhibitor->destroy.link); 13 wl_list_remove(&inhibitor->destroy.link);
14 sway_idle_inhibit_v1_check_active(inhibitor->manager); 14 sway_idle_inhibit_v1_check_active();
15 free(inhibitor); 15 free(inhibitor);
16} 16}
17 17
@@ -34,7 +34,6 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
34 return; 34 return;
35 } 35 }
36 36
37 inhibitor->manager = manager;
38 inhibitor->mode = INHIBIT_IDLE_APPLICATION; 37 inhibitor->mode = INHIBIT_IDLE_APPLICATION;
39 inhibitor->wlr_inhibitor = wlr_inhibitor; 38 inhibitor->wlr_inhibitor = wlr_inhibitor;
40 wl_list_insert(&manager->inhibitors, &inhibitor->link); 39 wl_list_insert(&manager->inhibitors, &inhibitor->link);
@@ -42,33 +41,34 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
42 inhibitor->destroy.notify = handle_destroy; 41 inhibitor->destroy.notify = handle_destroy;
43 wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); 42 wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy);
44 43
45 sway_idle_inhibit_v1_check_active(manager); 44 sway_idle_inhibit_v1_check_active();
46} 45}
47 46
48void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, 47void 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
67struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( 67struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view(
68 struct sway_view *view) { 68 struct sway_view *view) {
69 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
69 struct sway_idle_inhibitor_v1 *inhibitor; 70 struct sway_idle_inhibitor_v1 *inhibitor;
70 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, 71 wl_list_for_each(inhibitor, &manager->inhibitors, link) {
71 link) {
72 if (inhibitor->mode != INHIBIT_IDLE_APPLICATION && 72 if (inhibitor->mode != INHIBIT_IDLE_APPLICATION &&
73 inhibitor->view == view) { 73 inhibitor->view == view) {
74 return inhibitor; 74 return inhibitor;
@@ -79,9 +79,9 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view(
79 79
80struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( 80struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view(
81 struct sway_view *view) { 81 struct sway_view *view) {
82 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
82 struct sway_idle_inhibitor_v1 *inhibitor; 83 struct sway_idle_inhibitor_v1 *inhibitor;
83 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, 84 wl_list_for_each(inhibitor, &manager->inhibitors, link) {
84 link) {
85 if (inhibitor->mode == INHIBIT_IDLE_APPLICATION && 85 if (inhibitor->mode == INHIBIT_IDLE_APPLICATION &&
86 view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { 86 view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) {
87 return inhibitor; 87 return inhibitor;
@@ -130,8 +130,8 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) {
130 return false; 130 return false;
131} 131}
132 132
133void sway_idle_inhibit_v1_check_active( 133void 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
145struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( 145bool 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 */
18static 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
47void 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
63void 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
78struct 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
103struct 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
141static 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
177static 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
183struct 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
234static 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
241struct 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
264const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) {
265 const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token);
266 return token;
267}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index d44d6338..4b2584b6 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -3,10 +3,12 @@
3#include <string.h> 3#include <string.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/types/wlr_layer_shell_v1.h> 5#include <wlr/types/wlr_layer_shell_v1.h>
6#include <wlr/types/wlr_output_damage.h>
7#include <wlr/types/wlr_output.h> 6#include <wlr/types/wlr_output.h>
7#include <wlr/types/wlr_scene.h>
8#include <wlr/types/wlr_subcompositor.h> 8#include <wlr/types/wlr_subcompositor.h>
9#include <wlr/types/wlr_xdg_shell.h>
9#include "log.h" 10#include "log.h"
11#include "sway/scene_descriptor.h"
10#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 13#include "sway/input/cursor.h"
12#include "sway/input/input-manager.h" 14#include "sway/input/input-manager.h"
@@ -17,162 +19,55 @@
17#include "sway/tree/arrange.h" 19#include "sway/tree/arrange.h"
18#include "sway/tree/workspace.h" 20#include "sway/tree/workspace.h"
19 21
20static void apply_exclusive(struct wlr_box *usable_area, 22struct 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
93static void arrange_layer(struct sway_output *output, struct wl_list *list, 55static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area,
94 struct wlr_box *usable_area, bool exclusive) { 56 struct wlr_box *usable_area, struct wlr_scene_tree *tree) {
95 struct sway_layer_surface *sway_layer; 57 struct wlr_scene_node *node;
96 struct wlr_box full_area = { 0 }; 58 wl_list_for_each(node, &tree->children, link) {
97 wlr_output_effective_resolution(output->wlr_output, 59 struct sway_layer_surface *surface = scene_descriptor_try_get(node,
98 &full_area.width, &full_area.height); 60 SWAY_SCENE_DESC_LAYER_SHELL);
99 wl_list_for_each(sway_layer, list, link) { 61 // surface could be null during destruction
100 struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; 62 if (!surface) {
101 struct wlr_layer_surface_v1_state *state = &layer->current;
102 if (exclusive != (state->exclusive_zone > 0)) {
103 continue; 63 continue;
104 } 64 }
105 struct wlr_box bounds; 65
106 if (state->exclusive_zone == -1) { 66 if (!surface->scene->layer_surface->initialized) {
107 bounds = full_area;
108 } else {
109 bounds = *usable_area;
110 }
111 struct wlr_box box = {
112 .width = state->desired_width,
113 .height = state->desired_height
114 };
115 // Horizontal axis
116 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
117 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
118 if (box.width == 0) {
119 box.x = bounds.x;
120 } else if ((state->anchor & both_horiz) == both_horiz) {
121 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
122 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
123 box.x = bounds.x;
124 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
125 box.x = bounds.x + (bounds.width - box.width);
126 } else {
127 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
128 }
129 // Vertical axis
130 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
131 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
132 if (box.height == 0) {
133 box.y = bounds.y;
134 } else if ((state->anchor & both_vert) == both_vert) {
135 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
136 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
137 box.y = bounds.y;
138 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
139 box.y = bounds.y + (bounds.height - box.height);
140 } else {
141 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
142 }
143 // Margin
144 if (box.width == 0) {
145 box.x += state->margin.left;
146 box.width = bounds.width -
147 (state->margin.left + state->margin.right);
148 } else if ((state->anchor & both_horiz) == both_horiz) {
149 // don't apply margins
150 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
151 box.x += state->margin.left;
152 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
153 box.x -= state->margin.right;
154 }
155 if (box.height == 0) {
156 box.y += state->margin.top;
157 box.height = bounds.height -
158 (state->margin.top + state->margin.bottom);
159 } else if ((state->anchor & both_vert) == both_vert) {
160 // don't apply margins
161 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
162 box.y += state->margin.top;
163 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
164 box.y -= state->margin.bottom;
165 }
166 if (!sway_assert(box.width >= 0 && box.height >= 0,
167 "Expected layer surface to have positive size")) {
168 continue; 67 continue;
169 } 68 }
170 // Apply 69
171 sway_layer->geo = box; 70 wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area);
172 apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
173 state->margin.top, state->margin.right,
174 state->margin.bottom, state->margin.left);
175 wlr_layer_surface_v1_configure(layer, box.width, box.height);
176 } 71 }
177} 72}
178 73
@@ -180,81 +75,94 @@ void arrange_layers(struct sway_output *output) {
180 struct wlr_box usable_area = { 0 }; 75 struct wlr_box usable_area = { 0 };
181 wlr_output_effective_resolution(output->wlr_output, 76 wlr_output_effective_resolution(output->wlr_output,
182 &usable_area.width, &usable_area.height); 77 &usable_area.width, &usable_area.height);
78 const struct wlr_box full_area = usable_area;
79
80 arrange_surface(output, &full_area, &usable_area, output->layers.shell_background);
81 arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom);
82 arrange_surface(output, &full_area, &usable_area, output->layers.shell_top);
83 arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay);
183 84
184 // Arrange exclusive surfaces from top->bottom 85 if (!wlr_box_equal(&usable_area, &output->usable_area)) {
185 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
186 &usable_area, true);
187 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
188 &usable_area, true);
189 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
190 &usable_area, true);
191 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
192 &usable_area, true);
193
194 if (memcmp(&usable_area, &output->usable_area,
195 sizeof(struct wlr_box)) != 0) {
196 sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); 86 sway_log(SWAY_DEBUG, "Usable area changed, rearranging output");
197 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 87 output->usable_area = usable_area;
198 arrange_output(output); 88 arrange_output(output);
89 } else {
90 arrange_popups(root->layers.popup);
199 } 91 }
92}
200 93
201 // Arrange non-exclusive surfaces from top->bottom 94static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output,
202 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 95 enum zwlr_layer_shell_v1_layer type) {
203 &usable_area, false); 96 switch (type) {
204 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], 97 case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:
205 &usable_area, false); 98 return output->layers.shell_background;
206 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], 99 case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:
207 &usable_area, false); 100 return output->layers.shell_bottom;
208 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], 101 case ZWLR_LAYER_SHELL_V1_LAYER_TOP:
209 &usable_area, false); 102 return output->layers.shell_top;
210 103 case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY:
211 // Find topmost keyboard interactive layer, if such a layer exists 104 return output->layers.shell_overlay;
212 uint32_t layers_above_shell[] = {
213 ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
214 ZWLR_LAYER_SHELL_V1_LAYER_TOP,
215 };
216 size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
217 struct sway_layer_surface *layer, *topmost = NULL;
218 for (size_t i = 0; i < nlayers; ++i) {
219 wl_list_for_each_reverse(layer,
220 &output->layers[layers_above_shell[i]], link) {
221 if (layer->layer_surface->current.keyboard_interactive &&
222 layer->layer_surface->mapped) {
223 topmost = layer;
224 break;
225 }
226 }
227 if (topmost != NULL) {
228 break;
229 }
230 } 105 }
231 106
232 struct sway_seat *seat; 107 sway_assert(false, "unreachable");
233 wl_list_for_each(seat, &server.input->seats, link) { 108 return NULL;
234 if (topmost != NULL) { 109}
235 seat_set_focus_layer(seat, topmost->layer_surface); 110
236 } else if (seat->focused_layer && 111static struct sway_layer_surface *sway_layer_surface_create(
237 !seat->focused_layer->current.keyboard_interactive) { 112 struct wlr_scene_layer_surface_v1 *scene) {
238 seat_set_focus_layer(seat, NULL); 113 struct sway_layer_surface *surface = calloc(1, sizeof(*surface));
239 } 114 if (!surface) {
115 sway_log(SWAY_ERROR, "Could not allocate a scene_layer surface");
116 return NULL;
240 } 117 }
118
119 struct wlr_scene_tree *popups = wlr_scene_tree_create(root->layers.popup);
120 if (!popups) {
121 sway_log(SWAY_ERROR, "Could not allocate a scene_layer popup node");
122 free(surface);
123 return NULL;
124 }
125
126 surface->desc.relative = &scene->tree->node;
127
128 if (!scene_descriptor_assign(&popups->node,
129 SWAY_SCENE_DESC_POPUP, &surface->desc)) {
130 sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor");
131 wlr_scene_node_destroy(&popups->node);
132 free(surface);
133 return NULL;
134 }
135
136 surface->tree = scene->tree;
137 surface->scene = scene;
138 surface->layer_surface = scene->layer_surface;
139 surface->popups = popups;
140 surface->layer_surface->data = surface;
141
142 return surface;
241} 143}
242 144
243static struct sway_layer_surface *find_mapped_layer_by_client( 145static struct sway_layer_surface *find_mapped_layer_by_client(
244 struct wl_client *client, struct wlr_output *ignore_output) { 146 struct wl_client *client, struct sway_output *ignore_output) {
245 for (int i = 0; i < root->outputs->length; ++i) { 147 for (int i = 0; i < root->outputs->length; ++i) {
246 struct sway_output *output = root->outputs->items[i]; 148 struct sway_output *output = root->outputs->items[i];
247 if (output->wlr_output == ignore_output) { 149 if (output == ignore_output) {
248 continue; 150 continue;
249 } 151 }
250 // For now we'll only check the overlay layer 152 // For now we'll only check the overlay layer
251 struct sway_layer_surface *lsurface; 153 struct wlr_scene_node *node;
252 wl_list_for_each(lsurface, 154 wl_list_for_each (node, &output->layers.shell_overlay->children, link) {
253 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { 155 struct sway_layer_surface *surface = scene_descriptor_try_get(node,
254 struct wl_resource *resource = lsurface->layer_surface->resource; 156 SWAY_SCENE_DESC_LAYER_SHELL);
157 if (!surface) {
158 continue;
159 }
160
161 struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface;
162 struct wl_resource *resource = layer_surface->resource;
255 if (wl_resource_get_client(resource) == client 163 if (wl_resource_get_client(resource) == client
256 && lsurface->layer_surface->mapped) { 164 && layer_surface->surface->mapped) {
257 return lsurface; 165 return surface;
258 } 166 }
259 } 167 }
260 } 168 }
@@ -262,293 +170,142 @@ static struct sway_layer_surface *find_mapped_layer_by_client(
262} 170}
263 171
264static void handle_output_destroy(struct wl_listener *listener, void *data) { 172static void handle_output_destroy(struct wl_listener *listener, void *data) {
265 struct sway_layer_surface *sway_layer = 173 struct sway_layer_surface *layer =
266 wl_container_of(listener, sway_layer, output_destroy); 174 wl_container_of(listener, layer, output_destroy);
267 // Determine if this layer is being used by an exclusive client. If it is,
268 // try and find another layer owned by this client to pass focus to.
269 struct sway_seat *seat = input_manager_get_default_seat();
270 struct wl_client *client =
271 wl_resource_get_client(sway_layer->layer_surface->resource);
272 bool set_focus = seat->exclusive_client == client;
273
274 if (set_focus) {
275 struct sway_layer_surface *layer =
276 find_mapped_layer_by_client(client, sway_layer->layer_surface->output);
277 if (layer) {
278 seat_set_focus_layer(seat, layer->layer_surface);
279 }
280 }
281 175
282 wlr_layer_surface_v1_destroy(sway_layer->layer_surface); 176 layer->output = NULL;
177 wlr_scene_node_destroy(&layer->scene->tree->node);
283} 178}
284 179
285static void handle_surface_commit(struct wl_listener *listener, void *data) { 180static void handle_node_destroy(struct wl_listener *listener, void *data) {
286 struct sway_layer_surface *layer = 181 struct sway_layer_surface *layer =
287 wl_container_of(listener, layer, surface_commit); 182 wl_container_of(listener, layer, node_destroy);
288 struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface;
289 struct wlr_output *wlr_output = layer_surface->output;
290 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
291 struct sway_output *output = wlr_output->data;
292 struct wlr_box old_extent = layer->extent;
293
294 bool layer_changed = false;
295 if (layer_surface->current.committed != 0
296 || layer->mapped != layer_surface->mapped) {
297 layer->mapped = layer_surface->mapped;
298 layer_changed = layer->layer != layer_surface->current.layer;
299 if (layer_changed) {
300 wl_list_remove(&layer->link);
301 wl_list_insert(&output->layers[layer_surface->current.layer],
302 &layer->link);
303 layer->layer = layer_surface->current.layer;
304 }
305 arrange_layers(output);
306 }
307
308 wlr_surface_get_extends(layer_surface->surface, &layer->extent);
309 layer->extent.x += layer->geo.x;
310 layer->extent.y += layer->geo.y;
311 183
312 bool extent_changed = 184 // destroy the scene descriptor straight away if it exists, otherwise
313 memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0; 185 // we will try to reflow still considering the destroyed node.
314 if (extent_changed || layer_changed) { 186 scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL);
315 output_damage_box(output, &old_extent);
316 output_damage_surface(output, layer->geo.x, layer->geo.y,
317 layer_surface->surface, true);
318 } else {
319 output_damage_surface(output, layer->geo.x, layer->geo.y,
320 layer_surface->surface, false);
321 }
322 187
323 transaction_commit_dirty(); 188 // Determine if this layer is being used by an exclusive client. If it is,
324} 189 // try and find another layer owned by this client to pass focus to.
325 190 struct sway_seat *seat = input_manager_get_default_seat();
326static void unmap(struct sway_layer_surface *sway_layer) { 191 struct wl_client *client =
327 struct sway_seat *seat; 192 wl_resource_get_client(layer->layer_surface->resource);
328 wl_list_for_each(seat, &server.input->seats, link) { 193 if (!server.session_lock.lock) {
329 if (seat->focused_layer == sway_layer->layer_surface) { 194 struct sway_layer_surface *consider_layer =
330 seat_set_focus_layer(seat, NULL); 195 find_mapped_layer_by_client(client, layer->output);
196 if (consider_layer) {
197 seat_set_focus_layer(seat, consider_layer->layer_surface);
331 } 198 }
332 } 199 }
333 200
334 cursor_rebase_all(); 201 if (layer->output) {
335 202 arrange_layers(layer->output);
336 struct wlr_output *wlr_output = sway_layer->layer_surface->output; 203 transaction_commit_dirty();
337 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
338 struct sway_output *output = wlr_output->data;
339 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
340 sway_layer->layer_surface->surface, true);
341}
342
343static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface);
344
345static void handle_destroy(struct wl_listener *listener, void *data) {
346 struct sway_layer_surface *sway_layer =
347 wl_container_of(listener, sway_layer, destroy);
348 sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)",
349 sway_layer->layer_surface->namespace);
350 if (sway_layer->layer_surface->mapped) {
351 unmap(sway_layer);
352 }
353
354 struct sway_layer_subsurface *subsurface, *subsurface_tmp;
355 wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) {
356 layer_subsurface_destroy(subsurface);
357 } 204 }
358 205
359 wl_list_remove(&sway_layer->link); 206 wlr_scene_node_destroy(&layer->popups->node);
360 wl_list_remove(&sway_layer->destroy.link);
361 wl_list_remove(&sway_layer->map.link);
362 wl_list_remove(&sway_layer->unmap.link);
363 wl_list_remove(&sway_layer->surface_commit.link);
364 wl_list_remove(&sway_layer->new_popup.link);
365 wl_list_remove(&sway_layer->new_subsurface.link);
366
367 struct wlr_output *wlr_output = sway_layer->layer_surface->output;
368 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
369 struct sway_output *output = wlr_output->data;
370 arrange_layers(output);
371 transaction_commit_dirty();
372 wl_list_remove(&sway_layer->output_destroy.link);
373 sway_layer->layer_surface->output = NULL;
374
375 free(sway_layer);
376}
377 207
378static void handle_map(struct wl_listener *listener, void *data) { 208 wl_list_remove(&layer->map.link);
379 struct sway_layer_surface *sway_layer = wl_container_of(listener, 209 wl_list_remove(&layer->unmap.link);
380 sway_layer, map); 210 wl_list_remove(&layer->surface_commit.link);
381 struct wlr_output *wlr_output = sway_layer->layer_surface->output; 211 wl_list_remove(&layer->node_destroy.link);
382 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); 212 wl_list_remove(&layer->output_destroy.link);
383 struct sway_output *output = wlr_output->data;
384 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
385 sway_layer->layer_surface->surface, true);
386 wlr_surface_send_enter(sway_layer->layer_surface->surface,
387 sway_layer->layer_surface->output);
388 cursor_rebase_all();
389}
390 213
391static void handle_unmap(struct wl_listener *listener, void *data) { 214 layer->layer_surface->data = NULL;
392 struct sway_layer_surface *sway_layer = wl_container_of(
393 listener, sway_layer, unmap);
394 unmap(sway_layer);
395}
396 215
397static void subsurface_damage(struct sway_layer_subsurface *subsurface, 216 free(layer);
398 bool whole) {
399 struct sway_layer_surface *layer = subsurface->layer_surface;
400 struct wlr_output *wlr_output = layer->layer_surface->output;
401 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
402 struct sway_output *output = wlr_output->data;
403 int ox = subsurface->wlr_subsurface->current.x + layer->geo.x;
404 int oy = subsurface->wlr_subsurface->current.y + layer->geo.y;
405 output_damage_surface(
406 output, ox, oy, subsurface->wlr_subsurface->surface, whole);
407} 217}
408 218
409static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { 219static void handle_surface_commit(struct wl_listener *listener, void *data) {
410 struct sway_layer_subsurface *subsurface = 220 struct sway_layer_surface *surface =
411 wl_container_of(listener, subsurface, unmap); 221 wl_container_of(listener, surface, surface_commit);
412 subsurface_damage(subsurface, true);
413}
414
415static void subsurface_handle_map(struct wl_listener *listener, void *data) {
416 struct sway_layer_subsurface *subsurface =
417 wl_container_of(listener, subsurface, map);
418 subsurface_damage(subsurface, true);
419}
420
421static void subsurface_handle_commit(struct wl_listener *listener, void *data) {
422 struct sway_layer_subsurface *subsurface =
423 wl_container_of(listener, subsurface, commit);
424 subsurface_damage(subsurface, false);
425}
426
427static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) {
428 wl_list_remove(&subsurface->link);
429 wl_list_remove(&subsurface->map.link);
430 wl_list_remove(&subsurface->unmap.link);
431 wl_list_remove(&subsurface->destroy.link);
432 wl_list_remove(&subsurface->commit.link);
433 free(subsurface);
434}
435
436static void subsurface_handle_destroy(struct wl_listener *listener,
437 void *data) {
438 struct sway_layer_subsurface *subsurface =
439 wl_container_of(listener, subsurface, destroy);
440 layer_subsurface_destroy(subsurface);
441}
442 222
443static struct sway_layer_subsurface *create_subsurface( 223 struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface;
444 struct wlr_subsurface *wlr_subsurface, 224 if (!layer_surface->initialized) {
445 struct sway_layer_surface *layer_surface) { 225 return;
446 struct sway_layer_subsurface *subsurface =
447 calloc(1, sizeof(struct sway_layer_subsurface));
448 if (subsurface == NULL) {
449 return NULL;
450 } 226 }
451 227
452 subsurface->wlr_subsurface = wlr_subsurface; 228 uint32_t committed = layer_surface->current.committed;
453 subsurface->layer_surface = layer_surface; 229 if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) {
454 wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); 230 enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer;
455 231 struct wlr_scene_tree *output_layer = sway_layer_get_scene(
456 subsurface->map.notify = subsurface_handle_map; 232 surface->output, layer_type);
457 wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); 233 wlr_scene_node_reparent(&surface->scene->tree->node, output_layer);
458 subsurface->unmap.notify = subsurface_handle_unmap; 234 }
459 wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap);
460 subsurface->destroy.notify = subsurface_handle_destroy;
461 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
462 subsurface->commit.notify = subsurface_handle_commit;
463 wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit);
464
465 return subsurface;
466}
467
468static void handle_new_subsurface(struct wl_listener *listener, void *data) {
469 struct sway_layer_surface *sway_layer_surface =
470 wl_container_of(listener, sway_layer_surface, new_subsurface);
471 struct wlr_subsurface *wlr_subsurface = data;
472 create_subsurface(wlr_subsurface, sway_layer_surface);
473}
474
475 235
476static struct sway_layer_surface *popup_get_layer( 236 if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) {
477 struct sway_layer_popup *popup) { 237 surface->mapped = layer_surface->surface->mapped;
478 while (popup->parent_type == LAYER_PARENT_POPUP) { 238 arrange_layers(surface->output);
479 popup = popup->parent_popup; 239 transaction_commit_dirty();
480 } 240 }
481 return popup->parent_layer;
482} 241}
483 242
484static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { 243static void handle_map(struct wl_listener *listener, void *data) {
485 struct wlr_xdg_popup *popup = layer_popup->wlr_popup; 244 struct sway_layer_surface *surface = wl_container_of(listener,
486 struct wlr_surface *surface = popup->base->surface; 245 surface, map);
487 int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; 246
488 int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; 247 struct wlr_layer_surface_v1 *layer_surface =
489 int ox = popup_sx, oy = popup_sy; 248 surface->scene->layer_surface;
490 struct sway_layer_surface *layer; 249
491 while (true) { 250 // focus on new surface
492 if (layer_popup->parent_type == LAYER_PARENT_POPUP) { 251 if (layer_surface->current.keyboard_interactive &&
493 layer_popup = layer_popup->parent_popup; 252 (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ||
494 ox += layer_popup->wlr_popup->current.geometry.x; 253 layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) {
495 oy += layer_popup->wlr_popup->current.geometry.y; 254 struct sway_seat *seat;
496 } else { 255 wl_list_for_each(seat, &server.input->seats, link) {
497 layer = layer_popup->parent_layer; 256 // but only if the currently focused layer has a lower precedence
498 ox += layer->geo.x; 257 if (!seat->focused_layer ||
499 oy += layer->geo.y; 258 seat->focused_layer->current.layer >= layer_surface->current.layer) {
500 break; 259 seat_set_focus_layer(seat, layer_surface);
260 }
501 } 261 }
262 arrange_layers(surface->output);
502 } 263 }
503 struct wlr_output *wlr_output = layer->layer_surface->output;
504 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
505 struct sway_output *output = wlr_output->data;
506 output_damage_surface(output, ox, oy, surface, whole);
507}
508 264
509static void popup_handle_map(struct wl_listener *listener, void *data) { 265 cursor_rebase_all();
510 struct sway_layer_popup *popup = wl_container_of(listener, popup, map);
511 struct sway_layer_surface *layer = popup_get_layer(popup);
512 struct wlr_output *wlr_output = layer->layer_surface->output;
513 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
514 wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output);
515 popup_damage(popup, true);
516} 266}
517 267
518static void popup_handle_unmap(struct wl_listener *listener, void *data) { 268static void handle_unmap(struct wl_listener *listener, void *data) {
519 struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); 269 struct sway_layer_surface *surface = wl_container_of(
520 popup_damage(popup, true); 270 listener, surface, unmap);
521} 271 struct sway_seat *seat;
272 wl_list_for_each(seat, &server.input->seats, link) {
273 if (seat->focused_layer == surface->layer_surface) {
274 seat_set_focus_layer(seat, NULL);
275 }
276 }
522 277
523static void popup_handle_commit(struct wl_listener *listener, void *data) { 278 cursor_rebase_all();
524 struct sway_layer_popup *popup = wl_container_of(listener, popup, commit);
525 popup_damage(popup, false);
526} 279}
527 280
528static void popup_handle_destroy(struct wl_listener *listener, void *data) { 281static void popup_handle_destroy(struct wl_listener *listener, void *data) {
529 struct sway_layer_popup *popup = 282 struct sway_layer_popup *popup =
530 wl_container_of(listener, popup, destroy); 283 wl_container_of(listener, popup, destroy);
531 284
532 wl_list_remove(&popup->map.link);
533 wl_list_remove(&popup->unmap.link);
534 wl_list_remove(&popup->destroy.link); 285 wl_list_remove(&popup->destroy.link);
286 wl_list_remove(&popup->new_popup.link);
535 wl_list_remove(&popup->commit.link); 287 wl_list_remove(&popup->commit.link);
536 free(popup); 288 free(popup);
537} 289}
538 290
539static void popup_unconstrain(struct sway_layer_popup *popup) { 291static void popup_unconstrain(struct sway_layer_popup *popup) {
540 struct sway_layer_surface *layer = popup_get_layer(popup);
541 struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; 292 struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
293 struct sway_output *output = popup->toplevel->output;
294
295 // if a client tries to create a popup while we are in the process of destroying
296 // its output, don't crash.
297 if (!output) {
298 return;
299 }
542 300
543 struct wlr_output *wlr_output = layer->layer_surface->output; 301 int lx, ly;
544 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); 302 wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly);
545 struct sway_output *output = wlr_output->data;
546 303
547 // the output box expressed in the coordinate system of the toplevel parent 304 // the output box expressed in the coordinate system of the toplevel parent
548 // of the popup 305 // of the popup
549 struct wlr_box output_toplevel_sx_box = { 306 struct wlr_box output_toplevel_sx_box = {
550 .x = -layer->geo.x, 307 .x = output->lx - lx,
551 .y = -layer->geo.y, 308 .y = output->ly - ly,
552 .width = output->width, 309 .width = output->width,
553 .height = output->height, 310 .height = output->height,
554 }; 311 };
@@ -556,32 +313,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) {
556 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 313 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
557} 314}
558 315
316static 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
559static void popup_handle_new_popup(struct wl_listener *listener, void *data); 323static void popup_handle_new_popup(struct wl_listener *listener, void *data);
560 324
561static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, 325static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup,
562 enum layer_parent parent_type, void *parent) { 326 struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) {
563 struct sway_layer_popup *popup = 327 struct sway_layer_popup *popup = calloc(1, sizeof(*popup));
564 calloc(1, sizeof(struct sway_layer_popup));
565 if (popup == NULL) { 328 if (popup == NULL) {
566 return NULL; 329 return NULL;
567 } 330 }
568 331
332 popup->toplevel = toplevel;
569 popup->wlr_popup = wlr_popup; 333 popup->wlr_popup = wlr_popup;
570 popup->parent_type = parent_type; 334 popup->scene = wlr_scene_xdg_surface_create(parent,
571 popup->parent_layer = parent; 335 wlr_popup->base);
336
337 if (!popup->scene) {
338 free(popup);
339 return NULL;
340 }
572 341
573 popup->map.notify = popup_handle_map;
574 wl_signal_add(&wlr_popup->base->events.map, &popup->map);
575 popup->unmap.notify = popup_handle_unmap;
576 wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
577 popup->destroy.notify = popup_handle_destroy; 342 popup->destroy.notify = popup_handle_destroy;
578 wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); 343 wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
579 popup->commit.notify = popup_handle_commit;
580 wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
581 popup->new_popup.notify = popup_handle_new_popup; 344 popup->new_popup.notify = popup_handle_new_popup;
582 wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); 345 wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
583 346 popup->commit.notify = popup_handle_commit;
584 popup_unconstrain(popup); 347 wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
585 348
586 return popup; 349 return popup;
587} 350}
@@ -590,19 +353,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
590 struct sway_layer_popup *sway_layer_popup = 353 struct sway_layer_popup *sway_layer_popup =
591 wl_container_of(listener, sway_layer_popup, new_popup); 354 wl_container_of(listener, sway_layer_popup, new_popup);
592 struct wlr_xdg_popup *wlr_popup = data; 355 struct wlr_xdg_popup *wlr_popup = data;
593 create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); 356 create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene);
594} 357}
595 358
596static void handle_new_popup(struct wl_listener *listener, void *data) { 359static void handle_new_popup(struct wl_listener *listener, void *data) {
597 struct sway_layer_surface *sway_layer_surface = 360 struct sway_layer_surface *sway_layer_surface =
598 wl_container_of(listener, sway_layer_surface, new_popup); 361 wl_container_of(listener, sway_layer_surface, new_popup);
599 struct wlr_xdg_popup *wlr_popup = data; 362 struct wlr_xdg_popup *wlr_popup = data;
600 create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); 363 create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups);
601}
602
603struct sway_layer_surface *layer_from_wlr_layer_surface_v1(
604 struct wlr_layer_surface_v1 *layer_surface) {
605 return layer_surface->data;
606} 364}
607 365
608void handle_layer_shell_surface(struct wl_listener *listener, void *data) { 366void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
@@ -634,10 +392,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
634 sway_log(SWAY_ERROR, 392 sway_log(SWAY_ERROR,
635 "no output to auto-assign layer surface '%s' to", 393 "no output to auto-assign layer surface '%s' to",
636 layer_surface->namespace); 394 layer_surface->namespace);
637 // Note that layer_surface->output can be NULL
638 // here, but none of our destroy callbacks are
639 // registered yet so we don't have to make them
640 // handle that case.
641 wlr_layer_surface_v1_destroy(layer_surface); 395 wlr_layer_surface_v1_destroy(layer_surface);
642 return; 396 return;
643 } 397 }
@@ -646,44 +400,51 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
646 layer_surface->output = output->wlr_output; 400 layer_surface->output = output->wlr_output;
647 } 401 }
648 402
649 struct sway_layer_surface *sway_layer = 403 struct sway_output *output = layer_surface->output->data;
650 calloc(1, sizeof(struct sway_layer_surface)); 404
651 if (!sway_layer) { 405 enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer;
406 struct wlr_scene_tree *output_layer = sway_layer_get_scene(
407 output, layer_type);
408 struct wlr_scene_layer_surface_v1 *scene_surface =
409 wlr_scene_layer_surface_v1_create(output_layer, layer_surface);
410 if (!scene_surface) {
411 sway_log(SWAY_ERROR, "Could not allocate a layer_surface_v1");
412 return;
413 }
414
415 struct sway_layer_surface *surface =
416 sway_layer_surface_create(scene_surface);
417 if (!surface) {
418 wlr_layer_surface_v1_destroy(layer_surface);
419
420 sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface");
652 return; 421 return;
653 } 422 }
654 423
655 wl_list_init(&sway_layer->subsurfaces); 424 if (!scene_descriptor_assign(&scene_surface->tree->node,
425 SWAY_SCENE_DESC_LAYER_SHELL, surface)) {
426 sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor");
427 // destroying the layer_surface will also destroy its corresponding
428 // scene node
429 wlr_layer_surface_v1_destroy(layer_surface);
430 return;
431 }
656 432
657 sway_layer->surface_commit.notify = handle_surface_commit; 433 surface->output = output;
658 wl_signal_add(&layer_surface->surface->events.commit,
659 &sway_layer->surface_commit);
660
661 sway_layer->destroy.notify = handle_destroy;
662 wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy);
663 sway_layer->map.notify = handle_map;
664 wl_signal_add(&layer_surface->events.map, &sway_layer->map);
665 sway_layer->unmap.notify = handle_unmap;
666 wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap);
667 sway_layer->new_popup.notify = handle_new_popup;
668 wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup);
669 sway_layer->new_subsurface.notify = handle_new_subsurface;
670 wl_signal_add(&layer_surface->surface->events.new_subsurface,
671 &sway_layer->new_subsurface);
672
673 sway_layer->layer_surface = layer_surface;
674 layer_surface->data = sway_layer;
675 434
676 struct sway_output *output = layer_surface->output->data; 435 surface->surface_commit.notify = handle_surface_commit;
677 sway_layer->output_destroy.notify = handle_output_destroy; 436 wl_signal_add(&layer_surface->surface->events.commit,
678 wl_signal_add(&output->events.disable, &sway_layer->output_destroy); 437 &surface->surface_commit);
679 438 surface->map.notify = handle_map;
680 wl_list_insert(&output->layers[layer_surface->pending.layer], 439 wl_signal_add(&layer_surface->surface->events.map, &surface->map);
681 &sway_layer->link); 440 surface->unmap.notify = handle_unmap;
682 441 wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap);
683 // Temporarily set the layer's current state to pending 442 surface->new_popup.notify = handle_new_popup;
684 // So that we can easily arrange it 443 wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup);
685 struct wlr_layer_surface_v1_state old_state = layer_surface->current; 444
686 layer_surface->current = layer_surface->pending; 445 surface->output_destroy.notify = handle_output_destroy;
687 arrange_layers(output); 446 wl_signal_add(&output->events.disable, &surface->output_destroy);
688 layer_surface->current = old_state; 447
448 surface->node_destroy.notify = handle_node_destroy;
449 wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy);
689} 450}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 7bb9dab2..bd3de3fe 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -1,44 +1,61 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <strings.h> 3#include <strings.h>
5#include <time.h> 4#include <time.h>
6#include <wayland-server-core.h> 5#include <wayland-server-core.h>
7#include <wlr/backend/drm.h> 6#include <wlr/config.h>
8#include <wlr/backend/headless.h> 7#include <wlr/backend/headless.h>
8#include <wlr/render/swapchain.h>
9#include <wlr/render/wlr_renderer.h> 9#include <wlr/render/wlr_renderer.h>
10#include <wlr/types/wlr_buffer.h> 10#include <wlr/types/wlr_buffer.h>
11#include <wlr/types/wlr_drm_lease_v1.h> 11#include <wlr/types/wlr_gamma_control_v1.h>
12#include <wlr/types/wlr_matrix.h> 12#include <wlr/types/wlr_matrix.h>
13#include <wlr/types/wlr_output_damage.h>
14#include <wlr/types/wlr_output_layout.h> 13#include <wlr/types/wlr_output_layout.h>
14#include <wlr/types/wlr_output_management_v1.h>
15#include <wlr/types/wlr_output_power_management_v1.h>
15#include <wlr/types/wlr_output.h> 16#include <wlr/types/wlr_output.h>
16#include <wlr/types/wlr_presentation_time.h> 17#include <wlr/types/wlr_presentation_time.h>
17#include <wlr/types/wlr_compositor.h> 18#include <wlr/types/wlr_compositor.h>
18#include <wlr/util/region.h> 19#include <wlr/util/region.h>
20#include <wlr/util/transform.h>
19#include "config.h" 21#include "config.h"
20#include "log.h" 22#include "log.h"
21#include "sway/config.h" 23#include "sway/config.h"
22#include "sway/desktop/transaction.h" 24#include "sway/desktop/transaction.h"
23#include "sway/input/input-manager.h" 25#include "sway/input/input-manager.h"
24#include "sway/input/seat.h" 26#include "sway/input/seat.h"
27#include "sway/ipc-server.h"
25#include "sway/layers.h" 28#include "sway/layers.h"
26#include "sway/output.h" 29#include "sway/output.h"
30#include "sway/scene_descriptor.h"
27#include "sway/server.h" 31#include "sway/server.h"
28#include "sway/surface.h"
29#include "sway/tree/arrange.h" 32#include "sway/tree/arrange.h"
30#include "sway/tree/container.h" 33#include "sway/tree/container.h"
31#include "sway/tree/root.h" 34#include "sway/tree/root.h"
32#include "sway/tree/view.h" 35#include "sway/tree/view.h"
33#include "sway/tree/workspace.h" 36#include "sway/tree/workspace.h"
34 37
38#if WLR_HAS_DRM_BACKEND
39#include <wlr/backend/drm.h>
40#include <wlr/types/wlr_drm_lease_v1.h>
41#endif
42
43bool 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
35struct sway_output *output_by_name_or_id(const char *name_or_id) { 55struct sway_output *output_by_name_or_id(const char *name_or_id) {
36 for (int i = 0; i < root->outputs->length; ++i) { 56 for (int i = 0; i < root->outputs->length; ++i) {
37 struct sway_output *output = root->outputs->items[i]; 57 struct sway_output *output = root->outputs->items[i];
38 char identifier[128]; 58 if (output_match_name_or_id(output, name_or_id)) {
39 output_get_identifier(identifier, sizeof(identifier), output);
40 if (strcasecmp(identifier, name_or_id) == 0
41 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
42 return output; 59 return output;
43 } 60 }
44 } 61 }
@@ -48,536 +65,217 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) {
48struct sway_output *all_output_by_name_or_id(const char *name_or_id) { 65struct sway_output *all_output_by_name_or_id(const char *name_or_id) {
49 struct sway_output *output; 66 struct sway_output *output;
50 wl_list_for_each(output, &root->all_outputs, link) { 67 wl_list_for_each(output, &root->all_outputs, link) {
51 char identifier[128]; 68 if (output_match_name_or_id(output, name_or_id)) {
52 output_get_identifier(identifier, sizeof(identifier), output);
53 if (strcasecmp(identifier, name_or_id) == 0
54 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
55 return output; 69 return output;
56 } 70 }
57 } 71 }
58 return NULL; 72 return NULL;
59} 73}
60 74
61struct surface_iterator_data {
62 sway_surface_iterator_func_t user_iterator;
63 void *user_data;
64
65 struct sway_output *output;
66 struct sway_view *view;
67 double ox, oy;
68 int width, height;
69};
70
71static bool get_surface_box(struct surface_iterator_data *data,
72 struct wlr_surface *surface, int sx, int sy,
73 struct wlr_box *surface_box) {
74 struct sway_output *output = data->output;
75
76 if (!wlr_surface_has_buffer(surface)) {
77 return false;
78 }
79 75
80 int sw = surface->current.width; 76struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
81 int sh = surface->current.height; 77 struct sway_seat *seat = input_manager_current_seat();
82 78 struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node);
83 struct wlr_box box = { 79 if (!focus) {
84 .x = floor(data->ox + sx), 80 if (!output->workspaces->length) {
85 .y = floor(data->oy + sy), 81 return NULL;
86 .width = sw, 82 }
87 .height = sh, 83 return output->workspaces->items[0];
88 };
89 if (surface_box != NULL) {
90 memcpy(surface_box, &box, sizeof(struct wlr_box));
91 } 84 }
92 85 return focus->sway_workspace;
93 struct wlr_box output_box = {
94 .width = output->width,
95 .height = output->height,
96 };
97
98 struct wlr_box intersection;
99 return wlr_box_intersection(&intersection, &output_box, &box);
100} 86}
101 87
102static void output_for_each_surface_iterator(struct wlr_surface *surface, 88struct send_frame_done_data {
103 int sx, int sy, void *_data) { 89 struct timespec when;
104 struct surface_iterator_data *data = _data; 90 int msec_until_refresh;
105 91 struct sway_output *output;
106 struct wlr_box box; 92};
107 bool intersects = get_surface_box(data, surface, sx, sy, &box);
108 if (!intersects) {
109 return;
110 }
111 93
112 data->user_iterator(data->output, data->view, surface, &box, 94struct buffer_timer {
113 data->user_data); 95 struct wl_listener destroy;
114} 96 struct wl_event_source *frame_done_timer;
97};
115 98
116void output_surface_for_each_surface(struct sway_output *output, 99static int handle_buffer_timer(void *data) {
117 struct wlr_surface *surface, double ox, double oy, 100 struct wlr_scene_buffer *buffer = data;
118 sway_surface_iterator_func_t iterator, void *user_data) {
119 struct surface_iterator_data data = {
120 .user_iterator = iterator,
121 .user_data = user_data,
122 .output = output,
123 .view = NULL,
124 .ox = ox,
125 .oy = oy,
126 .width = surface->current.width,
127 .height = surface->current.height,
128 };
129
130 wlr_surface_for_each_surface(surface,
131 output_for_each_surface_iterator, &data);
132}
133 101
134void output_view_for_each_surface(struct sway_output *output, 102 struct timespec now;
135 struct sway_view *view, sway_surface_iterator_func_t iterator, 103 clock_gettime(CLOCK_MONOTONIC, &now);
136 void *user_data) { 104 wlr_scene_buffer_send_frame_done(buffer, &now);
137 struct surface_iterator_data data = { 105 return 0;
138 .user_iterator = iterator,
139 .user_data = user_data,
140 .output = output,
141 .view = view,
142 .ox = view->container->surface_x - output->lx
143 - view->geometry.x,
144 .oy = view->container->surface_y - output->ly
145 - view->geometry.y,
146 .width = view->container->current.content_width,
147 .height = view->container->current.content_height,
148 };
149
150 view_for_each_surface(view, output_for_each_surface_iterator, &data);
151} 106}
152 107
153void output_view_for_each_popup_surface(struct sway_output *output, 108static void handle_buffer_timer_destroy(struct wl_listener *listener,
154 struct sway_view *view, sway_surface_iterator_func_t iterator, 109 void *data) {
155 void *user_data) { 110 struct buffer_timer *timer = wl_container_of(listener, timer, destroy);
156 struct surface_iterator_data data = {
157 .user_iterator = iterator,
158 .user_data = user_data,
159 .output = output,
160 .view = view,
161 .ox = view->container->surface_x - output->lx
162 - view->geometry.x,
163 .oy = view->container->surface_y - output->ly
164 - view->geometry.y,
165 .width = view->container->current.content_width,
166 .height = view->container->current.content_height,
167 };
168
169 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data);
170}
171 111
172void output_layer_for_each_surface(struct sway_output *output, 112 wl_list_remove(&timer->destroy.link);
173 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 113 wl_event_source_remove(timer->frame_done_timer);
174 void *user_data) { 114 free(timer);
175 struct sway_layer_surface *layer_surface;
176 wl_list_for_each(layer_surface, layer_surfaces, link) {
177 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
178 layer_surface->layer_surface;
179 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
180 struct surface_iterator_data data = {
181 .user_iterator = iterator,
182 .user_data = user_data,
183 .output = output,
184 .view = NULL,
185 .ox = layer_surface->geo.x,
186 .oy = layer_surface->geo.y,
187 .width = surface->current.width,
188 .height = surface->current.height,
189 };
190 wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1,
191 output_for_each_surface_iterator, &data);
192 }
193} 115}
194 116
195void output_layer_for_each_toplevel_surface(struct sway_output *output, 117static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) {
196 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 118 struct buffer_timer *timer =
197 void *user_data) { 119 scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER);
198 struct sway_layer_surface *layer_surface; 120 if (timer) {
199 wl_list_for_each(layer_surface, layer_surfaces, link) { 121 return timer;
200 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
201 layer_surface->layer_surface;
202 output_surface_for_each_surface(output, wlr_layer_surface_v1->surface,
203 layer_surface->geo.x, layer_surface->geo.y, iterator,
204 user_data);
205 } 122 }
206}
207
208 123
209void output_layer_for_each_popup_surface(struct sway_output *output, 124 timer = calloc(1, sizeof(struct buffer_timer));
210 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 125 if (!timer) {
211 void *user_data) { 126 return NULL;
212 struct sway_layer_surface *layer_surface;
213 wl_list_for_each(layer_surface, layer_surfaces, link) {
214 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
215 layer_surface->layer_surface;
216 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
217 struct surface_iterator_data data = {
218 .user_iterator = iterator,
219 .user_data = user_data,
220 .output = output,
221 .view = NULL,
222 .ox = layer_surface->geo.x,
223 .oy = layer_surface->geo.y,
224 .width = surface->current.width,
225 .height = surface->current.height,
226 };
227 wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1,
228 output_for_each_surface_iterator, &data);
229 } 127 }
230}
231 128
232#if HAVE_XWAYLAND 129 timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop,
233void output_unmanaged_for_each_surface(struct sway_output *output, 130 handle_buffer_timer, buffer);
234 struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, 131 if (!timer->frame_done_timer) {
235 void *user_data) { 132 free(timer);
236 struct sway_xwayland_unmanaged *unmanaged_surface; 133 return NULL;
237 wl_list_for_each(unmanaged_surface, unmanaged, link) {
238 struct wlr_xwayland_surface *xsurface =
239 unmanaged_surface->wlr_xwayland_surface;
240 double ox = unmanaged_surface->lx - output->lx;
241 double oy = unmanaged_surface->ly - output->ly;
242
243 output_surface_for_each_surface(output, xsurface->surface, ox, oy,
244 iterator, user_data);
245 } 134 }
246}
247#endif
248 135
249void output_drag_icons_for_each_surface(struct sway_output *output, 136 scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer);
250 struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
251 void *user_data) {
252 struct sway_drag_icon *drag_icon;
253 wl_list_for_each(drag_icon, drag_icons, link) {
254 double ox = drag_icon->x - output->lx;
255 double oy = drag_icon->y - output->ly;
256
257 if (drag_icon->wlr_drag_icon->mapped) {
258 output_surface_for_each_surface(output,
259 drag_icon->wlr_drag_icon->surface, ox, oy,
260 iterator, user_data);
261 }
262 }
263}
264 137
265static void for_each_surface_container_iterator(struct sway_container *con, 138 timer->destroy.notify = handle_buffer_timer_destroy;
266 void *_data) { 139 wl_signal_add(&buffer->node.events.destroy, &timer->destroy);
267 if (!con->view || !view_is_visible(con->view)) {
268 return;
269 }
270 140
271 struct surface_iterator_data *data = _data; 141 return timer;
272 output_view_for_each_surface(data->output, con->view,
273 data->user_iterator, data->user_data);
274} 142}
275 143
276static void output_for_each_surface(struct sway_output *output, 144static void send_frame_done_iterator(struct wlr_scene_buffer *buffer,
277 sway_surface_iterator_func_t iterator, void *user_data) { 145 int x, int y, void *user_data) {
278 if (server.session_lock.locked) { 146 struct send_frame_done_data *data = user_data;
279 if (server.session_lock.lock == NULL) { 147 struct sway_output *output = data->output;
280 return; 148 int view_max_render_time = 0;
281 }
282 struct wlr_session_lock_surface_v1 *lock_surface;
283 wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
284 if (lock_surface->output != output->wlr_output) {
285 continue;
286 }
287 if (!lock_surface->mapped) {
288 continue;
289 }
290 149
291 output_surface_for_each_surface(output, lock_surface->surface, 150 if (buffer->primary_output != data->output->scene_output) {
292 0.0, 0.0, iterator, user_data);
293 }
294 return; 151 return;
295 } 152 }
296 153
297 if (output_has_opaque_overlay_layer_surface(output)) { 154 struct wlr_scene_node *current = &buffer->node;
298 goto overlay; 155 while (true) {
299 } 156 struct sway_view *view = scene_descriptor_try_get(current,
300 157 SWAY_SCENE_DESC_VIEW);
301 struct surface_iterator_data data = { 158 if (view) {
302 .user_iterator = iterator, 159 view_max_render_time = view->max_render_time;
303 .user_data = user_data, 160 break;
304 .output = output,
305 .view = NULL,
306 };
307
308 struct sway_workspace *workspace = output_get_active_workspace(output);
309 struct sway_container *fullscreen_con = root->fullscreen_global;
310 if (!fullscreen_con) {
311 if (!workspace) {
312 return;
313 }
314 fullscreen_con = workspace->current.fullscreen;
315 }
316 if (fullscreen_con) {
317 for_each_surface_container_iterator(fullscreen_con, &data);
318 container_for_each_child(fullscreen_con,
319 for_each_surface_container_iterator, &data);
320
321 // TODO: Show transient containers for fullscreen global
322 if (fullscreen_con == workspace->current.fullscreen) {
323 for (int i = 0; i < workspace->current.floating->length; ++i) {
324 struct sway_container *floater =
325 workspace->current.floating->items[i];
326 if (container_is_transient_for(floater, fullscreen_con)) {
327 for_each_surface_container_iterator(floater, &data);
328 }
329 }
330 } 161 }
331#if HAVE_XWAYLAND
332 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
333 iterator, user_data);
334#endif
335 } else {
336 output_layer_for_each_surface(output,
337 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
338 iterator, user_data);
339 output_layer_for_each_surface(output,
340 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
341 iterator, user_data);
342
343 workspace_for_each_container(workspace,
344 for_each_surface_container_iterator, &data);
345
346#if HAVE_XWAYLAND
347 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
348 iterator, user_data);
349#endif
350 output_layer_for_each_surface(output,
351 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
352 iterator, user_data);
353 }
354
355overlay:
356 output_layer_for_each_surface(output,
357 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
358 iterator, user_data);
359 output_drag_icons_for_each_surface(output, &root->drag_icons,
360 iterator, user_data);
361}
362
363static int scale_length(int length, int offset, float scale) {
364 return round((offset + length) * scale) - round(offset * scale);
365}
366
367void scale_box(struct wlr_box *box, float scale) {
368 box->width = scale_length(box->width, box->x, scale);
369 box->height = scale_length(box->height, box->y, scale);
370 box->x = round(box->x * scale);
371 box->y = round(box->y * scale);
372}
373 162
374struct sway_workspace *output_get_active_workspace(struct sway_output *output) { 163 if (!current->parent) {
375 struct sway_seat *seat = input_manager_current_seat(); 164 break;
376 struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node);
377 if (!focus) {
378 if (!output->workspaces->length) {
379 return NULL;
380 } 165 }
381 return output->workspaces->items[0];
382 }
383 return focus->sway_workspace;
384}
385 166
386bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { 167 current = &current->parent->node;
387 struct sway_layer_surface *sway_layer_surface;
388 wl_list_for_each(sway_layer_surface,
389 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) {
390 struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface;
391 pixman_box32_t output_box = {
392 .x2 = output->width,
393 .y2 = output->height,
394 };
395 pixman_region32_t surface_opaque_box;
396 pixman_region32_init(&surface_opaque_box);
397 pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region);
398 pixman_region32_translate(&surface_opaque_box,
399 sway_layer_surface->geo.x, sway_layer_surface->geo.y);
400 pixman_region_overlap_t contains =
401 pixman_region32_contains_rectangle(&surface_opaque_box, &output_box);
402 pixman_region32_fini(&surface_opaque_box);
403
404 if (contains == PIXMAN_REGION_IN) {
405 return true;
406 }
407 } 168 }
408 return false;
409}
410
411struct send_frame_done_data {
412 struct timespec when;
413 int msec_until_refresh;
414};
415
416static void send_frame_done_iterator(struct sway_output *output,
417 struct sway_view *view, struct wlr_surface *surface,
418 struct wlr_box *box, void *user_data) {
419 int view_max_render_time = 0;
420 if (view != NULL) {
421 view_max_render_time = view->max_render_time;
422 }
423
424 struct send_frame_done_data *data = user_data;
425 169
426 int delay = data->msec_until_refresh - output->max_render_time 170 int delay = data->msec_until_refresh - output->max_render_time
427 - view_max_render_time; 171 - view_max_render_time;
428 172
429 if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { 173 struct buffer_timer *timer = NULL;
430 wlr_surface_send_frame_done(surface, &data->when);
431 } else {
432 struct sway_surface *sway_surface = surface->data;
433 wl_event_source_timer_update(sway_surface->frame_done_timer, delay);
434 }
435}
436
437static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) {
438 output_for_each_surface(output, send_frame_done_iterator, data);
439}
440
441static void count_surface_iterator(struct sway_output *output,
442 struct sway_view *view, struct wlr_surface *surface,
443 struct wlr_box *box, void *data) {
444 size_t *n = data;
445 (*n)++;
446}
447 174
448static bool scan_out_fullscreen_view(struct sway_output *output, 175 if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) {
449 struct sway_view *view) { 176 timer = buffer_timer_get_or_create(buffer);
450 struct wlr_output *wlr_output = output->wlr_output;
451 struct sway_workspace *workspace = output->current.active_workspace;
452 if (!sway_assert(workspace, "Expected an active workspace")) {
453 return false;
454 } 177 }
455 178
456 if (server.session_lock.locked) { 179 if (timer) {
457 return false; 180 wl_event_source_timer_update(timer->frame_done_timer, delay);
181 } else {
182 wlr_scene_buffer_send_frame_done(buffer, &data->when);
458 } 183 }
184}
459 185
460 if (!wl_list_empty(&view->saved_buffers)) { 186static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output,
461 return false; 187 struct wlr_scene_buffer *buffer) {
188 // if we are scaling down, we should always choose linear
189 if (buffer->dst_width > 0 && buffer->dst_height > 0 && (
190 buffer->dst_width < buffer->buffer_width ||
191 buffer->dst_height < buffer->buffer_height)) {
192 return WLR_SCALE_FILTER_BILINEAR;
462 } 193 }
463 194
464 for (int i = 0; i < workspace->current.floating->length; ++i) { 195 switch (output->scale_filter) {
465 struct sway_container *floater = 196 case SCALE_FILTER_LINEAR:
466 workspace->current.floating->items[i]; 197 return WLR_SCALE_FILTER_BILINEAR;
467 if (container_is_transient_for(floater, view->container)) { 198 case SCALE_FILTER_NEAREST:
468 return false; 199 return WLR_SCALE_FILTER_NEAREST;
469 } 200 default:
201 abort(); // unreachable
470 } 202 }
203}
471 204
472#if HAVE_XWAYLAND 205static void output_configure_scene(struct sway_output *output,
473 if (!wl_list_empty(&root->xwayland_unmanaged)) { 206 struct wlr_scene_node *node, float opacity) {
474 return false; 207 if (!node->enabled) {
475 } 208 return;
476#endif
477
478 if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) {
479 return false;
480 }
481 if (!wl_list_empty(&root->drag_icons)) {
482 return false;
483 } 209 }
484 210
485 struct wlr_surface *surface = view->surface; 211 struct sway_container *con =
486 if (surface == NULL) { 212 scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER);
487 return false; 213 if (con) {
488 } 214 opacity = con->alpha;
489 size_t n_surfaces = 0;
490 output_view_for_each_surface(output, view,
491 count_surface_iterator, &n_surfaces);
492 if (n_surfaces != 1) {
493 return false;
494 } 215 }
495 216
496 if (surface->buffer == NULL) { 217 if (node->type == WLR_SCENE_NODE_BUFFER) {
497 return false; 218 struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
498 }
499 219
500 if ((float)surface->current.scale != wlr_output->scale || 220 // hack: don't call the scene setter because that will damage all outputs
501 surface->current.transform != wlr_output->transform) { 221 // We don't want to damage outputs that aren't our current output that
502 return false; 222 // we're configuring
503 } 223 buffer->filter_mode = get_scale_filter(output, buffer);
504 224
505 wlr_output_attach_buffer(wlr_output, &surface->buffer->base); 225 wlr_scene_buffer_set_opacity(buffer, opacity);
506 if (!wlr_output_test(wlr_output)) { 226 } else if (node->type == WLR_SCENE_NODE_TREE) {
507 return false; 227 struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);
228 struct wlr_scene_node *node;
229 wl_list_for_each(node, &tree->children, link) {
230 output_configure_scene(output, node, opacity);
231 }
508 } 232 }
509
510 wlr_presentation_surface_sampled_on_output(server.presentation, surface,
511 wlr_output);
512
513 return wlr_output_commit(wlr_output);
514} 233}
515 234
516static int output_repaint_timer_handler(void *data) { 235static int output_repaint_timer_handler(void *data) {
517 struct sway_output *output = data; 236 struct sway_output *output = data;
518 if (output->wlr_output == NULL) {
519 return 0;
520 }
521
522 output->wlr_output->frame_pending = false;
523 237
524 struct sway_workspace *workspace = output->current.active_workspace; 238 if (!output->enabled) {
525 if (workspace == NULL) {
526 return 0; 239 return 0;
527 } 240 }
528 241
529 struct sway_container *fullscreen_con = root->fullscreen_global; 242 output->wlr_output->frame_pending = false;
530 if (!fullscreen_con) {
531 fullscreen_con = workspace->current.fullscreen;
532 }
533 243
534 if (fullscreen_con && fullscreen_con->view && !debug.noscanout) { 244 output_configure_scene(output, &root->root_scene->tree.node, 1.0f);
535 // Try to scan-out the fullscreen view
536 static bool last_scanned_out = false;
537 bool scanned_out =
538 scan_out_fullscreen_view(output, fullscreen_con->view);
539 245
540 if (scanned_out && !last_scanned_out) { 246 if (output->gamma_lut_changed) {
541 sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", 247 struct wlr_output_state pending;
542 output->wlr_output->name); 248 wlr_output_state_init(&pending);
249 if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) {
250 return 0;
543 } 251 }
544 if (last_scanned_out && !scanned_out) { 252
545 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", 253 output->gamma_lut_changed = false;
546 output->wlr_output->name); 254 struct wlr_gamma_control_v1 *gamma_control =
547 output_damage_whole(output); 255 wlr_gamma_control_manager_v1_get_control(
256 server.gamma_control_manager_v1, output->wlr_output);
257 if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) {
258 wlr_output_state_finish(&pending);
259 return 0;
548 } 260 }
549 last_scanned_out = scanned_out;
550 261
551 if (scanned_out) { 262 if (!wlr_output_commit_state(output->wlr_output, &pending)) {
263 wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
264 wlr_output_state_finish(&pending);
552 return 0; 265 return 0;
553 } 266 }
554 }
555 267
556 bool needs_frame; 268 wlr_output_state_finish(&pending);
557 pixman_region32_t damage;
558 pixman_region32_init(&damage);
559 if (!wlr_output_damage_attach_render(output->damage,
560 &needs_frame, &damage)) {
561 return 0; 269 return 0;
562 } 270 }
563 271
564 if (needs_frame) { 272 wlr_scene_output_commit(output->scene_output, NULL);
565 struct timespec now;
566 clock_gettime(CLOCK_MONOTONIC, &now);
567
568 output_render(output, &now, &damage);
569 } else {
570 wlr_output_rollback(output->wlr_output);
571 }
572
573 pixman_region32_fini(&damage);
574
575 return 0; 273 return 0;
576} 274}
577 275
578static void damage_handle_frame(struct wl_listener *listener, void *user_data) { 276static void handle_frame(struct wl_listener *listener, void *user_data) {
579 struct sway_output *output = 277 struct sway_output *output =
580 wl_container_of(listener, output, damage_frame); 278 wl_container_of(listener, output, frame);
581 if (!output->enabled || !output->wlr_output->enabled) { 279 if (!output->enabled || !output->wlr_output->enabled) {
582 return; 280 return;
583 } 281 }
@@ -588,9 +286,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
588 286
589 if (output->max_render_time != 0) { 287 if (output->max_render_time != 0) {
590 struct timespec now; 288 struct timespec now;
591 clockid_t presentation_clock 289 clock_gettime(CLOCK_MONOTONIC, &now);
592 = wlr_backend_get_presentation_clock(server.backend);
593 clock_gettime(presentation_clock, &now);
594 290
595 const long NSEC_IN_SECONDS = 1000000000; 291 const long NSEC_IN_SECONDS = 1000000000;
596 struct timespec predicted_refresh = output->last_presentation; 292 struct timespec predicted_refresh = output->last_presentation;
@@ -637,116 +333,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
637 struct send_frame_done_data data = {0}; 333 struct send_frame_done_data data = {0};
638 clock_gettime(CLOCK_MONOTONIC, &data.when); 334 clock_gettime(CLOCK_MONOTONIC, &data.when);
639 data.msec_until_refresh = msec_until_refresh; 335 data.msec_until_refresh = msec_until_refresh;
640 send_frame_done(output, &data); 336 data.output = output;
641} 337 wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data);
642
643void output_damage_whole(struct sway_output *output) {
644 // The output can exist with no wlr_output if it's just been disconnected
645 // and the transaction to evacuate it has't completed yet.
646 if (output && output->wlr_output && output->damage) {
647 wlr_output_damage_add_whole(output->damage);
648 }
649}
650
651static void damage_surface_iterator(struct sway_output *output,
652 struct sway_view *view, struct wlr_surface *surface,
653 struct wlr_box *_box, void *_data) {
654 bool *data = _data;
655 bool whole = *data;
656
657 struct wlr_box box = *_box;
658 scale_box(&box, output->wlr_output->scale);
659
660 pixman_region32_t damage;
661 pixman_region32_init(&damage);
662 wlr_surface_get_effective_damage(surface, &damage);
663 wlr_region_scale(&damage, &damage, output->wlr_output->scale);
664 if (ceil(output->wlr_output->scale) > surface->current.scale) {
665 // When scaling up a surface, it'll become blurry so we need to
666 // expand the damage region
667 wlr_region_expand(&damage, &damage,
668 ceil(output->wlr_output->scale) - surface->current.scale);
669 }
670 pixman_region32_translate(&damage, box.x, box.y);
671 wlr_output_damage_add(output->damage, &damage);
672 pixman_region32_fini(&damage);
673
674 if (whole) {
675 wlr_output_damage_add_box(output->damage, &box);
676 }
677
678 if (!wl_list_empty(&surface->current.frame_callback_list)) {
679 wlr_output_schedule_frame(output->wlr_output);
680 }
681}
682
683void output_damage_surface(struct sway_output *output, double ox, double oy,
684 struct wlr_surface *surface, bool whole) {
685 output_surface_for_each_surface(output, surface, ox, oy,
686 damage_surface_iterator, &whole);
687}
688
689void output_damage_from_view(struct sway_output *output,
690 struct sway_view *view) {
691 if (!view_is_visible(view)) {
692 return;
693 }
694 bool whole = false;
695 output_view_for_each_surface(output, view, damage_surface_iterator, &whole);
696}
697
698// Expecting an unscaled box in layout coordinates
699void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
700 struct wlr_box box;
701 memcpy(&box, _box, sizeof(struct wlr_box));
702 box.x -= output->lx;
703 box.y -= output->ly;
704 scale_box(&box, output->wlr_output->scale);
705 wlr_output_damage_add_box(output->damage, &box);
706}
707
708static void damage_child_views_iterator(struct sway_container *con,
709 void *data) {
710 if (!con->view || !view_is_visible(con->view)) {
711 return;
712 }
713 struct sway_output *output = data;
714 bool whole = true;
715 output_view_for_each_surface(output, con->view, damage_surface_iterator,
716 &whole);
717}
718
719void output_damage_whole_container(struct sway_output *output,
720 struct sway_container *con) {
721 // Pad the box by 1px, because the width is a double and might be a fraction
722 struct wlr_box box = {
723 .x = con->current.x - output->lx - 1,
724 .y = con->current.y - output->ly - 1,
725 .width = con->current.width + 2,
726 .height = con->current.height + 2,
727 };
728 scale_box(&box, output->wlr_output->scale);
729 wlr_output_damage_add_box(output->damage, &box);
730 // Damage subsurfaces as well, which may extend outside the box
731 if (con->view) {
732 damage_child_views_iterator(con, output);
733 } else {
734 container_for_each_child(con, damage_child_views_iterator, output);
735 }
736}
737
738static void damage_handle_destroy(struct wl_listener *listener, void *data) {
739 struct sway_output *output =
740 wl_container_of(listener, output, damage_destroy);
741 if (!output->enabled) {
742 return;
743 }
744 output_disable(output);
745
746 wl_list_remove(&output->damage_destroy.link);
747 wl_list_remove(&output->damage_frame.link);
748
749 transaction_commit_dirty();
750} 338}
751 339
752static void update_output_manager_config(struct sway_server *server) { 340static void update_output_manager_config(struct sway_server *server) {
@@ -764,33 +352,36 @@ static void update_output_manager_config(struct sway_server *server) {
764 wlr_output_layout_get_box(root->output_layout, 352 wlr_output_layout_get_box(root->output_layout,
765 output->wlr_output, &output_box); 353 output->wlr_output, &output_box);
766 // We mark the output enabled when it's switched off but not disabled 354 // We mark the output enabled when it's switched off but not disabled
767 config_head->state.enabled = output->current_mode != NULL && output->enabled; 355 config_head->state.enabled = !wlr_box_empty(&output_box);
768 config_head->state.mode = output->current_mode; 356 config_head->state.x = output_box.x;
769 if (!wlr_box_empty(&output_box)) { 357 config_head->state.y = output_box.y;
770 config_head->state.x = output_box.x;
771 config_head->state.y = output_box.y;
772 }
773 } 358 }
774 359
775 wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); 360 wlr_output_manager_v1_set_configuration(server->output_manager_v1, config);
361
362 ipc_event_output();
776} 363}
777 364
778static void handle_destroy(struct wl_listener *listener, void *data) { 365static void begin_destroy(struct sway_output *output) {
779 struct sway_output *output = wl_container_of(listener, output, destroy);
780 struct sway_server *server = output->server; 366 struct sway_server *server = output->server;
781 output_begin_destroy(output);
782 367
783 if (output->enabled) { 368 if (output->enabled) {
784 output_disable(output); 369 output_disable(output);
785 } 370 }
786 371
372 output_begin_destroy(output);
373
787 wl_list_remove(&output->link); 374 wl_list_remove(&output->link);
788 375
376 wl_list_remove(&output->layout_destroy.link);
789 wl_list_remove(&output->destroy.link); 377 wl_list_remove(&output->destroy.link);
790 wl_list_remove(&output->commit.link); 378 wl_list_remove(&output->commit.link);
791 wl_list_remove(&output->mode.link);
792 wl_list_remove(&output->present.link); 379 wl_list_remove(&output->present.link);
380 wl_list_remove(&output->frame.link);
381 wl_list_remove(&output->request_state.link);
793 382
383 wlr_scene_output_destroy(output->scene_output);
384 output->scene_output = NULL;
794 output->wlr_output->data = NULL; 385 output->wlr_output->data = NULL;
795 output->wlr_output = NULL; 386 output->wlr_output = NULL;
796 387
@@ -799,34 +390,14 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
799 update_output_manager_config(server); 390 update_output_manager_config(server);
800} 391}
801 392
802static void handle_mode(struct wl_listener *listener, void *data) { 393static void handle_destroy(struct wl_listener *listener, void *data) {
803 struct sway_output *output = wl_container_of(listener, output, mode); 394 struct sway_output *output = wl_container_of(listener, output, destroy);
804 if (!output->enabled && !output->enabling) { 395 begin_destroy(output);
805 struct output_config *oc = find_output_config(output);
806 if (output->wlr_output->current_mode != NULL &&
807 (!oc || oc->enabled)) {
808 // We want to enable this output, but it didn't work last time,
809 // possibly because we hadn't enough CRTCs. Try again now that the
810 // output has a mode.
811 sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, "
812 "trying to enable it", output->wlr_output->name);
813 apply_output_config(oc, output);
814 }
815 return;
816 }
817 if (!output->enabled) {
818 return;
819 }
820 arrange_layers(output);
821 arrange_output(output);
822 transaction_commit_dirty();
823
824 update_output_manager_config(output->server);
825} 396}
826 397
827static void update_textures(struct sway_container *con, void *data) { 398static void handle_layout_destroy(struct wl_listener *listener, void *data) {
828 container_update_title_textures(con); 399 struct sway_output *output = wl_container_of(listener, output, layout_destroy);
829 container_update_marks_textures(con); 400 begin_destroy(output);
830} 401}
831 402
832static void handle_commit(struct wl_listener *listener, void *data) { 403static void handle_commit(struct wl_listener *listener, void *data) {
@@ -837,17 +408,21 @@ static void handle_commit(struct wl_listener *listener, void *data) {
837 return; 408 return;
838 } 409 }
839 410
840 if (event->committed & WLR_OUTPUT_STATE_SCALE) { 411 if (event->state->committed & (
841 output_for_each_container(output, update_textures, NULL); 412 WLR_OUTPUT_STATE_MODE |
842 } 413 WLR_OUTPUT_STATE_TRANSFORM |
843 414 WLR_OUTPUT_STATE_SCALE)) {
844 if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) {
845 arrange_layers(output); 415 arrange_layers(output);
846 arrange_output(output); 416 arrange_output(output);
847 transaction_commit_dirty(); 417 transaction_commit_dirty();
848 418
849 update_output_manager_config(output->server); 419 update_output_manager_config(output->server);
850 } 420 }
421
422 // Next time the output is enabled, try to re-apply the gamma LUT
423 if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) {
424 output->gamma_lut_changed = true;
425 }
851} 426}
852 427
853static void handle_present(struct wl_listener *listener, void *data) { 428static void handle_present(struct wl_listener *listener, void *data) {
@@ -862,6 +437,13 @@ static void handle_present(struct wl_listener *listener, void *data) {
862 output->refresh_nsec = output_event->refresh; 437 output->refresh_nsec = output_event->refresh;
863} 438}
864 439
440static 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
865static unsigned int last_headless_num = 0; 447static unsigned int last_headless_num = 0;
866 448
867void handle_new_output(struct wl_listener *listener, void *data) { 449void handle_new_output(struct wl_listener *listener, void *data) {
@@ -883,10 +465,14 @@ void handle_new_output(struct wl_listener *listener, void *data) {
883 465
884 if (wlr_output->non_desktop) { 466 if (wlr_output->non_desktop) {
885 sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); 467 sway_log(SWAY_DEBUG, "Not configuring non-desktop output");
468 struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output);
469#if WLR_HAS_DRM_BACKEND
886 if (server->drm_lease_manager) { 470 if (server->drm_lease_manager) {
887 wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, 471 wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager,
888 wlr_output); 472 wlr_output);
889 } 473 }
474#endif
475 list_add(root->non_desktop_outputs, non_desktop);
890 return; 476 return;
891 } 477 }
892 478
@@ -896,32 +482,46 @@ void handle_new_output(struct wl_listener *listener, void *data) {
896 return; 482 return;
897 } 483 }
898 484
485 // Create the scene output here so we're not accidentally creating one for
486 // the fallback output
487 struct wlr_scene_output *scene_output =
488 wlr_scene_output_create(root->root_scene, wlr_output);
489 if (!scene_output) {
490 sway_log(SWAY_ERROR, "Failed to create a scene output");
491 return;
492 }
493
899 struct sway_output *output = output_create(wlr_output); 494 struct sway_output *output = output_create(wlr_output);
900 if (!output) { 495 if (!output) {
496 sway_log(SWAY_ERROR, "Failed to create a sway output");
497 wlr_scene_output_destroy(scene_output);
901 return; 498 return;
902 } 499 }
500
903 output->server = server; 501 output->server = server;
904 output->damage = wlr_output_damage_create(wlr_output); 502 output->scene_output = scene_output;
905 503
504 wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy);
505 output->layout_destroy.notify = handle_layout_destroy;
906 wl_signal_add(&wlr_output->events.destroy, &output->destroy); 506 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
907 output->destroy.notify = handle_destroy; 507 output->destroy.notify = handle_destroy;
908 wl_signal_add(&wlr_output->events.commit, &output->commit); 508 wl_signal_add(&wlr_output->events.commit, &output->commit);
909 output->commit.notify = handle_commit; 509 output->commit.notify = handle_commit;
910 wl_signal_add(&wlr_output->events.mode, &output->mode);
911 output->mode.notify = handle_mode;
912 wl_signal_add(&wlr_output->events.present, &output->present); 510 wl_signal_add(&wlr_output->events.present, &output->present);
913 output->present.notify = handle_present; 511 output->present.notify = handle_present;
914 wl_signal_add(&output->damage->events.frame, &output->damage_frame); 512 wl_signal_add(&wlr_output->events.frame, &output->frame);
915 output->damage_frame.notify = damage_handle_frame; 513 output->frame.notify = handle_frame;
916 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); 514 wl_signal_add(&wlr_output->events.request_state, &output->request_state);
917 output->damage_destroy.notify = damage_handle_destroy; 515 output->request_state.notify = handle_request_state;
918 516
919 output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, 517 output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop,
920 output_repaint_timer_handler, output); 518 output_repaint_timer_handler, output);
921 519
922 struct output_config *oc = find_output_config(output); 520 if (server->session_lock.lock) {
923 apply_output_config(oc, output); 521 sway_session_lock_add_output(server->session_lock.lock, output);
924 free_output_config(oc); 522 }
523
524 apply_all_output_configs();
925 525
926 transaction_commit_dirty(); 526 transaction_commit_dirty();
927 527
@@ -935,62 +535,103 @@ void handle_output_layout_change(struct wl_listener *listener,
935 update_output_manager_config(server); 535 update_output_manager_config(server);
936} 536}
937 537
538void 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
553static 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
938static void output_manager_apply(struct sway_server *server, 581static void output_manager_apply(struct sway_server *server,
939 struct wlr_output_configuration_v1 *config, bool test_only) { 582 struct wlr_output_configuration_v1 *config, bool test_only) {
940 // TODO: perform atomic tests on the whole backend atomically 583 size_t configs_len = wl_list_length(&root->all_outputs);
941 584 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
942 struct wlr_output_configuration_head_v1 *config_head; 585 if (!configs) {
943 // First disable outputs we need to disable 586 return;
944 bool ok = true; 587 }
945 wl_list_for_each(config_head, &config->heads, link) { 588
946 struct wlr_output *wlr_output = config_head->state.output; 589 int config_idx = 0;
947 struct sway_output *output = wlr_output->data; 590 struct sway_output *sway_output;
948 if (!output->enabled || config_head->state.enabled) { 591 wl_list_for_each(sway_output, &root->all_outputs, link) {
592 if (sway_output == root->fallback_output) {
593 configs_len--;
949 continue; 594 continue;
950 } 595 }
951 struct output_config *oc = new_output_config(output->wlr_output->name);
952 oc->enabled = false;
953 596
954 if (test_only) { 597 struct matched_output_config *cfg = &configs[config_idx++];
955 ok &= test_output_config(oc, output); 598 cfg->output = sway_output;
956 } else {
957 oc = store_output_config(oc);
958 ok &= apply_output_config(oc, output);
959 }
960 }
961 599
962 // Then enable outputs that need to 600 struct wlr_output_configuration_head_v1 *config_head;
963 wl_list_for_each(config_head, &config->heads, link) { 601 wl_list_for_each(config_head, &config->heads, link) {
964 struct wlr_output *wlr_output = config_head->state.output; 602 if (config_head->state.output == sway_output->wlr_output) {
965 struct sway_output *output = wlr_output->data; 603 cfg->config = output_config_for_config_head(config_head, sway_output);
966 if (!config_head->state.enabled) { 604 break;
967 continue; 605 }
968 } 606 }
969 struct output_config *oc = new_output_config(output->wlr_output->name); 607 if (!cfg->config) {
970 oc->enabled = true; 608 cfg->config = find_output_config(sway_output);
971 if (config_head->state.mode != NULL) {
972 struct wlr_output_mode *mode = config_head->state.mode;
973 oc->width = mode->width;
974 oc->height = mode->height;
975 oc->refresh_rate = mode->refresh / 1000.f;
976 } else {
977 oc->width = config_head->state.custom_mode.width;
978 oc->height = config_head->state.custom_mode.height;
979 oc->refresh_rate =
980 config_head->state.custom_mode.refresh / 1000.f;
981 } 609 }
982 oc->x = config_head->state.x; 610 }
983 oc->y = config_head->state.y; 611
984 oc->transform = config_head->state.transform; 612 bool ok = apply_output_configs(configs, configs_len, test_only);
985 oc->scale = config_head->state.scale; 613 for (size_t idx = 0; idx < configs_len; idx++) {
614 struct matched_output_config *cfg = &configs[idx];
986 615
987 if (test_only) { 616 // Only store new configs for successful non-test commits. Old configs,
988 ok &= test_output_config(oc, output); 617 // test-only and failed commits just get freed.
618 bool store_config = false;
619 if (!test_only && ok) {
620 struct wlr_output_configuration_head_v1 *config_head;
621 wl_list_for_each(config_head, &config->heads, link) {
622 if (config_head->state.output == sway_output->wlr_output) {
623 store_config = true;
624 break;
625 }
626 }
627 }
628 if (store_config) {
629 store_output_config(cfg->config);
989 } else { 630 } else {
990 oc = store_output_config(oc); 631 free_output_config(cfg->config);
991 ok &= apply_output_config(oc, output);
992 } 632 }
993 } 633 }
634 free(configs);
994 635
995 if (ok) { 636 if (ok) {
996 wlr_output_configuration_v1_send_succeeded(config); 637 wlr_output_configuration_v1_send_succeeded(config);
@@ -1034,6 +675,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
1034 oc->power = 1; 675 oc->power = 1;
1035 break; 676 break;
1036 } 677 }
1037 oc = store_output_config(oc); 678 store_output_config(oc);
1038 apply_output_config(oc, output); 679 apply_all_output_configs();
1039} 680}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
deleted file mode 100644
index ed9ad490..00000000
--- a/sway/desktop/render.c
+++ /dev/null
@@ -1,1204 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <GLES2/gl2.h>
4#include <stdlib.h>
5#include <strings.h>
6#include <time.h>
7#include <wayland-server-core.h>
8#include <wlr/render/gles2.h>
9#include <wlr/render/wlr_renderer.h>
10#include <wlr/types/wlr_buffer.h>
11#include <wlr/types/wlr_matrix.h>
12#include <wlr/types/wlr_output_damage.h>
13#include <wlr/types/wlr_output_layout.h>
14#include <wlr/types/wlr_output.h>
15#include <wlr/types/wlr_compositor.h>
16#include <wlr/util/region.h>
17#include "log.h"
18#include "config.h"
19#include "sway/config.h"
20#include "sway/input/input-manager.h"
21#include "sway/input/seat.h"
22#include "sway/layers.h"
23#include "sway/output.h"
24#include "sway/server.h"
25#include "sway/tree/arrange.h"
26#include "sway/tree/container.h"
27#include "sway/tree/root.h"
28#include "sway/tree/view.h"
29#include "sway/tree/workspace.h"
30
31struct render_data {
32 pixman_region32_t *damage;
33 float alpha;
34 struct wlr_box *clip_box;
35};
36
37/**
38 * Apply scale to a width or height.
39 *
40 * One does not simply multiply the width by the scale. We allow fractional
41 * scaling, which means the resulting scaled width might be a decimal.
42 * So we round it.
43 *
44 * But even this can produce undesirable results depending on the X or Y offset
45 * of the box. For example, with a scale of 1.5, a box with width=1 should not
46 * scale to 2px if its X coordinate is 1, because the X coordinate would have
47 * scaled to 2px.
48 */
49static int scale_length(int length, int offset, float scale) {
50 return round((offset + length) * scale) - round(offset * scale);
51}
52
53static void scissor_output(struct wlr_output *wlr_output,
54 pixman_box32_t *rect) {
55 struct wlr_renderer *renderer = wlr_output->renderer;
56 assert(renderer);
57
58 struct wlr_box box = {
59 .x = rect->x1,
60 .y = rect->y1,
61 .width = rect->x2 - rect->x1,
62 .height = rect->y2 - rect->y1,
63 };
64
65 int ow, oh;
66 wlr_output_transformed_resolution(wlr_output, &ow, &oh);
67
68 enum wl_output_transform transform =
69 wlr_output_transform_invert(wlr_output->transform);
70 wlr_box_transform(&box, &box, transform, ow, oh);
71
72 wlr_renderer_scissor(renderer, &box);
73}
74
75static 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
99static void render_texture(struct wlr_output *wlr_output,
100 pixman_region32_t *output_damage, struct wlr_texture *texture,
101 const struct wlr_fbox *src_box, const struct wlr_box *dst_box,
102 const float matrix[static 9], float alpha) {
103 struct wlr_renderer *renderer = wlr_output->renderer;
104 struct sway_output *output = wlr_output->data;
105
106 pixman_region32_t damage;
107 pixman_region32_init(&damage);
108 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y,
109 dst_box->width, dst_box->height);
110 pixman_region32_intersect(&damage, &damage, output_damage);
111 bool damaged = pixman_region32_not_empty(&damage);
112 if (!damaged) {
113 goto damage_finish;
114 }
115
116 int nrects;
117 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
118 for (int i = 0; i < nrects; ++i) {
119 scissor_output(wlr_output, &rects[i]);
120 set_scale_filter(wlr_output, texture, output->scale_filter);
121 if (src_box != NULL) {
122 wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha);
123 } else {
124 wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
125 }
126 }
127
128damage_finish:
129 pixman_region32_fini(&damage);
130}
131
132static void render_surface_iterator(struct sway_output *output,
133 struct sway_view *view, struct wlr_surface *surface,
134 struct wlr_box *_box, void *_data) {
135 struct render_data *data = _data;
136 struct wlr_output *wlr_output = output->wlr_output;
137 pixman_region32_t *output_damage = data->damage;
138 float alpha = data->alpha;
139
140 struct wlr_texture *texture = wlr_surface_get_texture(surface);
141 if (!texture) {
142 return;
143 }
144
145 struct wlr_fbox src_box;
146 wlr_surface_get_buffer_source_box(surface, &src_box);
147
148 struct wlr_box proj_box = *_box;
149 scale_box(&proj_box, wlr_output->scale);
150
151 float matrix[9];
152 enum wl_output_transform transform =
153 wlr_output_transform_invert(surface->current.transform);
154 wlr_matrix_project_box(matrix, &proj_box, transform, 0.0,
155 wlr_output->transform_matrix);
156
157 struct wlr_box dst_box = *_box;
158 struct wlr_box *clip_box = data->clip_box;
159 if (clip_box != NULL) {
160 dst_box.width = fmin(dst_box.width, clip_box->width);
161 dst_box.height = fmin(dst_box.height, clip_box->height);
162 }
163 scale_box(&dst_box, wlr_output->scale);
164
165 render_texture(wlr_output, output_damage, texture,
166 &src_box, &dst_box, matrix, alpha);
167
168 wlr_presentation_surface_sampled_on_output(server.presentation, surface,
169 wlr_output);
170}
171
172static void render_layer_toplevel(struct sway_output *output,
173 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
174 struct render_data data = {
175 .damage = damage,
176 .alpha = 1.0f,
177 };
178 output_layer_for_each_toplevel_surface(output, layer_surfaces,
179 render_surface_iterator, &data);
180}
181
182static void render_layer_popups(struct sway_output *output,
183 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
184 struct render_data data = {
185 .damage = damage,
186 .alpha = 1.0f,
187 };
188 output_layer_for_each_popup_surface(output, layer_surfaces,
189 render_surface_iterator, &data);
190}
191
192#if HAVE_XWAYLAND
193static void render_unmanaged(struct sway_output *output,
194 pixman_region32_t *damage, struct wl_list *unmanaged) {
195 struct render_data data = {
196 .damage = damage,
197 .alpha = 1.0f,
198 };
199 output_unmanaged_for_each_surface(output, unmanaged,
200 render_surface_iterator, &data);
201}
202#endif
203
204static void render_drag_icons(struct sway_output *output,
205 pixman_region32_t *damage, struct wl_list *drag_icons) {
206 struct render_data data = {
207 .damage = damage,
208 .alpha = 1.0f,
209 };
210 output_drag_icons_for_each_surface(output, drag_icons,
211 render_surface_iterator, &data);
212}
213
214// _box.x and .y are expected to be layout-local
215// _box.width and .height are expected to be output-buffer-local
216void render_rect(struct sway_output *output,
217 pixman_region32_t *output_damage, const struct wlr_box *_box,
218 float color[static 4]) {
219 struct wlr_output *wlr_output = output->wlr_output;
220 struct wlr_renderer *renderer = wlr_output->renderer;
221
222 struct wlr_box box;
223 memcpy(&box, _box, sizeof(struct wlr_box));
224 box.x -= output->lx * wlr_output->scale;
225 box.y -= output->ly * wlr_output->scale;
226
227 pixman_region32_t damage;
228 pixman_region32_init(&damage);
229 pixman_region32_union_rect(&damage, &damage, box.x, box.y,
230 box.width, box.height);
231 pixman_region32_intersect(&damage, &damage, output_damage);
232 bool damaged = pixman_region32_not_empty(&damage);
233 if (!damaged) {
234 goto damage_finish;
235 }
236
237 int nrects;
238 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
239 for (int i = 0; i < nrects; ++i) {
240 scissor_output(wlr_output, &rects[i]);
241 wlr_render_rect(renderer, &box, color,
242 wlr_output->transform_matrix);
243 }
244
245damage_finish:
246 pixman_region32_fini(&damage);
247}
248
249void premultiply_alpha(float color[4], float opacity) {
250 color[3] *= opacity;
251 color[0] *= color[3];
252 color[1] *= color[3];
253 color[2] *= color[3];
254}
255
256static void render_view_toplevels(struct sway_view *view,
257 struct sway_output *output, pixman_region32_t *damage, float alpha) {
258 struct render_data data = {
259 .damage = damage,
260 .alpha = alpha,
261 };
262 struct wlr_box clip_box;
263 if (!container_is_current_floating(view->container)) {
264 // As we pass the geometry offsets to the surface iterator, we will
265 // need to account for the offsets in the clip dimensions.
266 clip_box.width = view->container->current.content_width + view->geometry.x;
267 clip_box.height = view->container->current.content_height + view->geometry.y;
268 data.clip_box = &clip_box;
269 }
270 // Render all toplevels without descending into popups
271 double ox = view->container->surface_x -
272 output->lx - view->geometry.x;
273 double oy = view->container->surface_y -
274 output->ly - view->geometry.y;
275 output_surface_for_each_surface(output, view->surface, ox, oy,
276 render_surface_iterator, &data);
277}
278
279static void render_view_popups(struct sway_view *view,
280 struct sway_output *output, pixman_region32_t *damage, float alpha) {
281 struct render_data data = {
282 .damage = damage,
283 .alpha = alpha,
284 };
285 output_view_for_each_popup_surface(output, view,
286 render_surface_iterator, &data);
287}
288
289static void render_saved_view(struct sway_view *view,
290 struct sway_output *output, pixman_region32_t *damage, float alpha) {
291 struct wlr_output *wlr_output = output->wlr_output;
292
293 if (wl_list_empty(&view->saved_buffers)) {
294 return;
295 }
296
297 bool floating = container_is_current_floating(view->container);
298
299 struct sway_saved_buffer *saved_buf;
300 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
301 if (!saved_buf->buffer->texture) {
302 continue;
303 }
304
305 struct wlr_box proj_box = {
306 .x = saved_buf->x - view->saved_geometry.x - output->lx,
307 .y = saved_buf->y - view->saved_geometry.y - output->ly,
308 .width = saved_buf->width,
309 .height = saved_buf->height,
310 };
311
312 struct wlr_box output_box = {
313 .width = output->width,
314 .height = output->height,
315 };
316
317 struct wlr_box intersection;
318 bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box);
319 if (!intersects) {
320 continue;
321 }
322
323 struct wlr_box dst_box = proj_box;
324 scale_box(&proj_box, wlr_output->scale);
325
326 float matrix[9];
327 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
328 wlr_matrix_project_box(matrix, &proj_box, transform, 0,
329 wlr_output->transform_matrix);
330
331 if (!floating) {
332 dst_box.width = fmin(dst_box.width,
333 view->container->current.content_width -
334 (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x);
335 dst_box.height = fmin(dst_box.height,
336 view->container->current.content_height -
337 (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y);
338 }
339 scale_box(&dst_box, wlr_output->scale);
340
341 render_texture(wlr_output, damage, saved_buf->buffer->texture,
342 &saved_buf->source_box, &dst_box, matrix, alpha);
343 }
344
345 // FIXME: we should set the surface that this saved buffer originates from
346 // as sampled here.
347 // https://github.com/swaywm/sway/pull/4465#discussion_r321082059
348}
349
350/**
351 * Render a view's surface and left/bottom/right borders.
352 */
353static void render_view(struct sway_output *output, pixman_region32_t *damage,
354 struct sway_container *con, struct border_colors *colors) {
355 struct sway_view *view = con->view;
356 if (!wl_list_empty(&view->saved_buffers)) {
357 render_saved_view(view, output, damage, view->container->alpha);
358 } else if (view->surface) {
359 render_view_toplevels(view, output, damage, view->container->alpha);
360 }
361
362 if (con->current.border == B_NONE || con->current.border == B_CSD) {
363 return;
364 }
365
366 struct wlr_box box;
367 float output_scale = output->wlr_output->scale;
368 float color[4];
369 struct sway_container_state *state = &con->current;
370
371 if (state->border_left) {
372 memcpy(&color, colors->child_border, sizeof(float) * 4);
373 premultiply_alpha(color, con->alpha);
374 box.x = floor(state->x);
375 box.y = floor(state->content_y);
376 box.width = state->border_thickness;
377 box.height = state->content_height;
378 scale_box(&box, output_scale);
379 render_rect(output, damage, &box, color);
380 }
381
382 list_t *siblings = container_get_current_siblings(con);
383 enum sway_container_layout layout =
384 container_current_parent_layout(con);
385
386 if (state->border_right) {
387 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) {
388 memcpy(&color, colors->indicator, sizeof(float) * 4);
389 } else {
390 memcpy(&color, colors->child_border, sizeof(float) * 4);
391 }
392 premultiply_alpha(color, con->alpha);
393 box.x = floor(state->content_x + state->content_width);
394 box.y = floor(state->content_y);
395 box.width = state->border_thickness;
396 box.height = state->content_height;
397 scale_box(&box, output_scale);
398 render_rect(output, damage, &box, color);
399 }
400
401 if (state->border_bottom) {
402 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) {
403 memcpy(&color, colors->indicator, sizeof(float) * 4);
404 } else {
405 memcpy(&color, colors->child_border, sizeof(float) * 4);
406 }
407 premultiply_alpha(color, con->alpha);
408 box.x = floor(state->x);
409 box.y = floor(state->content_y + state->content_height);
410 box.width = state->width;
411 box.height = state->border_thickness;
412 scale_box(&box, output_scale);
413 render_rect(output, damage, &box, color);
414 }
415}
416
417/**
418 * Render a titlebar.
419 *
420 * Care must be taken not to render over the same pixel multiple times,
421 * otherwise the colors will be incorrect when using opacity.
422 *
423 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
424 * The left side is: 1px border, 2px padding, title
425 */
426static void render_titlebar(struct sway_output *output,
427 pixman_region32_t *output_damage, struct sway_container *con,
428 int x, int y, int width,
429 struct border_colors *colors, struct wlr_texture *title_texture,
430 struct wlr_texture *marks_texture) {
431 struct wlr_box box;
432 float color[4];
433 float output_scale = output->wlr_output->scale;
434 double output_x = output->lx;
435 double output_y = output->ly;
436 int titlebar_border_thickness = config->titlebar_border_thickness;
437 int titlebar_h_padding = config->titlebar_h_padding;
438 int titlebar_v_padding = config->titlebar_v_padding;
439 enum alignment title_align = config->title_align;
440
441 // Single pixel bar above title
442 memcpy(&color, colors->border, sizeof(float) * 4);
443 premultiply_alpha(color, con->alpha);
444 box.x = x;
445 box.y = y;
446 box.width = width;
447 box.height = titlebar_border_thickness;
448 scale_box(&box, output_scale);
449 render_rect(output, output_damage, &box, color);
450
451 // Single pixel bar below title
452 box.x = x;
453 box.y = y + container_titlebar_height() - titlebar_border_thickness;
454 box.width = width;
455 box.height = titlebar_border_thickness;
456 scale_box(&box, output_scale);
457 render_rect(output, output_damage, &box, color);
458
459 // Single pixel left edge
460 box.x = x;
461 box.y = y + titlebar_border_thickness;
462 box.width = titlebar_border_thickness;
463 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
464 scale_box(&box, output_scale);
465 render_rect(output, output_damage, &box, color);
466
467 // Single pixel right edge
468 box.x = x + width - titlebar_border_thickness;
469 box.y = y + titlebar_border_thickness;
470 box.width = titlebar_border_thickness;
471 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
472 scale_box(&box, output_scale);
473 render_rect(output, output_damage, &box, color);
474
475 int inner_x = x - output_x + titlebar_h_padding;
476 int bg_y = y + titlebar_border_thickness;
477 size_t inner_width = width - titlebar_h_padding * 2;
478
479 // output-buffer local
480 int ob_inner_x = round(inner_x * output_scale);
481 int ob_inner_width = scale_length(inner_width, inner_x, output_scale);
482 int ob_bg_height = scale_length(
483 (titlebar_v_padding - titlebar_border_thickness) * 2 +
484 config->font_height, bg_y, output_scale);
485
486 // Marks
487 int ob_marks_x = 0; // output-buffer-local
488 int ob_marks_width = 0; // output-buffer-local
489 if (config->show_marks && marks_texture) {
490 struct wlr_box texture_box = {
491 .width = marks_texture->width,
492 .height = marks_texture->height,
493 };
494 ob_marks_width = texture_box.width;
495
496 // The marks texture might be shorter than the config->font_height, in
497 // which case we need to pad it as evenly as possible above and below.
498 int ob_padding_total = ob_bg_height - texture_box.height;
499 int ob_padding_above = floor(ob_padding_total / 2.0);
500 int ob_padding_below = ceil(ob_padding_total / 2.0);
501
502 // Render texture. If the title is on the right, the marks will be on
503 // the left. Otherwise, they will be on the right.
504 if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) {
505 texture_box.x = ob_inner_x;
506 } else {
507 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
508 }
509 ob_marks_x = texture_box.x;
510
511 texture_box.y = round((bg_y - output_y) * output_scale) +
512 ob_padding_above;
513
514 float matrix[9];
515 wlr_matrix_project_box(matrix, &texture_box,
516 WL_OUTPUT_TRANSFORM_NORMAL,
517 0.0, output->wlr_output->transform_matrix);
518
519 if (ob_inner_width < texture_box.width) {
520 texture_box.width = ob_inner_width;
521 }
522 render_texture(output->wlr_output, output_damage, marks_texture,
523 NULL, &texture_box, matrix, con->alpha);
524
525 // Padding above
526 memcpy(&color, colors->background, sizeof(float) * 4);
527 premultiply_alpha(color, con->alpha);
528 box.x = texture_box.x + round(output_x * output_scale);
529 box.y = round((y + titlebar_border_thickness) * output_scale);
530 box.width = texture_box.width;
531 box.height = ob_padding_above;
532 render_rect(output, output_damage, &box, color);
533
534 // Padding below
535 box.y += ob_padding_above + texture_box.height;
536 box.height = ob_padding_below;
537 render_rect(output, output_damage, &box, color);
538 }
539
540 // Title text
541 int ob_title_x = 0; // output-buffer-local
542 int ob_title_width = 0; // output-buffer-local
543 if (title_texture) {
544 struct wlr_box texture_box = {
545 .width = title_texture->width,
546 .height = title_texture->height,
547 };
548
549 // The effective output may be NULL when con is not on any output.
550 // This can happen because we render all children of containers,
551 // even those that are out of the bounds of any output.
552 struct sway_output *effective = container_get_effective_output(con);
553 float title_scale = effective ? effective->wlr_output->scale : output_scale;
554 texture_box.width = texture_box.width * output_scale / title_scale;
555 texture_box.height = texture_box.height * output_scale / title_scale;
556 ob_title_width = texture_box.width;
557
558 // The title texture might be shorter than the config->font_height,
559 // in which case we need to pad it above and below.
560 int ob_padding_above = round((titlebar_v_padding -
561 titlebar_border_thickness) * output_scale);
562 int ob_padding_below = ob_bg_height - ob_padding_above -
563 texture_box.height;
564
565 // Render texture
566 if (texture_box.width > ob_inner_width - ob_marks_width) {
567 texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width)
568 ? ob_marks_x + ob_marks_width : ob_inner_x;
569 } else if (title_align == ALIGN_LEFT) {
570 texture_box.x = ob_inner_x;
571 } else if (title_align == ALIGN_CENTER) {
572 // If there are marks visible, center between the edge and marks.
573 // Otherwise, center in the inner area.
574 if (ob_marks_width) {
575 texture_box.x = (ob_inner_x + ob_marks_x) / 2
576 - texture_box.width / 2;
577 } else {
578 texture_box.x = ob_inner_x + ob_inner_width / 2
579 - texture_box.width / 2;
580 }
581 } else {
582 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
583 }
584 ob_title_x = texture_box.x;
585
586 texture_box.y =
587 round((bg_y - output_y) * output_scale) + ob_padding_above;
588
589 float matrix[9];
590 wlr_matrix_project_box(matrix, &texture_box,
591 WL_OUTPUT_TRANSFORM_NORMAL,
592 0.0, output->wlr_output->transform_matrix);
593
594 if (ob_inner_width - ob_marks_width < texture_box.width) {
595 texture_box.width = ob_inner_width - ob_marks_width;
596 }
597
598 render_texture(output->wlr_output, output_damage, title_texture,
599 NULL, &texture_box, matrix, con->alpha);
600
601 // Padding above
602 memcpy(&color, colors->background, sizeof(float) * 4);
603 premultiply_alpha(color, con->alpha);
604 box.x = texture_box.x + round(output_x * output_scale);
605 box.y = round((y + titlebar_border_thickness) * output_scale);
606 box.width = texture_box.width;
607 box.height = ob_padding_above;
608 render_rect(output, output_damage, &box, color);
609
610 // Padding below
611 box.y += ob_padding_above + texture_box.height;
612 box.height = ob_padding_below;
613 render_rect(output, output_damage, &box, color);
614 }
615
616 // Determine the left + right extends of the textures (output-buffer local)
617 int ob_left_x, ob_left_width, ob_right_x, ob_right_width;
618 if (ob_title_width == 0 && ob_marks_width == 0) {
619 ob_left_x = ob_inner_x;
620 ob_left_width = 0;
621 ob_right_x = ob_inner_x;
622 ob_right_width = 0;
623 } else if (ob_title_x < ob_marks_x) {
624 ob_left_x = ob_title_x;
625 ob_left_width = ob_title_width;
626 ob_right_x = ob_marks_x;
627 ob_right_width = ob_marks_width;
628 } else {
629 ob_left_x = ob_marks_x;
630 ob_left_width = ob_marks_width;
631 ob_right_x = ob_title_x;
632 ob_right_width = ob_title_width;
633 }
634 if (ob_left_x < ob_inner_x) {
635 ob_left_x = ob_inner_x;
636 } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) {
637 ob_right_x = ob_left_x;
638 ob_right_width = ob_left_width;
639 }
640
641 // Filler between title and marks
642 box.width = ob_right_x - ob_left_x - ob_left_width;
643 if (box.width > 0) {
644 box.x = ob_left_x + ob_left_width + round(output_x * output_scale);
645 box.y = round(bg_y * output_scale);
646 box.height = ob_bg_height;
647 render_rect(output, output_damage, &box, color);
648 }
649
650 // Padding on left side
651 box.x = x + titlebar_border_thickness;
652 box.y = y + titlebar_border_thickness;
653 box.width = titlebar_h_padding - titlebar_border_thickness;
654 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
655 config->font_height;
656 scale_box(&box, output_scale);
657 int left_x = ob_left_x + round(output_x * output_scale);
658 if (box.x + box.width < left_x) {
659 box.width += left_x - box.x - box.width;
660 }
661 render_rect(output, output_damage, &box, color);
662
663 // Padding on right side
664 box.x = x + width - titlebar_h_padding;
665 box.y = y + titlebar_border_thickness;
666 box.width = titlebar_h_padding - titlebar_border_thickness;
667 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
668 config->font_height;
669 scale_box(&box, output_scale);
670 int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale);
671 if (right_rx < box.x) {
672 box.width += box.x - right_rx;
673 box.x = right_rx;
674 }
675 render_rect(output, output_damage, &box, color);
676}
677
678/**
679 * Render the top border line for a view using "border pixel".
680 */
681static void render_top_border(struct sway_output *output,
682 pixman_region32_t *output_damage, struct sway_container *con,
683 struct border_colors *colors) {
684 struct sway_container_state *state = &con->current;
685 if (!state->border_top) {
686 return;
687 }
688 struct wlr_box box;
689 float color[4];
690 float output_scale = output->wlr_output->scale;
691
692 // Child border - top edge
693 memcpy(&color, colors->child_border, sizeof(float) * 4);
694 premultiply_alpha(color, con->alpha);
695 box.x = floor(state->x);
696 box.y = floor(state->y);
697 box.width = state->width;
698 box.height = state->border_thickness;
699 scale_box(&box, output_scale);
700 render_rect(output, output_damage, &box, color);
701}
702
703struct parent_data {
704 enum sway_container_layout layout;
705 struct wlr_box box;
706 list_t *children;
707 bool focused;
708 struct sway_container *active_child;
709};
710
711static void render_container(struct sway_output *output,
712 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
713
714/**
715 * Render a container's children using a L_HORIZ or L_VERT layout.
716 *
717 * Wrap child views in borders and leave child containers borderless because
718 * they'll apply their own borders to their children.
719 */
720static void render_containers_linear(struct sway_output *output,
721 pixman_region32_t *damage, struct parent_data *parent) {
722 for (int i = 0; i < parent->children->length; ++i) {
723 struct sway_container *child = parent->children->items[i];
724
725 if (child->view) {
726 struct sway_view *view = child->view;
727 struct border_colors *colors;
728 struct wlr_texture *title_texture;
729 struct wlr_texture *marks_texture;
730 struct sway_container_state *state = &child->current;
731
732 if (view_is_urgent(view)) {
733 colors = &config->border_colors.urgent;
734 title_texture = child->title_urgent;
735 marks_texture = child->marks_urgent;
736 } else if (state->focused || parent->focused) {
737 colors = &config->border_colors.focused;
738 title_texture = child->title_focused;
739 marks_texture = child->marks_focused;
740 } else if (child == parent->active_child) {
741 colors = &config->border_colors.focused_inactive;
742 title_texture = child->title_focused_inactive;
743 marks_texture = child->marks_focused_inactive;
744 } else {
745 colors = &config->border_colors.unfocused;
746 title_texture = child->title_unfocused;
747 marks_texture = child->marks_unfocused;
748 }
749
750 if (state->border == B_NORMAL) {
751 render_titlebar(output, damage, child, floor(state->x),
752 floor(state->y), state->width, colors,
753 title_texture, marks_texture);
754 } else if (state->border == B_PIXEL) {
755 render_top_border(output, damage, child, colors);
756 }
757 render_view(output, damage, child, colors);
758 } else {
759 render_container(output, damage, child,
760 parent->focused || child->current.focused);
761 }
762 }
763}
764
765static bool container_is_focused(struct sway_container *con, void *data) {
766 return con->current.focused;
767}
768
769static bool container_has_focused_child(struct sway_container *con) {
770 return container_find_child(con, container_is_focused, NULL);
771}
772
773/**
774 * Render a container's children using the L_TABBED layout.
775 */
776static void render_containers_tabbed(struct sway_output *output,
777 pixman_region32_t *damage, struct parent_data *parent) {
778 if (!parent->children->length) {
779 return;
780 }
781 struct sway_container *current = parent->active_child;
782 struct border_colors *current_colors = &config->border_colors.unfocused;
783 int tab_width = parent->box.width / parent->children->length;
784
785 // Render tabs
786 for (int i = 0; i < parent->children->length; ++i) {
787 struct sway_container *child = parent->children->items[i];
788 struct sway_view *view = child->view;
789 struct sway_container_state *cstate = &child->current;
790 struct border_colors *colors;
791 struct wlr_texture *title_texture;
792 struct wlr_texture *marks_texture;
793 bool urgent = view ?
794 view_is_urgent(view) : container_has_urgent_child(child);
795
796 if (urgent) {
797 colors = &config->border_colors.urgent;
798 title_texture = child->title_urgent;
799 marks_texture = child->marks_urgent;
800 } else if (cstate->focused || parent->focused) {
801 colors = &config->border_colors.focused;
802 title_texture = child->title_focused;
803 marks_texture = child->marks_focused;
804 } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
805 colors = &config->border_colors.focused_tab_title;
806 title_texture = child->title_focused_tab_title;
807 marks_texture = child->marks_focused_tab_title;
808 } else if (child == parent->active_child) {
809 colors = &config->border_colors.focused_inactive;
810 title_texture = child->title_focused_inactive;
811 marks_texture = child->marks_focused_inactive;
812 } else {
813 colors = &config->border_colors.unfocused;
814 title_texture = child->title_unfocused;
815 marks_texture = child->marks_unfocused;
816 }
817
818 int x = floor(cstate->x + tab_width * i);
819
820 // Make last tab use the remaining width of the parent
821 if (i == parent->children->length - 1) {
822 tab_width = parent->box.width - tab_width * i;
823 }
824
825 render_titlebar(output, damage, child, x, parent->box.y, tab_width,
826 colors, title_texture, marks_texture);
827
828 if (child == current) {
829 current_colors = colors;
830 }
831 }
832
833 // Render surface and left/right/bottom borders
834 if (current->view) {
835 render_view(output, damage, current, current_colors);
836 } else {
837 render_container(output, damage, current,
838 parent->focused || current->current.focused);
839 }
840}
841
842/**
843 * Render a container's children using the L_STACKED layout.
844 */
845static void render_containers_stacked(struct sway_output *output,
846 pixman_region32_t *damage, struct parent_data *parent) {
847 if (!parent->children->length) {
848 return;
849 }
850 struct sway_container *current = parent->active_child;
851 struct border_colors *current_colors = &config->border_colors.unfocused;
852 size_t titlebar_height = container_titlebar_height();
853
854 // Render titles
855 for (int i = 0; i < parent->children->length; ++i) {
856 struct sway_container *child = parent->children->items[i];
857 struct sway_view *view = child->view;
858 struct sway_container_state *cstate = &child->current;
859 struct border_colors *colors;
860 struct wlr_texture *title_texture;
861 struct wlr_texture *marks_texture;
862 bool urgent = view ?
863 view_is_urgent(view) : container_has_urgent_child(child);
864
865 if (urgent) {
866 colors = &config->border_colors.urgent;
867 title_texture = child->title_urgent;
868 marks_texture = child->marks_urgent;
869 } else if (cstate->focused || parent->focused) {
870 colors = &config->border_colors.focused;
871 title_texture = child->title_focused;
872 marks_texture = child->marks_focused;
873 } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
874 colors = &config->border_colors.focused_tab_title;
875 title_texture = child->title_focused_tab_title;
876 marks_texture = child->marks_focused_tab_title;
877 } else if (child == parent->active_child) {
878 colors = &config->border_colors.focused_inactive;
879 title_texture = child->title_focused_inactive;
880 marks_texture = child->marks_focused_inactive;
881 } else {
882 colors = &config->border_colors.unfocused;
883 title_texture = child->title_unfocused;
884 marks_texture = child->marks_unfocused;
885 }
886
887 int y = parent->box.y + titlebar_height * i;
888 render_titlebar(output, damage, child, parent->box.x, y,
889 parent->box.width, colors, title_texture, marks_texture);
890
891 if (child == current) {
892 current_colors = colors;
893 }
894 }
895
896 // Render surface and left/right/bottom borders
897 if (current->view) {
898 render_view(output, damage, current, current_colors);
899 } else {
900 render_container(output, damage, current,
901 parent->focused || current->current.focused);
902 }
903}
904
905static void render_containers(struct sway_output *output,
906 pixman_region32_t *damage, struct parent_data *parent) {
907 if (config->hide_lone_tab && parent->children->length == 1) {
908 struct sway_container *child = parent->children->items[0];
909 if (child->view) {
910 render_containers_linear(output,damage, parent);
911 return;
912 }
913 }
914
915 switch (parent->layout) {
916 case L_NONE:
917 case L_HORIZ:
918 case L_VERT:
919 render_containers_linear(output, damage, parent);
920 break;
921 case L_STACKED:
922 render_containers_stacked(output, damage, parent);
923 break;
924 case L_TABBED:
925 render_containers_tabbed(output, damage, parent);
926 break;
927 }
928}
929
930static void render_container(struct sway_output *output,
931 pixman_region32_t *damage, struct sway_container *con, bool focused) {
932 struct parent_data data = {
933 .layout = con->current.layout,
934 .box = {
935 .x = floor(con->current.x),
936 .y = floor(con->current.y),
937 .width = con->current.width,
938 .height = con->current.height,
939 },
940 .children = con->current.children,
941 .focused = focused,
942 .active_child = con->current.focused_inactive_child,
943 };
944 render_containers(output, damage, &data);
945}
946
947static void render_workspace(struct sway_output *output,
948 pixman_region32_t *damage, struct sway_workspace *ws, bool focused) {
949 struct parent_data data = {
950 .layout = ws->current.layout,
951 .box = {
952 .x = floor(ws->current.x),
953 .y = floor(ws->current.y),
954 .width = ws->current.width,
955 .height = ws->current.height,
956 },
957 .children = ws->current.tiling,
958 .focused = focused,
959 .active_child = ws->current.focused_inactive_child,
960 };
961 render_containers(output, damage, &data);
962}
963
964static void render_floating_container(struct sway_output *soutput,
965 pixman_region32_t *damage, struct sway_container *con) {
966 if (con->view) {
967 struct sway_view *view = con->view;
968 struct border_colors *colors;
969 struct wlr_texture *title_texture;
970 struct wlr_texture *marks_texture;
971
972 if (view_is_urgent(view)) {
973 colors = &config->border_colors.urgent;
974 title_texture = con->title_urgent;
975 marks_texture = con->marks_urgent;
976 } else if (con->current.focused) {
977 colors = &config->border_colors.focused;
978 title_texture = con->title_focused;
979 marks_texture = con->marks_focused;
980 } else {
981 colors = &config->border_colors.unfocused;
982 title_texture = con->title_unfocused;
983 marks_texture = con->marks_unfocused;
984 }
985
986 if (con->current.border == B_NORMAL) {
987 render_titlebar(soutput, damage, con, floor(con->current.x),
988 floor(con->current.y), con->current.width, colors,
989 title_texture, marks_texture);
990 } else if (con->current.border == B_PIXEL) {
991 render_top_border(soutput, damage, con, colors);
992 }
993 render_view(soutput, damage, con, colors);
994 } else {
995 render_container(soutput, damage, con, con->current.focused);
996 }
997}
998
999static void render_floating(struct sway_output *soutput,
1000 pixman_region32_t *damage) {
1001 for (int i = 0; i < root->outputs->length; ++i) {
1002 struct sway_output *output = root->outputs->items[i];
1003 for (int j = 0; j < output->current.workspaces->length; ++j) {
1004 struct sway_workspace *ws = output->current.workspaces->items[j];
1005 if (!workspace_is_visible(ws)) {
1006 continue;
1007 }
1008 for (int k = 0; k < ws->current.floating->length; ++k) {
1009 struct sway_container *floater = ws->current.floating->items[k];
1010 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
1011 continue;
1012 }
1013 render_floating_container(soutput, damage, floater);
1014 }
1015 }
1016 }
1017}
1018
1019static void render_seatops(struct sway_output *output,
1020 pixman_region32_t *damage) {
1021 struct sway_seat *seat;
1022 wl_list_for_each(seat, &server.input->seats, link) {
1023 seatop_render(seat, output, damage);
1024 }
1025}
1026
1027void output_render(struct sway_output *output, struct timespec *when,
1028 pixman_region32_t *damage) {
1029 struct wlr_output *wlr_output = output->wlr_output;
1030 struct wlr_renderer *renderer = output->server->renderer;
1031
1032 struct sway_workspace *workspace = output->current.active_workspace;
1033 if (workspace == NULL) {
1034 return;
1035 }
1036
1037 struct sway_container *fullscreen_con = root->fullscreen_global;
1038 if (!fullscreen_con) {
1039 fullscreen_con = workspace->current.fullscreen;
1040 }
1041
1042 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
1043
1044 if (debug.damage == DAMAGE_RERENDER) {
1045 int width, height;
1046 wlr_output_transformed_resolution(wlr_output, &width, &height);
1047 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1048 }
1049
1050 if (!pixman_region32_not_empty(damage)) {
1051 // Output isn't damaged but needs buffer swap
1052 goto renderer_end;
1053 }
1054
1055 if (debug.damage == DAMAGE_HIGHLIGHT) {
1056 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
1057 }
1058
1059 if (server.session_lock.locked) {
1060 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1061 if (server.session_lock.lock == NULL) {
1062 // abandoned lock -> red BG
1063 clear_color[0] = 1.f;
1064 }
1065 int nrects;
1066 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1067 for (int i = 0; i < nrects; ++i) {
1068 scissor_output(wlr_output, &rects[i]);
1069 wlr_renderer_clear(renderer, clear_color);
1070 }
1071
1072 if (server.session_lock.lock != NULL) {
1073 struct render_data data = {
1074 .damage = damage,
1075 .alpha = 1.0f,
1076 };
1077
1078 struct wlr_session_lock_surface_v1 *lock_surface;
1079 wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
1080 if (lock_surface->output != wlr_output) {
1081 continue;
1082 }
1083 if (!lock_surface->mapped) {
1084 continue;
1085 }
1086
1087 output_surface_for_each_surface(output, lock_surface->surface,
1088 0.0, 0.0, render_surface_iterator, &data);
1089 }
1090 }
1091 goto renderer_end;
1092 }
1093
1094 if (output_has_opaque_overlay_layer_surface(output)) {
1095 goto render_overlay;
1096 }
1097
1098 if (fullscreen_con) {
1099 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1100
1101 int nrects;
1102 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1103 for (int i = 0; i < nrects; ++i) {
1104 scissor_output(wlr_output, &rects[i]);
1105 wlr_renderer_clear(renderer, clear_color);
1106 }
1107
1108 if (fullscreen_con->view) {
1109 if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) {
1110 render_saved_view(fullscreen_con->view, output, damage, 1.0f);
1111 } else if (fullscreen_con->view->surface) {
1112 render_view_toplevels(fullscreen_con->view,
1113 output, damage, 1.0f);
1114 }
1115 } else {
1116 render_container(output, damage, fullscreen_con,
1117 fullscreen_con->current.focused);
1118 }
1119
1120 for (int i = 0; i < workspace->current.floating->length; ++i) {
1121 struct sway_container *floater =
1122 workspace->current.floating->items[i];
1123 if (container_is_transient_for(floater, fullscreen_con)) {
1124 render_floating_container(output, damage, floater);
1125 }
1126 }
1127#if HAVE_XWAYLAND
1128 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1129#endif
1130 } else {
1131 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
1132
1133 int nrects;
1134 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1135 for (int i = 0; i < nrects; ++i) {
1136 scissor_output(wlr_output, &rects[i]);
1137 wlr_renderer_clear(renderer, clear_color);
1138 }
1139
1140 render_layer_toplevel(output, damage,
1141 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1142 render_layer_toplevel(output, damage,
1143 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1144
1145 render_workspace(output, damage, workspace, workspace->current.focused);
1146 render_floating(output, damage);
1147#if HAVE_XWAYLAND
1148 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1149#endif
1150 render_layer_toplevel(output, damage,
1151 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1152
1153 render_layer_popups(output, damage,
1154 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1155 render_layer_popups(output, damage,
1156 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1157 render_layer_popups(output, damage,
1158 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1159 }
1160
1161 render_seatops(output, damage);
1162
1163 struct sway_seat *seat = input_manager_current_seat();
1164 struct sway_container *focus = seat_get_focused_container(seat);
1165 if (focus && focus->view) {
1166 render_view_popups(focus->view, output, damage, focus->alpha);
1167 }
1168
1169render_overlay:
1170 render_layer_toplevel(output, damage,
1171 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1172 render_layer_popups(output, damage,
1173 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1174 render_drag_icons(output, damage, &root->drag_icons);
1175
1176renderer_end:
1177 wlr_renderer_scissor(renderer, NULL);
1178 wlr_output_render_software_cursors(wlr_output, damage);
1179 wlr_renderer_end(renderer);
1180
1181 int width, height;
1182 wlr_output_transformed_resolution(wlr_output, &width, &height);
1183
1184 pixman_region32_t frame_damage;
1185 pixman_region32_init(&frame_damage);
1186
1187 enum wl_output_transform transform =
1188 wlr_output_transform_invert(wlr_output->transform);
1189 wlr_region_transform(&frame_damage, &output->damage->current,
1190 transform, width, height);
1191
1192 if (debug.damage != DAMAGE_DEFAULT) {
1193 pixman_region32_union_rect(&frame_damage, &frame_damage,
1194 0, 0, wlr_output->width, wlr_output->height);
1195 }
1196
1197 wlr_output_set_damage(wlr_output, &frame_damage);
1198 pixman_region32_fini(&frame_damage);
1199
1200 if (!wlr_output_commit(wlr_output)) {
1201 return;
1202 }
1203 output->last_frame = *when;
1204}
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c
deleted file mode 100644
index 1d7b536d..00000000
--- a/sway/desktop/surface.c
+++ /dev/null
@@ -1,46 +0,0 @@
1#define _POSIX_C_SOURCE 200112L
2#include <stdlib.h>
3#include <time.h>
4#include <wlr/types/wlr_compositor.h>
5#include "sway/server.h"
6#include "sway/surface.h"
7
8static 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
21static 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
31void handle_compositor_new_surface(struct wl_listener *listener, void *data) {
32 struct wlr_surface *wlr_surface = data;
33
34 struct sway_surface *surface = calloc(1, sizeof(struct sway_surface));
35 surface->wlr_surface = wlr_surface;
36 wlr_surface->data = surface;
37
38 surface->destroy.notify = handle_destroy;
39 wl_signal_add(&wlr_surface->events.destroy, &surface->destroy);
40
41 surface->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop,
42 surface_frame_done_timer_handler, surface);
43 if (!surface->frame_done_timer) {
44 wl_resource_post_no_memory(wlr_surface->resource);
45 }
46}
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index f5a3a053..042141ab 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -1,11 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
5#include <time.h> 4#include <time.h>
6#include <wlr/types/wlr_buffer.h> 5#include <wlr/types/wlr_buffer.h>
7#include "sway/config.h" 6#include "sway/config.h"
8#include "sway/desktop.h" 7#include "sway/scene_descriptor.h"
9#include "sway/desktop/idle_inhibit_v1.h" 8#include "sway/desktop/idle_inhibit_v1.h"
10#include "sway/desktop/transaction.h" 9#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 10#include "sway/input/cursor.h"
@@ -214,39 +213,20 @@ static void transaction_add_node(struct sway_transaction *transaction,
214 213
215static void apply_output_state(struct sway_output *output, 214static void apply_output_state(struct sway_output *output,
216 struct sway_output_state *state) { 215 struct sway_output_state *state) {
217 output_damage_whole(output);
218 list_free(output->current.workspaces); 216 list_free(output->current.workspaces);
219 memcpy(&output->current, state, sizeof(struct sway_output_state)); 217 memcpy(&output->current, state, sizeof(struct sway_output_state));
220 output_damage_whole(output);
221} 218}
222 219
223static void apply_workspace_state(struct sway_workspace *ws, 220static void apply_workspace_state(struct sway_workspace *ws,
224 struct sway_workspace_state *state) { 221 struct sway_workspace_state *state) {
225 output_damage_whole(ws->current.output);
226 list_free(ws->current.floating); 222 list_free(ws->current.floating);
227 list_free(ws->current.tiling); 223 list_free(ws->current.tiling);
228 memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); 224 memcpy(&ws->current, state, sizeof(struct sway_workspace_state));
229 output_damage_whole(ws->current.output);
230} 225}
231 226
232static void apply_container_state(struct sway_container *container, 227static void apply_container_state(struct sway_container *container,
233 struct sway_container_state *state) { 228 struct sway_container_state *state) {
234 struct sway_view *view = container->view; 229 struct sway_view *view = container->view;
235 // Damage the old location
236 desktop_damage_whole_container(container);
237 if (view && !wl_list_empty(&view->saved_buffers)) {
238 struct sway_saved_buffer *saved_buf;
239 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
240 struct wlr_box box = {
241 .x = saved_buf->x - view->saved_geometry.x,
242 .y = saved_buf->y - view->saved_geometry.y,
243 .width = saved_buf->width,
244 .height = saved_buf->height,
245 };
246 desktop_damage_box(&box);
247 }
248 }
249
250 // There are separate children lists for each instruction state, the 230 // There are separate children lists for each instruction state, the
251 // container's current state and the container's pending state 231 // container's current state and the container's pending state
252 // (ie. con->children). The list itself needs to be freed here. 232 // (ie. con->children). The list itself needs to be freed here.
@@ -256,35 +236,443 @@ static void apply_container_state(struct sway_container *container,
256 236
257 memcpy(&container->current, state, sizeof(struct sway_container_state)); 237 memcpy(&container->current, state, sizeof(struct sway_container_state));
258 238
259 if (view && !wl_list_empty(&view->saved_buffers)) { 239 if (view) {
260 if (!container->node.destroying || container->node.ntxnrefs == 1) { 240 if (view->saved_surface_tree) {
261 view_remove_saved_buffer(view); 241 if (!container->node.destroying || container->node.ntxnrefs == 1) {
242 view_remove_saved_buffer(view);
243 }
244 }
245
246 // If the view hasn't responded to the configure, center it within
247 // the container. This is important for fullscreen views which
248 // refuse to resize to the size of the output.
249 if (view->surface) {
250 view_center_and_clip_surface(view);
251 }
252 }
253}
254
255static 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
271static 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
285static void arrange_container(struct sway_container *con,
286 int width, int height, bool title_bar, int gaps);
287
288static void arrange_children(enum sway_container_layout layout, list_t *children,
289 struct sway_container *active, struct wlr_scene_tree *content,
290 int width, int height, int gaps) {
291 int title_bar_height = container_titlebar_height();
292
293 if (layout == L_TABBED) {
294 struct sway_container *first = children->length == 1 ?
295 ((struct sway_container *)children->items[0]) : NULL;
296 if (config->hide_lone_tab && first && first->view &&
297 first->current.border != B_NORMAL) {
298 title_bar_height = 0;
299 }
300
301 double w = (double) width / children->length;
302 int title_offset = 0;
303 for (int i = 0; i < children->length; i++) {
304 struct sway_container *child = children->items[i];
305 bool activated = child == active;
306 int next_title_offset = round(w * i + w);
307
308 arrange_title_bar(child, title_offset, -title_bar_height,
309 next_title_offset - title_offset, title_bar_height);
310 wlr_scene_node_set_enabled(&child->border.tree->node, activated);
311 wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height);
312 wlr_scene_node_reparent(&child->scene_tree->node, content);
313
314 if (activated) {
315 arrange_container(child, width, height - title_bar_height,
316 false, 0);
317 } else {
318 disable_container(child);
319 }
320
321 title_offset = next_title_offset;
322 }
323 } else if (layout == L_STACKED) {
324 struct sway_container *first = children->length == 1 ?
325 ((struct sway_container *)children->items[0]) : NULL;
326 if (config->hide_lone_tab && first && first->view &&
327 first->current.border != B_NORMAL) {
328 title_bar_height = 0;
329 }
330
331 int title_height = title_bar_height * children->length;
332
333 int y = 0;
334 for (int i = 0; i < children->length; i++) {
335 struct sway_container *child = children->items[i];
336 bool activated = child == active;
337
338 arrange_title_bar(child, 0, y - title_height, width, title_bar_height);
339 wlr_scene_node_set_enabled(&child->border.tree->node, activated);
340 wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height);
341 wlr_scene_node_reparent(&child->scene_tree->node, content);
342
343 if (activated) {
344 arrange_container(child, width, height - title_height,
345 false, 0);
346 } else {
347 disable_container(child);
348 }
349
350 y += title_bar_height;
351 }
352 } else if (layout == L_VERT) {
353 int off = 0;
354 for (int i = 0; i < children->length; i++) {
355 struct sway_container *child = children->items[i];
356 int cheight = child->current.height;
357
358 wlr_scene_node_set_enabled(&child->border.tree->node, true);
359 wlr_scene_node_set_position(&child->scene_tree->node, 0, off);
360 wlr_scene_node_reparent(&child->scene_tree->node, content);
361 arrange_container(child, width, cheight, true, gaps);
362 off += cheight + gaps;
363 }
364 } else if (layout == L_HORIZ) {
365 int off = 0;
366 for (int i = 0; i < children->length; i++) {
367 struct sway_container *child = children->items[i];
368 int cwidth = child->current.width;
369
370 wlr_scene_node_set_enabled(&child->border.tree->node, true);
371 wlr_scene_node_set_position(&child->scene_tree->node, off, 0);
372 wlr_scene_node_reparent(&child->scene_tree->node, content);
373 arrange_container(child, cwidth, height, true, gaps);
374 off += cwidth + gaps;
375 }
376 } else {
377 sway_assert(false, "unreachable");
378 }
379}
380
381static void arrange_container(struct sway_container *con,
382 int width, int height, bool title_bar, int gaps) {
383 // this container might have previously been in the scratchpad,
384 // make sure it's enabled for viewing
385 wlr_scene_node_set_enabled(&con->scene_tree->node, true);
386
387 if (con->output_handler) {
388 wlr_scene_buffer_set_dest_size(con->output_handler, width, height);
389 }
390
391 if (con->view) {
392 int border_top = container_titlebar_height();
393 int border_width = con->current.border_thickness;
394
395 if (title_bar && con->current.border != B_NORMAL) {
396 wlr_scene_node_set_enabled(&con->title_bar.tree->node, false);
397 wlr_scene_node_set_enabled(&con->border.top->node, true);
398 } else {
399 wlr_scene_node_set_enabled(&con->border.top->node, false);
400 }
401
402 if (con->current.border == B_NORMAL) {
403 if (title_bar) {
404 arrange_title_bar(con, 0, 0, width, border_top);
405 } else {
406 border_top = 0;
407 // should be handled by the parent container
408 }
409 } else if (con->current.border == B_PIXEL) {
410 container_update(con);
411 border_top = title_bar && con->current.border_top ? border_width : 0;
412 } else if (con->current.border == B_NONE) {
413 container_update(con);
414 border_top = 0;
415 border_width = 0;
416 } else if (con->current.border == B_CSD) {
417 border_top = 0;
418 border_width = 0;
419 } else {
420 sway_assert(false, "unreachable");
421 }
422
423 int border_bottom = con->current.border_bottom ? border_width : 0;
424 int border_left = con->current.border_left ? border_width : 0;
425 int border_right = con->current.border_right ? border_width : 0;
426
427 wlr_scene_rect_set_size(con->border.top, width, border_top);
428 wlr_scene_rect_set_size(con->border.bottom, width, border_bottom);
429 wlr_scene_rect_set_size(con->border.left,
430 border_left, height - border_top - border_bottom);
431 wlr_scene_rect_set_size(con->border.right,
432 border_right, height - border_top - border_bottom);
433
434 wlr_scene_node_set_position(&con->border.top->node, 0, 0);
435 wlr_scene_node_set_position(&con->border.bottom->node,
436 0, height - border_bottom);
437 wlr_scene_node_set_position(&con->border.left->node,
438 0, border_top);
439 wlr_scene_node_set_position(&con->border.right->node,
440 width - border_right, border_top);
441
442 // make sure to reparent, it's possible that the client just came out of
443 // fullscreen mode where the parent of the surface is not the container
444 wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree);
445 wlr_scene_node_set_position(&con->view->scene_tree->node,
446 border_left, border_top);
447 } else {
448 // make sure to disable the title bar if the parent is not managing it
449 if (title_bar) {
450 wlr_scene_node_set_enabled(&con->title_bar.tree->node, false);
451 }
452
453 arrange_children(con->current.layout, con->current.children,
454 con->current.focused_inactive_child, con->content_tree,
455 width, height, gaps);
456 }
457}
458
459static int container_get_gaps(struct sway_container *con) {
460 struct sway_workspace *ws = con->current.workspace;
461 struct sway_container *temp = con;
462 while (temp) {
463 enum sway_container_layout layout;
464 if (temp->current.parent) {
465 layout = temp->current.parent->current.layout;
466 } else {
467 layout = ws->current.layout;
468 }
469 if (layout == L_TABBED || layout == L_STACKED) {
470 return 0;
471 }
472 temp = temp->pending.parent;
473 }
474 return ws->gaps_inner;
475}
476
477static 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
496static void arrange_workspace_floating(struct sway_workspace *ws) {
497 for (int i = 0; i < ws->current.floating->length; i++) {
498 struct sway_container *floater = ws->current.floating->items[i];
499 struct wlr_scene_tree *layer = root->layers.floating;
500
501 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
502 continue;
503 }
504
505 if (root->fullscreen_global) {
506 if (container_is_transient_for(floater, root->fullscreen_global)) {
507 layer = root->layers.fullscreen_global;
508 }
509 } else {
510 for (int i = 0; i < root->outputs->length; i++) {
511 struct sway_output *output = root->outputs->items[i];
512 struct sway_workspace *active = output->current.active_workspace;
513
514 if (active && active->fullscreen &&
515 container_is_transient_for(floater, active->fullscreen)) {
516 layer = root->layers.fullscreen;
517 }
518 }
519 }
520
521 wlr_scene_node_reparent(&floater->scene_tree->node, layer);
522 wlr_scene_node_set_position(&floater->scene_tree->node,
523 floater->current.x, floater->current.y);
524 wlr_scene_node_set_enabled(&floater->scene_tree->node, true);
525
526 arrange_container(floater, floater->current.width, floater->current.height,
527 true, ws->gaps_inner);
528 }
529}
530
531static 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
538static 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
557static void arrange_output(struct sway_output *output, int width, int height) {
558 for (int i = 0; i < output->current.workspaces->length; i++) {
559 struct sway_workspace *child = output->current.workspaces->items[i];
560
561 bool activated = output->current.active_workspace == child;
562
563 wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling);
564 wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen);
565
566 for (int i = 0; i < child->current.floating->length; i++) {
567 struct sway_container *floater = child->current.floating->items[i];
568 wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating);
569 wlr_scene_node_set_enabled(&floater->scene_tree->node, activated);
570 }
571
572 if (activated) {
573 struct sway_container *fs = child->current.fullscreen;
574 wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs);
575 wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs);
576
577 arrange_workspace_floating(child);
578
579 wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs);
580 wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs);
581 wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs);
582
583 if (fs) {
584 wlr_scene_rect_set_size(output->fullscreen_background, width, height);
585
586 arrange_fullscreen(child->layers.fullscreen, fs, child,
587 width, height);
588 } else {
589 struct wlr_box *area = &output->usable_area;
590 struct side_gaps *gaps = &child->current_gaps;
591
592 wlr_scene_node_set_position(&child->layers.tiling->node,
593 gaps->left + area->x, gaps->top + area->y);
594
595 arrange_workspace_tiling(child,
596 area->width - gaps->left - gaps->right,
597 area->height - gaps->top - gaps->bottom);
598 }
599 } else {
600 wlr_scene_node_set_enabled(&child->layers.tiling->node, false);
601 wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false);
602
603 disable_workspace(child);
262 } 604 }
263 } 605 }
606}
264 607
265 // If the view hasn't responded to the configure, center it within 608void arrange_popups(struct wlr_scene_tree *popups) {
266 // the container. This is important for fullscreen views which 609 struct wlr_scene_node *node;
267 // refuse to resize to the size of the output. 610 wl_list_for_each(node, &popups->children, link) {
268 if (view && view->surface) { 611 struct sway_popup_desc *popup = scene_descriptor_try_get(node,
269 view_center_surface(view); 612 SWAY_SCENE_DESC_POPUP);
613
614 int lx, ly;
615 wlr_scene_node_coords(popup->relative, &lx, &ly);
616 wlr_scene_node_set_position(node, lx, ly);
270 } 617 }
618}
619
620static void arrange_root(struct sway_root *root) {
621 struct sway_container *fs = root->fullscreen_global;
622
623 wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs);
624 wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs);
625 wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs);
626 wlr_scene_node_set_enabled(&root->layers.floating->node, !fs);
627 wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs);
628 wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs);
271 629
272 // Damage the new location 630 // hide all contents in the scratchpad
273 desktop_damage_whole_container(container); 631 for (int i = 0; i < root->scratchpad->length; i++) {
274 if (view && view->surface) { 632 struct sway_container *con = root->scratchpad->items[i];
275 struct wlr_surface *surface = view->surface; 633
276 struct wlr_box box = { 634 wlr_scene_node_set_enabled(&con->scene_tree->node, false);
277 .x = container->current.content_x - view->geometry.x,
278 .y = container->current.content_y - view->geometry.y,
279 .width = surface->current.width,
280 .height = surface->current.height,
281 };
282 desktop_damage_box(&box);
283 } 635 }
284 636
285 if (!container->node.destroying) { 637 if (fs) {
286 container_discover_outputs(container); 638 for (int i = 0; i < root->outputs->length; i++) {
639 struct sway_output *output = root->outputs->items[i];
640 struct sway_workspace *ws = output->current.active_workspace;
641
642 if (ws) {
643 arrange_workspace_floating(ws);
644 }
645 }
646
647 arrange_fullscreen(root->layers.fullscreen_global, fs, NULL,
648 root->width, root->height);
649 } else {
650 for (int i = 0; i < root->outputs->length; i++) {
651 struct sway_output *output = root->outputs->items[i];
652
653 wlr_scene_output_set_position(output->scene_output, output->lx, output->ly);
654
655 wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background);
656 wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom);
657 wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling);
658 wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top);
659 wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay);
660 wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen);
661 wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock);
662
663 wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly);
664 wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly);
665 wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly);
666 wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly);
667 wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly);
668 wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly);
669 wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly);
670
671 arrange_output(output, output->width, output->height);
672 }
287 } 673 }
674
675 arrange_popups(root->layers.popup);
288} 676}
289 677
290/** 678/**
@@ -326,8 +714,6 @@ static void transaction_apply(struct sway_transaction *transaction) {
326 714
327 node->instruction = NULL; 715 node->instruction = NULL;
328 } 716 }
329
330 cursor_rebase_all();
331} 717}
332 718
333static void transaction_commit_pending(void); 719static void transaction_commit_pending(void);
@@ -340,11 +726,13 @@ static void transaction_progress(void) {
340 return; 726 return;
341 } 727 }
342 transaction_apply(server.queued_transaction); 728 transaction_apply(server.queued_transaction);
729 arrange_root(root);
730 cursor_rebase_all();
343 transaction_destroy(server.queued_transaction); 731 transaction_destroy(server.queued_transaction);
344 server.queued_transaction = NULL; 732 server.queued_transaction = NULL;
345 733
346 if (!server.pending_transaction) { 734 if (!server.pending_transaction) {
347 sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); 735 sway_idle_inhibit_v1_check_active();
348 return; 736 return;
349 } 737 }
350 738
@@ -415,21 +803,11 @@ static void transaction_commit(struct sway_transaction *transaction) {
415 ++transaction->num_waiting; 803 ++transaction->num_waiting;
416 } 804 }
417 805
418 // From here on we are rendering a saved buffer of the view, which 806 view_send_frame_done(node->sway_container->view);
419 // means we can send a frame done event to make the client redraw it
420 // as soon as possible. Additionally, this is required if a view is
421 // mapping and its default geometry doesn't intersect an output.
422 struct timespec now;
423 clock_gettime(CLOCK_MONOTONIC, &now);
424 wlr_surface_send_frame_done(
425 node->sway_container->view->surface, &now);
426 } 807 }
427 if (!hidden && node_is_view(node) && 808 if (!hidden && node_is_view(node) &&
428 wl_list_empty(&node->sway_container->view->saved_buffers)) { 809 !node->sway_container->view->saved_surface_tree) {
429 view_save_buffer(node->sway_container->view); 810 view_save_buffer(node->sway_container->view);
430 memcpy(&node->sway_container->view->saved_geometry,
431 &node->sway_container->view->geometry,
432 sizeof(struct wlr_box));
433 } 811 }
434 node->instruction = instruction; 812 node->instruction = instruction;
435 } 813 }
@@ -499,16 +877,18 @@ static void set_instruction_ready(
499 transaction_progress(); 877 transaction_progress();
500} 878}
501 879
502void transaction_notify_view_ready_by_serial(struct sway_view *view, 880bool transaction_notify_view_ready_by_serial(struct sway_view *view,
503 uint32_t serial) { 881 uint32_t serial) {
504 struct sway_transaction_instruction *instruction = 882 struct sway_transaction_instruction *instruction =
505 view->container->node.instruction; 883 view->container->node.instruction;
506 if (instruction != NULL && instruction->serial == serial) { 884 if (instruction != NULL && instruction->serial == serial) {
507 set_instruction_ready(instruction); 885 set_instruction_ready(instruction);
886 return true;
508 } 887 }
888 return false;
509} 889}
510 890
511void transaction_notify_view_ready_by_geometry(struct sway_view *view, 891bool transaction_notify_view_ready_by_geometry(struct sway_view *view,
512 double x, double y, int width, int height) { 892 double x, double y, int width, int height) {
513 struct sway_transaction_instruction *instruction = 893 struct sway_transaction_instruction *instruction =
514 view->container->node.instruction; 894 view->container->node.instruction;
@@ -518,7 +898,9 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view,
518 instruction->container_state.content_width == width && 898 instruction->container_state.content_width == width &&
519 instruction->container_state.content_height == height) { 899 instruction->container_state.content_height == height) {
520 set_instruction_ready(instruction); 900 set_instruction_ready(instruction);
901 return true;
521 } 902 }
903 return false;
522} 904}
523 905
524static void _transaction_commit_dirty(bool server_request) { 906static void _transaction_commit_dirty(bool server_request) {
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 8da922d5..7c417891 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 199309L
2#include <float.h> 1#include <float.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -7,7 +6,7 @@
7#include <wlr/util/edges.h> 6#include <wlr/util/edges.h>
8#include "log.h" 7#include "log.h"
9#include "sway/decoration.h" 8#include "sway/decoration.h"
10#include "sway/desktop.h" 9#include "sway/scene_descriptor.h"
11#include "sway/desktop/transaction.h" 10#include "sway/desktop/transaction.h"
12#include "sway/input/cursor.h" 11#include "sway/input/cursor.h"
13#include "sway/input/input-manager.h" 12#include "sway/input/input-manager.h"
@@ -19,55 +18,39 @@
19#include "sway/tree/workspace.h" 18#include "sway/tree/workspace.h"
20#include "sway/xdg_decoration.h" 19#include "sway/xdg_decoration.h"
21 20
22static const struct sway_view_child_impl popup_impl;
23
24static void popup_get_view_coords(struct sway_view_child *child,
25 int *sx, int *sy) {
26 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
27 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
28
29 wlr_xdg_popup_get_toplevel_coords(wlr_popup,
30 wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x,
31 wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y,
32 sx, sy);
33}
34
35static void popup_destroy(struct sway_view_child *child) {
36 if (!sway_assert(child->impl == &popup_impl,
37 "Expected an xdg_shell popup")) {
38 return;
39 }
40 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
41 wl_list_remove(&popup->new_popup.link);
42 wl_list_remove(&popup->destroy.link);
43 free(popup);
44}
45
46static const struct sway_view_child_impl popup_impl = {
47 .get_view_coords = popup_get_view_coords,
48 .destroy = popup_destroy,
49};
50
51static struct sway_xdg_popup *popup_create( 21static struct sway_xdg_popup *popup_create(
52 struct wlr_xdg_popup *wlr_popup, struct sway_view *view); 22 struct wlr_xdg_popup *wlr_popup, struct sway_view *view,
23 struct wlr_scene_tree *parent);
53 24
54static void popup_handle_new_popup(struct wl_listener *listener, void *data) { 25static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
55 struct sway_xdg_popup *popup = 26 struct sway_xdg_popup *popup =
56 wl_container_of(listener, popup, new_popup); 27 wl_container_of(listener, popup, new_popup);
57 struct wlr_xdg_popup *wlr_popup = data; 28 struct wlr_xdg_popup *wlr_popup = data;
58 popup_create(wlr_popup, popup->child.view); 29 popup_create(wlr_popup, popup->view, popup->xdg_surface_tree);
59} 30}
60 31
61static void popup_handle_destroy(struct wl_listener *listener, void *data) { 32static void popup_handle_destroy(struct wl_listener *listener, void *data) {
62 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); 33 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy);
63 view_child_destroy(&popup->child); 34
35 wl_list_remove(&popup->new_popup.link);
36 wl_list_remove(&popup->destroy.link);
37 wl_list_remove(&popup->surface_commit.link);
38 wl_list_remove(&popup->reposition.link);
39 wlr_scene_node_destroy(&popup->scene_tree->node);
40 free(popup);
64} 41}
65 42
66static void popup_unconstrain(struct sway_xdg_popup *popup) { 43static void popup_unconstrain(struct sway_xdg_popup *popup) {
67 struct sway_view *view = popup->child.view; 44 struct sway_view *view = popup->view;
68 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; 45 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
69 46
70 struct sway_output *output = view->container->pending.workspace->output; 47 struct sway_workspace *workspace = view->container->pending.workspace;
48 if (!workspace) {
49 // is null if in the scratchpad
50 return;
51 }
52
53 struct sway_output *output = workspace->output;
71 54
72 // the output box expressed in the coordinate system of the toplevel parent 55 // the output box expressed in the coordinate system of the toplevel parent
73 // of the popup 56 // of the popup
@@ -81,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
81 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 64 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
82} 65}
83 66
84static struct sway_xdg_popup *popup_create( 67static void popup_handle_surface_commit(struct wl_listener *listener, void *data) {
85 struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { 68 struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit);
69 if (popup->wlr_xdg_popup->base->initial_commit) {
70 popup_unconstrain(popup);
71 }
72}
73
74static 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
79static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup,
80 struct sway_view *view, struct wlr_scene_tree *parent) {
86 struct wlr_xdg_surface *xdg_surface = wlr_popup->base; 81 struct wlr_xdg_surface *xdg_surface = wlr_popup->base;
87 82
88 struct sway_xdg_popup *popup = 83 struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup));
89 calloc(1, sizeof(struct sway_xdg_popup)); 84 if (!popup) {
90 if (popup == NULL) { 85 return NULL;
86 }
87
88 popup->wlr_xdg_popup = wlr_popup;
89 popup->view = view;
90
91 popup->scene_tree = wlr_scene_tree_create(parent);
92 if (!popup->scene_tree) {
93 free(popup);
91 return NULL; 94 return NULL;
92 } 95 }
93 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); 96
97 popup->xdg_surface_tree = wlr_scene_xdg_surface_create(
98 popup->scene_tree, xdg_surface);
99 if (!popup->xdg_surface_tree) {
100 wlr_scene_node_destroy(&popup->scene_tree->node);
101 free(popup);
102 return NULL;
103 }
104
105 popup->desc.relative = &view->content_tree->node;
106 popup->desc.view = view;
107
108 if (!scene_descriptor_assign(&popup->scene_tree->node,
109 SWAY_SCENE_DESC_POPUP, &popup->desc)) {
110 sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor");
111 wlr_scene_node_destroy(&popup->scene_tree->node);
112 free(popup);
113 return NULL;
114 }
115
94 popup->wlr_xdg_popup = xdg_surface->popup; 116 popup->wlr_xdg_popup = xdg_surface->popup;
117 struct sway_xdg_shell_view *shell_view =
118 wl_container_of(view, shell_view, view);
119 xdg_surface->data = shell_view;
95 120
121 wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit);
122 popup->surface_commit.notify = popup_handle_surface_commit;
96 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 123 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
97 popup->new_popup.notify = popup_handle_new_popup; 124 popup->new_popup.notify = popup_handle_new_popup;
98 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); 125 wl_signal_add(&wlr_popup->events.reposition, &popup->reposition);
126 popup->reposition.notify = popup_handle_reposition;
127 wl_signal_add(&wlr_popup->events.destroy, &popup->destroy);
99 popup->destroy.notify = popup_handle_destroy; 128 popup->destroy.notify = popup_handle_destroy;
100 129
101 wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map);
102 wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap);
103
104 popup_unconstrain(popup);
105
106 return popup; 130 return popup;
107} 131}
108 132
109
110static struct sway_xdg_shell_view *xdg_shell_view_from_view( 133static struct sway_xdg_shell_view *xdg_shell_view_from_view(
111 struct sway_view *view) { 134 struct sway_view *view) {
112 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, 135 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL,
@@ -163,12 +186,19 @@ static void set_tiled(struct sway_view *view, bool tiled) {
163 if (xdg_shell_view_from_view(view) == NULL) { 186 if (xdg_shell_view_from_view(view) == NULL) {
164 return; 187 return;
165 } 188 }
166 enum wlr_edges edges = WLR_EDGE_NONE; 189 if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >=
167 if (tiled) { 190 XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) {
168 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | 191 enum wlr_edges edges = WLR_EDGE_NONE;
169 WLR_EDGE_BOTTOM; 192 if (tiled) {
193 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP |
194 WLR_EDGE_BOTTOM;
195 }
196 wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
197 } else {
198 // The version is too low for the tiled state; configure as maximized instead
199 // to stop the client from drawing decorations outside of the toplevel geometry.
200 wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled);
170 } 201 }
171 wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
172} 202}
173 203
174static void set_fullscreen(struct sway_view *view, bool fullscreen) { 204static void set_fullscreen(struct sway_view *view, bool fullscreen) {
@@ -194,24 +224,6 @@ static bool wants_floating(struct sway_view *view) {
194 || toplevel->parent; 224 || toplevel->parent;
195} 225}
196 226
197static void for_each_surface(struct sway_view *view,
198 wlr_surface_iterator_func_t iterator, void *user_data) {
199 if (xdg_shell_view_from_view(view) == NULL) {
200 return;
201 }
202 wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator,
203 user_data);
204}
205
206static void for_each_popup_surface(struct sway_view *view,
207 wlr_surface_iterator_func_t iterator, void *user_data) {
208 if (xdg_shell_view_from_view(view) == NULL) {
209 return;
210 }
211 wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base,
212 iterator, user_data);
213}
214
215static bool is_transient_for(struct sway_view *child, 227static bool is_transient_for(struct sway_view *child,
216 struct sway_view *ancestor) { 228 struct sway_view *ancestor) {
217 if (xdg_shell_view_from_view(child) == NULL) { 229 if (xdg_shell_view_from_view(child) == NULL) {
@@ -259,8 +271,6 @@ static const struct sway_view_impl view_impl = {
259 .set_fullscreen = set_fullscreen, 271 .set_fullscreen = set_fullscreen,
260 .set_resizing = set_resizing, 272 .set_resizing = set_resizing,
261 .wants_floating = wants_floating, 273 .wants_floating = wants_floating,
262 .for_each_surface = for_each_surface,
263 .for_each_popup_surface = for_each_popup_surface,
264 .is_transient_for = is_transient_for, 274 .is_transient_for = is_transient_for,
265 .close = _close, 275 .close = _close,
266 .close_popups = close_popups, 276 .close_popups = close_popups,
@@ -273,6 +283,20 @@ static void handle_commit(struct wl_listener *listener, void *data) {
273 struct sway_view *view = &xdg_shell_view->view; 283 struct sway_view *view = &xdg_shell_view->view;
274 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; 284 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base;
275 285
286 if (xdg_surface->initial_commit) {
287 if (view->xdg_decoration != NULL) {
288 set_xdg_decoration_mode(view->xdg_decoration);
289 }
290 // XXX: https://github.com/swaywm/sway/issues/2176
291 wlr_xdg_surface_schedule_configure(xdg_surface);
292 // TODO: wlr_xdg_toplevel_set_bounds()
293 return;
294 }
295
296 if (!xdg_surface->surface->mapped) {
297 return;
298 }
299
276 struct wlr_box new_geo; 300 struct wlr_box new_geo;
277 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); 301 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
278 bool new_size = new_geo.width != view->geometry.width || 302 bool new_size = new_geo.width != view->geometry.width ||
@@ -284,23 +308,32 @@ static void handle_commit(struct wl_listener *listener, void *data) {
284 // The client changed its surface size in this commit. For floating 308 // The client changed its surface size in this commit. For floating
285 // containers, we resize the container to match. For tiling containers, 309 // containers, we resize the container to match. For tiling containers,
286 // we only recenter the surface. 310 // we only recenter the surface.
287 desktop_damage_view(view);
288 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 311 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
289 if (container_is_floating(view->container)) { 312 if (container_is_floating(view->container)) {
290 view_update_size(view); 313 view_update_size(view);
314 // Only set the toplevel size the current container actually has a size.
315 if (view->container->current.width) {
316 wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width,
317 view->geometry.height);
318 }
291 transaction_commit_dirty_client(); 319 transaction_commit_dirty_client();
292 } else {
293 view_center_surface(view);
294 } 320 }
295 desktop_damage_view(view); 321
322 view_center_and_clip_surface(view);
296 } 323 }
297 324
298 if (view->container->node.instruction) { 325 if (view->container->node.instruction) {
299 transaction_notify_view_ready_by_serial(view, 326 bool successful = transaction_notify_view_ready_by_serial(view,
300 xdg_surface->current.configure_serial); 327 xdg_surface->current.configure_serial);
301 }
302 328
303 view_damage_from(view); 329 // If we saved the view and this commit isn't what we're looking for
330 // that means the user will never actually see the buffers submitted to
331 // us here. Just send frame done events to these surfaces so they can
332 // commit another time for us.
333 if (view->saved_surface_tree && !successful) {
334 view_send_frame_done(view);
335 }
336 }
304} 337}
305 338
306static void handle_set_title(struct wl_listener *listener, void *data) { 339static void handle_set_title(struct wl_listener *listener, void *data) {
@@ -315,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) {
315 struct sway_xdg_shell_view *xdg_shell_view = 348 struct sway_xdg_shell_view *xdg_shell_view =
316 wl_container_of(listener, xdg_shell_view, set_app_id); 349 wl_container_of(listener, xdg_shell_view, set_app_id);
317 struct sway_view *view = &xdg_shell_view->view; 350 struct sway_view *view = &xdg_shell_view->view;
351 view_update_app_id(view);
318 view_execute_criteria(view); 352 view_execute_criteria(view);
319} 353}
320 354
@@ -322,7 +356,16 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
322 struct sway_xdg_shell_view *xdg_shell_view = 356 struct sway_xdg_shell_view *xdg_shell_view =
323 wl_container_of(listener, xdg_shell_view, new_popup); 357 wl_container_of(listener, xdg_shell_view, new_popup);
324 struct wlr_xdg_popup *wlr_popup = data; 358 struct wlr_xdg_popup *wlr_popup = data;
325 popup_create(wlr_popup, &xdg_shell_view->view); 359
360 struct sway_xdg_popup *popup = popup_create(wlr_popup,
361 &xdg_shell_view->view, root->layers.popup);
362 if (!popup) {
363 return;
364 }
365
366 int lx, ly;
367 wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly);
368 wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly);
326} 369}
327 370
328static void handle_request_maximize(struct wl_listener *listener, void *data) { 371static void handle_request_maximize(struct wl_listener *listener, void *data) {
@@ -338,7 +381,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
338 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; 381 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
339 struct sway_view *view = &xdg_shell_view->view; 382 struct sway_view *view = &xdg_shell_view->view;
340 383
341 if (!toplevel->base->mapped) { 384 if (!toplevel->base->surface->mapped) {
342 return; 385 return;
343 } 386 }
344 387
@@ -403,7 +446,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
403 446
404 view_unmap(view); 447 view_unmap(view);
405 448
406 wl_list_remove(&xdg_shell_view->commit.link);
407 wl_list_remove(&xdg_shell_view->new_popup.link); 449 wl_list_remove(&xdg_shell_view->new_popup.link);
408 wl_list_remove(&xdg_shell_view->request_maximize.link); 450 wl_list_remove(&xdg_shell_view->request_maximize.link);
409 wl_list_remove(&xdg_shell_view->request_fullscreen.link); 451 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
@@ -446,10 +488,6 @@ static void handle_map(struct wl_listener *listener, void *data) {
446 488
447 transaction_commit_dirty(); 489 transaction_commit_dirty();
448 490
449 xdg_shell_view->commit.notify = handle_commit;
450 wl_signal_add(&toplevel->base->surface->events.commit,
451 &xdg_shell_view->commit);
452
453 xdg_shell_view->new_popup.notify = handle_new_popup; 491 xdg_shell_view->new_popup.notify = handle_new_popup;
454 wl_signal_add(&toplevel->base->events.new_popup, 492 wl_signal_add(&toplevel->base->events.new_popup,
455 &xdg_shell_view->new_popup); 493 &xdg_shell_view->new_popup);
@@ -489,6 +527,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
489 wl_list_remove(&xdg_shell_view->destroy.link); 527 wl_list_remove(&xdg_shell_view->destroy.link);
490 wl_list_remove(&xdg_shell_view->map.link); 528 wl_list_remove(&xdg_shell_view->map.link);
491 wl_list_remove(&xdg_shell_view->unmap.link); 529 wl_list_remove(&xdg_shell_view->unmap.link);
530 wl_list_remove(&xdg_shell_view->commit.link);
492 view->wlr_xdg_toplevel = NULL; 531 view->wlr_xdg_toplevel = NULL;
493 if (view->xdg_decoration) { 532 if (view->xdg_decoration) {
494 view->xdg_decoration->view = NULL; 533 view->xdg_decoration->view = NULL;
@@ -501,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface(
501 return xdg_surface->data; 540 return xdg_surface->data;
502} 541}
503 542
504void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { 543void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) {
505 struct wlr_xdg_surface *xdg_surface = data; 544 struct wlr_xdg_toplevel *xdg_toplevel = data;
506
507 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
508 sway_log(SWAY_DEBUG, "New xdg_shell popup");
509 return;
510 }
511 545
512 sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", 546 sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",
513 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); 547 xdg_toplevel->title, xdg_toplevel->app_id);
514 wlr_xdg_surface_ping(xdg_surface); 548 wlr_xdg_surface_ping(xdg_toplevel->base);
515 549
516 struct sway_xdg_shell_view *xdg_shell_view = 550 struct sway_xdg_shell_view *xdg_shell_view =
517 calloc(1, sizeof(struct sway_xdg_shell_view)); 551 calloc(1, sizeof(struct sway_xdg_shell_view));
@@ -519,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
519 return; 553 return;
520 } 554 }
521 555
522 view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); 556 if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) {
523 xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; 557 free(xdg_shell_view);
558 return;
559 }
560 xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel;
524 561
525 xdg_shell_view->map.notify = handle_map; 562 xdg_shell_view->map.notify = handle_map;
526 wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); 563 wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map);
527 564
528 xdg_shell_view->unmap.notify = handle_unmap; 565 xdg_shell_view->unmap.notify = handle_unmap;
529 wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); 566 wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap);
567
568 xdg_shell_view->commit.notify = handle_commit;
569 wl_signal_add(&xdg_toplevel->base->surface->events.commit,
570 &xdg_shell_view->commit);
530 571
531 xdg_shell_view->destroy.notify = handle_destroy; 572 xdg_shell_view->destroy.notify = handle_destroy;
532 wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); 573 wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy);
574
575 wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base);
576
577 xdg_toplevel->base->data = xdg_shell_view;
533 578
534 xdg_surface->data = xdg_shell_view; 579 wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel,
580 XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
535} 581}
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 7c5dde53..270cf08f 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -1,21 +1,23 @@
1#define _POSIX_C_SOURCE 199309L
2#include <float.h> 1#include <float.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
5#include <wayland-server-core.h> 4#include <wayland-server-core.h>
6#include <wlr/types/wlr_output_layout.h> 5#include <wlr/types/wlr_output_layout.h>
7#include <wlr/types/wlr_output.h> 6#include <wlr/types/wlr_output.h>
7#include <wlr/types/wlr_xdg_activation_v1.h>
8#include <wlr/types/wlr_scene.h>
8#include <wlr/xwayland.h> 9#include <wlr/xwayland.h>
9#include <xcb/xcb_icccm.h> 10#include <xcb/xcb_icccm.h>
10#include "log.h" 11#include "log.h"
11#include "sway/desktop.h"
12#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
13#include "sway/input/cursor.h" 13#include "sway/input/cursor.h"
14#include "sway/input/input-manager.h" 14#include "sway/input/input-manager.h"
15#include "sway/input/seat.h" 15#include "sway/input/seat.h"
16#include "sway/output.h" 16#include "sway/output.h"
17#include "sway/scene_descriptor.h"
17#include "sway/tree/arrange.h" 18#include "sway/tree/arrange.h"
18#include "sway/tree/container.h" 19#include "sway/tree/container.h"
20#include "sway/server.h"
19#include "sway/tree/view.h" 21#include "sway/tree/view.h"
20#include "sway/tree/workspace.h" 22#include "sway/tree/workspace.h"
21 23
@@ -43,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener,
43 ev->width, ev->height); 45 ev->width, ev->height);
44} 46}
45 47
46static void unmanaged_handle_commit(struct wl_listener *listener, void *data) {
47 struct sway_xwayland_unmanaged *surface =
48 wl_container_of(listener, surface, commit);
49 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
50
51 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
52 false);
53}
54
55static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { 48static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) {
56 struct sway_xwayland_unmanaged *surface = 49 struct sway_xwayland_unmanaged *surface =
57 wl_container_of(listener, surface, set_geometry); 50 wl_container_of(listener, surface, set_geometry);
58 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 51 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
59 52
60 if (xsurface->x != surface->lx || xsurface->y != surface->ly) { 53 wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y);
61 // Surface has moved
62 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
63 true);
64 surface->lx = xsurface->x;
65 surface->ly = xsurface->y;
66 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
67 true);
68 }
69} 54}
70 55
71static void unmanaged_handle_map(struct wl_listener *listener, void *data) { 56static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
@@ -73,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
73 wl_container_of(listener, surface, map); 58 wl_container_of(listener, surface, map);
74 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 59 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
75 60
76 wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); 61 surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged,
62 xsurface->surface);
77 63
78 wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); 64 if (surface->surface_scene) {
79 surface->set_geometry.notify = unmanaged_handle_set_geometry; 65 scene_descriptor_assign(&surface->surface_scene->buffer->node,
66 SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface);
67 wlr_scene_node_set_position(&surface->surface_scene->buffer->node,
68 xsurface->x, xsurface->y);
80 69
81 wl_signal_add(&xsurface->surface->events.commit, &surface->commit); 70 wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry);
82 surface->commit.notify = unmanaged_handle_commit; 71 surface->set_geometry.notify = unmanaged_handle_set_geometry;
83 72 }
84 surface->lx = xsurface->x;
85 surface->ly = xsurface->y;
86 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true);
87 73
88 if (wlr_xwayland_or_surface_wants_focus(xsurface)) { 74 if (wlr_xwayland_or_surface_wants_focus(xsurface)) {
89 struct sway_seat *seat = input_manager_current_seat(); 75 struct sway_seat *seat = input_manager_current_seat();
@@ -97,10 +83,13 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
97 struct sway_xwayland_unmanaged *surface = 83 struct sway_xwayland_unmanaged *surface =
98 wl_container_of(listener, surface, unmap); 84 wl_container_of(listener, surface, unmap);
99 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 85 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
100 desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); 86
101 wl_list_remove(&surface->link); 87 if (surface->surface_scene) {
102 wl_list_remove(&surface->set_geometry.link); 88 wl_list_remove(&surface->set_geometry.link);
103 wl_list_remove(&surface->commit.link); 89
90 wlr_scene_node_destroy(&surface->surface_scene->buffer->node);
91 surface->surface_scene = NULL;
92 }
104 93
105 struct sway_seat *seat = input_manager_current_seat(); 94 struct sway_seat *seat = input_manager_current_seat();
106 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { 95 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) {
@@ -123,8 +112,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
123} 112}
124 113
125static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { 114static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) {
126 struct wlr_xwayland_surface *xsurface = data; 115 struct sway_xwayland_unmanaged *surface =
127 if (!xsurface->mapped) { 116 wl_container_of(listener, surface, request_activate);
117 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
118 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
128 return; 119 return;
129 } 120 }
130 struct sway_seat *seat = input_manager_current_seat(); 121 struct sway_seat *seat = input_manager_current_seat();
@@ -136,12 +127,29 @@ static void unmanaged_handle_request_activate(struct wl_listener *listener, void
136 seat_set_focus_surface(seat, xsurface->surface, false); 127 seat_set_focus_surface(seat, xsurface->surface, false);
137} 128}
138 129
130static 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
140static 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
139static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { 147static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
140 struct sway_xwayland_unmanaged *surface = 148 struct sway_xwayland_unmanaged *surface =
141 wl_container_of(listener, surface, destroy); 149 wl_container_of(listener, surface, destroy);
142 wl_list_remove(&surface->request_configure.link); 150 wl_list_remove(&surface->request_configure.link);
143 wl_list_remove(&surface->map.link); 151 wl_list_remove(&surface->associate.link);
144 wl_list_remove(&surface->unmap.link); 152 wl_list_remove(&surface->dissociate.link);
145 wl_list_remove(&surface->destroy.link); 153 wl_list_remove(&surface->destroy.link);
146 wl_list_remove(&surface->override_redirect.link); 154 wl_list_remove(&surface->override_redirect.link);
147 wl_list_remove(&surface->request_activate.link); 155 wl_list_remove(&surface->request_activate.link);
@@ -149,6 +157,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
149} 157}
150 158
151static void handle_map(struct wl_listener *listener, void *data); 159static void handle_map(struct wl_listener *listener, void *data);
160static void handle_associate(struct wl_listener *listener, void *data);
152 161
153struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); 162struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface);
154 163
@@ -157,14 +166,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi
157 wl_container_of(listener, surface, override_redirect); 166 wl_container_of(listener, surface, override_redirect);
158 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 167 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
159 168
160 bool mapped = xsurface->mapped; 169 bool associated = xsurface->surface != NULL;
170 bool mapped = associated && xsurface->surface->mapped;
161 if (mapped) { 171 if (mapped) {
162 unmanaged_handle_unmap(&surface->unmap, NULL); 172 unmanaged_handle_unmap(&surface->unmap, NULL);
163 } 173 }
174 if (associated) {
175 unmanaged_handle_dissociate(&surface->dissociate, NULL);
176 }
164 177
165 unmanaged_handle_destroy(&surface->destroy, NULL); 178 unmanaged_handle_destroy(&surface->destroy, NULL);
166 xsurface->data = NULL; 179 xsurface->data = NULL;
180
167 struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); 181 struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface);
182 if (associated) {
183 handle_associate(&xwayland_view->associate, NULL);
184 }
168 if (mapped) { 185 if (mapped) {
169 handle_map(&xwayland_view->map, xsurface); 186 handle_map(&xwayland_view->map, xsurface);
170 } 187 }
@@ -184,10 +201,10 @@ static struct sway_xwayland_unmanaged *create_unmanaged(
184 wl_signal_add(&xsurface->events.request_configure, 201 wl_signal_add(&xsurface->events.request_configure,
185 &surface->request_configure); 202 &surface->request_configure);
186 surface->request_configure.notify = unmanaged_handle_request_configure; 203 surface->request_configure.notify = unmanaged_handle_request_configure;
187 wl_signal_add(&xsurface->events.map, &surface->map); 204 wl_signal_add(&xsurface->events.associate, &surface->associate);
188 surface->map.notify = unmanaged_handle_map; 205 surface->associate.notify = unmanaged_handle_associate;
189 wl_signal_add(&xsurface->events.unmap, &surface->unmap); 206 wl_signal_add(&xsurface->events.dissociate, &surface->dissociate);
190 surface->unmap.notify = unmanaged_handle_unmap; 207 surface->dissociate.notify = unmanaged_handle_dissociate;
191 wl_signal_add(&xsurface->events.destroy, &surface->destroy); 208 wl_signal_add(&xsurface->events.destroy, &surface->destroy);
192 surface->destroy.notify = unmanaged_handle_destroy; 209 surface->destroy.notify = unmanaged_handle_destroy;
193 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); 210 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect);
@@ -396,17 +413,6 @@ static const struct sway_view_impl view_impl = {
396 .destroy = destroy, 413 .destroy = destroy,
397}; 414};
398 415
399static void get_geometry(struct sway_view *view, struct wlr_box *box) {
400 box->x = box->y = 0;
401 if (view->surface) {
402 box->width = view->surface->current.width;
403 box->height = view->surface->current.height;
404 } else {
405 box->width = 0;
406 box->height = 0;
407 }
408}
409
410static void handle_commit(struct wl_listener *listener, void *data) { 416static void handle_commit(struct wl_listener *listener, void *data) {
411 struct sway_xwayland_view *xwayland_view = 417 struct sway_xwayland_view *xwayland_view =
412 wl_container_of(listener, xwayland_view, commit); 418 wl_container_of(listener, xwayland_view, commit);
@@ -414,34 +420,38 @@ static void handle_commit(struct wl_listener *listener, void *data) {
414 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 420 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
415 struct wlr_surface_state *state = &xsurface->surface->current; 421 struct wlr_surface_state *state = &xsurface->surface->current;
416 422
417 struct wlr_box new_geo; 423 struct wlr_box new_geo = {0};
418 get_geometry(view, &new_geo); 424 new_geo.width = state->width;
425 new_geo.height = state->height;
426
419 bool new_size = new_geo.width != view->geometry.width || 427 bool new_size = new_geo.width != view->geometry.width ||
420 new_geo.height != view->geometry.height || 428 new_geo.height != view->geometry.height;
421 new_geo.x != view->geometry.x ||
422 new_geo.y != view->geometry.y;
423 429
424 if (new_size) { 430 if (new_size) {
425 // The client changed its surface size in this commit. For floating 431 // The client changed its surface size in this commit. For floating
426 // containers, we resize the container to match. For tiling containers, 432 // containers, we resize the container to match. For tiling containers,
427 // we only recenter the surface. 433 // we only recenter the surface.
428 desktop_damage_view(view);
429 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 434 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
430 if (container_is_floating(view->container)) { 435 if (container_is_floating(view->container)) {
431 view_update_size(view); 436 view_update_size(view);
432 transaction_commit_dirty_client(); 437 transaction_commit_dirty_client();
433 } else {
434 view_center_surface(view);
435 } 438 }
436 desktop_damage_view(view); 439
440 view_center_and_clip_surface(view);
437 } 441 }
438 442
439 if (view->container->node.instruction) { 443 if (view->container->node.instruction) {
440 transaction_notify_view_ready_by_geometry(view, 444 bool successful = transaction_notify_view_ready_by_geometry(view,
441 xsurface->x, xsurface->y, state->width, state->height); 445 xsurface->x, xsurface->y, state->width, state->height);
442 }
443 446
444 view_damage_from(view); 447 // If we saved the view and this commit isn't what we're looking for
448 // that means the user will never actually see the buffers submitted to
449 // us here. Just send frame done events to these surfaces so they can
450 // commit another time for us.
451 if (view->saved_surface_tree && !successful) {
452 view_send_frame_done(view);
453 }
454 }
445} 455}
446 456
447static void handle_destroy(struct wl_listener *listener, void *data) { 457static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -466,11 +476,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
466 wl_list_remove(&xwayland_view->set_title.link); 476 wl_list_remove(&xwayland_view->set_title.link);
467 wl_list_remove(&xwayland_view->set_class.link); 477 wl_list_remove(&xwayland_view->set_class.link);
468 wl_list_remove(&xwayland_view->set_role.link); 478 wl_list_remove(&xwayland_view->set_role.link);
479 wl_list_remove(&xwayland_view->set_startup_id.link);
469 wl_list_remove(&xwayland_view->set_window_type.link); 480 wl_list_remove(&xwayland_view->set_window_type.link);
470 wl_list_remove(&xwayland_view->set_hints.link); 481 wl_list_remove(&xwayland_view->set_hints.link);
471 wl_list_remove(&xwayland_view->set_decorations.link); 482 wl_list_remove(&xwayland_view->set_decorations.link);
472 wl_list_remove(&xwayland_view->map.link); 483 wl_list_remove(&xwayland_view->associate.link);
473 wl_list_remove(&xwayland_view->unmap.link); 484 wl_list_remove(&xwayland_view->dissociate.link);
474 wl_list_remove(&xwayland_view->override_redirect.link); 485 wl_list_remove(&xwayland_view->override_redirect.link);
475 view_begin_destroy(&xwayland_view->view); 486 view_begin_destroy(&xwayland_view->view);
476} 487}
@@ -484,16 +495,28 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
484 return; 495 return;
485 } 496 }
486 497
498 wl_list_remove(&xwayland_view->commit.link);
499 wl_list_remove(&xwayland_view->surface_tree_destroy.link);
500
501 if (xwayland_view->surface_tree) {
502 wlr_scene_node_destroy(&xwayland_view->surface_tree->node);
503 xwayland_view->surface_tree = NULL;
504 }
505
487 view_unmap(view); 506 view_unmap(view);
507}
488 508
489 wl_list_remove(&xwayland_view->commit.link); 509static void handle_surface_tree_destroy(struct wl_listener *listener, void *data) {
510 struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view,
511 surface_tree_destroy);
512 xwayland_view->surface_tree = NULL;
490} 513}
491 514
492static void handle_map(struct wl_listener *listener, void *data) { 515static void handle_map(struct wl_listener *listener, void *data) {
493 struct sway_xwayland_view *xwayland_view = 516 struct sway_xwayland_view *xwayland_view =
494 wl_container_of(listener, xwayland_view, map); 517 wl_container_of(listener, xwayland_view, map);
495 struct wlr_xwayland_surface *xsurface = data;
496 struct sway_view *view = &xwayland_view->view; 518 struct sway_view *view = &xwayland_view->view;
519 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
497 520
498 view->natural_width = xsurface->width; 521 view->natural_width = xsurface->width;
499 view->natural_height = xsurface->height; 522 view->natural_height = xsurface->height;
@@ -506,23 +529,42 @@ static void handle_map(struct wl_listener *listener, void *data) {
506 // Put it back into the tree 529 // Put it back into the tree
507 view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); 530 view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false);
508 531
532 xwayland_view->surface_tree = wlr_scene_subsurface_tree_create(
533 xwayland_view->view.content_tree, xsurface->surface);
534
535 if (xwayland_view->surface_tree) {
536 xwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy;
537 wl_signal_add(&xwayland_view->surface_tree->node.events.destroy,
538 &xwayland_view->surface_tree_destroy);
539 }
540
509 transaction_commit_dirty(); 541 transaction_commit_dirty();
510} 542}
511 543
544static void handle_dissociate(struct wl_listener *listener, void *data);
545
512static void handle_override_redirect(struct wl_listener *listener, void *data) { 546static void handle_override_redirect(struct wl_listener *listener, void *data) {
513 struct sway_xwayland_view *xwayland_view = 547 struct sway_xwayland_view *xwayland_view =
514 wl_container_of(listener, xwayland_view, override_redirect); 548 wl_container_of(listener, xwayland_view, override_redirect);
515 struct wlr_xwayland_surface *xsurface = data;
516 struct sway_view *view = &xwayland_view->view; 549 struct sway_view *view = &xwayland_view->view;
550 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
517 551
518 bool mapped = xsurface->mapped; 552 bool associated = xsurface->surface != NULL;
553 bool mapped = associated && xsurface->surface->mapped;
519 if (mapped) { 554 if (mapped) {
520 handle_unmap(&xwayland_view->unmap, NULL); 555 handle_unmap(&xwayland_view->unmap, NULL);
521 } 556 }
557 if (associated) {
558 handle_dissociate(&xwayland_view->dissociate, NULL);
559 }
522 560
523 handle_destroy(&xwayland_view->destroy, view); 561 handle_destroy(&xwayland_view->destroy, view);
524 xsurface->data = NULL; 562 xsurface->data = NULL;
563
525 struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); 564 struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface);
565 if (associated) {
566 unmanaged_handle_associate(&unmanaged->associate, NULL);
567 }
526 if (mapped) { 568 if (mapped) {
527 unmanaged_handle_map(&unmanaged->map, xsurface); 569 unmanaged_handle_map(&unmanaged->map, xsurface);
528 } 570 }
@@ -534,7 +576,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
534 struct wlr_xwayland_surface_configure_event *ev = data; 576 struct wlr_xwayland_surface_configure_event *ev = data;
535 struct sway_view *view = &xwayland_view->view; 577 struct sway_view *view = &xwayland_view->view;
536 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 578 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
537 if (!xsurface->mapped) { 579 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
538 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, 580 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
539 ev->width, ev->height); 581 ev->width, ev->height);
540 return; 582 return;
@@ -563,7 +605,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
563 wl_container_of(listener, xwayland_view, request_fullscreen); 605 wl_container_of(listener, xwayland_view, request_fullscreen);
564 struct sway_view *view = &xwayland_view->view; 606 struct sway_view *view = &xwayland_view->view;
565 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 607 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
566 if (!xsurface->mapped) { 608 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
567 return; 609 return;
568 } 610 }
569 container_set_fullscreen(view->container, xsurface->fullscreen); 611 container_set_fullscreen(view->container, xsurface->fullscreen);
@@ -577,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) {
577 wl_container_of(listener, xwayland_view, request_minimize); 619 wl_container_of(listener, xwayland_view, request_minimize);
578 struct sway_view *view = &xwayland_view->view; 620 struct sway_view *view = &xwayland_view->view;
579 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 621 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
580 if (!xsurface->mapped) { 622 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
581 return; 623 return;
582 } 624 }
583 625
@@ -592,7 +634,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
592 wl_container_of(listener, xwayland_view, request_move); 634 wl_container_of(listener, xwayland_view, request_move);
593 struct sway_view *view = &xwayland_view->view; 635 struct sway_view *view = &xwayland_view->view;
594 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 636 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
595 if (!xsurface->mapped) { 637 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
596 return; 638 return;
597 } 639 }
598 if (!container_is_floating(view->container) || 640 if (!container_is_floating(view->container) ||
@@ -608,7 +650,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
608 wl_container_of(listener, xwayland_view, request_resize); 650 wl_container_of(listener, xwayland_view, request_resize);
609 struct sway_view *view = &xwayland_view->view; 651 struct sway_view *view = &xwayland_view->view;
610 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 652 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
611 if (!xsurface->mapped) { 653 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
612 return; 654 return;
613 } 655 }
614 if (!container_is_floating(view->container)) { 656 if (!container_is_floating(view->container)) {
@@ -624,10 +666,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) {
624 wl_container_of(listener, xwayland_view, request_activate); 666 wl_container_of(listener, xwayland_view, request_activate);
625 struct sway_view *view = &xwayland_view->view; 667 struct sway_view *view = &xwayland_view->view;
626 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 668 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
627 if (!xsurface->mapped) { 669 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
628 return; 670 return;
629 } 671 }
630 view_request_activate(view); 672 view_request_activate(view, NULL);
631 673
632 transaction_commit_dirty(); 674 transaction_commit_dirty();
633} 675}
@@ -637,7 +679,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) {
637 wl_container_of(listener, xwayland_view, set_title); 679 wl_container_of(listener, xwayland_view, set_title);
638 struct sway_view *view = &xwayland_view->view; 680 struct sway_view *view = &xwayland_view->view;
639 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 681 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
640 if (!xsurface->mapped) { 682 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
641 return; 683 return;
642 } 684 }
643 view_update_title(view, false); 685 view_update_title(view, false);
@@ -649,7 +691,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) {
649 wl_container_of(listener, xwayland_view, set_class); 691 wl_container_of(listener, xwayland_view, set_class);
650 struct sway_view *view = &xwayland_view->view; 692 struct sway_view *view = &xwayland_view->view;
651 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 693 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
652 if (!xsurface->mapped) { 694 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
653 return; 695 return;
654 } 696 }
655 view_execute_criteria(view); 697 view_execute_criteria(view);
@@ -660,18 +702,43 @@ static void handle_set_role(struct wl_listener *listener, void *data) {
660 wl_container_of(listener, xwayland_view, set_role); 702 wl_container_of(listener, xwayland_view, set_role);
661 struct sway_view *view = &xwayland_view->view; 703 struct sway_view *view = &xwayland_view->view;
662 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 704 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
663 if (!xsurface->mapped) { 705 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
664 return; 706 return;
665 } 707 }
666 view_execute_criteria(view); 708 view_execute_criteria(view);
667} 709}
668 710
711static 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
669static void handle_set_window_type(struct wl_listener *listener, void *data) { 736static void handle_set_window_type(struct wl_listener *listener, void *data) {
670 struct sway_xwayland_view *xwayland_view = 737 struct sway_xwayland_view *xwayland_view =
671 wl_container_of(listener, xwayland_view, set_window_type); 738 wl_container_of(listener, xwayland_view, set_window_type);
672 struct sway_view *view = &xwayland_view->view; 739 struct sway_view *view = &xwayland_view->view;
673 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 740 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
674 if (!xsurface->mapped) { 741 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
675 return; 742 return;
676 } 743 }
677 view_execute_criteria(view); 744 view_execute_criteria(view);
@@ -682,7 +749,7 @@ static void handle_set_hints(struct wl_listener *listener, void *data) {
682 wl_container_of(listener, xwayland_view, set_hints); 749 wl_container_of(listener, xwayland_view, set_hints);
683 struct sway_view *view = &xwayland_view->view; 750 struct sway_view *view = &xwayland_view->view;
684 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 751 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
685 if (!xsurface->mapped) { 752 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
686 return; 753 return;
687 } 754 }
688 const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); 755 const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints);
@@ -697,6 +764,24 @@ static void handle_set_hints(struct wl_listener *listener, void *data) {
697 } 764 }
698} 765}
699 766
767static 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
778static 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
700struct sway_view *view_from_wlr_xwayland_surface( 785struct sway_view *view_from_wlr_xwayland_surface(
701 struct wlr_xwayland_surface *xsurface) { 786 struct wlr_xwayland_surface *xsurface) {
702 return xsurface->data; 787 return xsurface->data;
@@ -712,7 +797,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
712 return NULL; 797 return NULL;
713 } 798 }
714 799
715 view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); 800 if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) {
801 free(xwayland_view);
802 return NULL;
803 }
716 xwayland_view->view.wlr_xwayland_surface = xsurface; 804 xwayland_view->view.wlr_xwayland_surface = xsurface;
717 805
718 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); 806 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
@@ -751,6 +839,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
751 wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); 839 wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role);
752 xwayland_view->set_role.notify = handle_set_role; 840 xwayland_view->set_role.notify = handle_set_role;
753 841
842 wl_signal_add(&xsurface->events.set_startup_id,
843 &xwayland_view->set_startup_id);
844 xwayland_view->set_startup_id.notify = handle_set_startup_id;
845
754 wl_signal_add(&xsurface->events.set_window_type, 846 wl_signal_add(&xsurface->events.set_window_type,
755 &xwayland_view->set_window_type); 847 &xwayland_view->set_window_type);
756 xwayland_view->set_window_type.notify = handle_set_window_type; 848 xwayland_view->set_window_type.notify = handle_set_window_type;
@@ -762,11 +854,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
762 &xwayland_view->set_decorations); 854 &xwayland_view->set_decorations);
763 xwayland_view->set_decorations.notify = handle_set_decorations; 855 xwayland_view->set_decorations.notify = handle_set_decorations;
764 856
765 wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); 857 wl_signal_add(&xsurface->events.associate, &xwayland_view->associate);
766 xwayland_view->unmap.notify = handle_unmap; 858 xwayland_view->associate.notify = handle_associate;
767 859
768 wl_signal_add(&xsurface->events.map, &xwayland_view->map); 860 wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate);
769 xwayland_view->map.notify = handle_map; 861 xwayland_view->dissociate.notify = handle_dissociate;
770 862
771 wl_signal_add(&xsurface->events.set_override_redirect, 863 wl_signal_add(&xsurface->events.set_override_redirect,
772 &xwayland_view->override_redirect); 864 &xwayland_view->override_redirect);
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 2ee63124..3d04826c 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <math.h> 2#include <math.h>
4#include <libevdev/libevdev.h> 3#include <libevdev/libevdev.h>
@@ -7,8 +6,9 @@
7#include <time.h> 6#include <time.h>
8#include <strings.h> 7#include <strings.h>
9#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
10#include <wlr/types/wlr_idle.h> 9#include <wlr/types/wlr_cursor_shape_v1.h>
11#include <wlr/types/wlr_pointer.h> 10#include <wlr/types/wlr_pointer.h>
11#include <wlr/types/wlr_relative_pointer_v1.h>
12#include <wlr/types/wlr_touch.h> 12#include <wlr/types/wlr_touch.h>
13#include <wlr/types/wlr_tablet_v2.h> 13#include <wlr/types/wlr_tablet_v2.h>
14#include <wlr/types/wlr_tablet_pad.h> 14#include <wlr/types/wlr_tablet_pad.h>
@@ -19,12 +19,12 @@
19#include "log.h" 19#include "log.h"
20#include "util.h" 20#include "util.h"
21#include "sway/commands.h" 21#include "sway/commands.h"
22#include "sway/desktop.h"
23#include "sway/input/cursor.h" 22#include "sway/input/cursor.h"
24#include "sway/input/keyboard.h" 23#include "sway/input/keyboard.h"
25#include "sway/input/tablet.h" 24#include "sway/input/tablet.h"
26#include "sway/layers.h" 25#include "sway/layers.h"
27#include "sway/output.h" 26#include "sway/output.h"
27#include "sway/scene_descriptor.h"
28#include "sway/tree/container.h" 28#include "sway/tree/container.h"
29#include "sway/tree/root.h" 29#include "sway/tree/root.h"
30#include "sway/tree/view.h" 30#include "sway/tree/view.h"
@@ -37,45 +37,6 @@ static uint32_t get_current_time_msec(void) {
37 return now.tv_sec * 1000 + now.tv_nsec / 1000000; 37 return now.tv_sec * 1000 + now.tv_nsec / 1000000;
38} 38}
39 39
40static struct wlr_surface *layer_surface_at(struct sway_output *output,
41 struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
42 struct sway_layer_surface *sway_layer;
43 wl_list_for_each_reverse(sway_layer, layer, link) {
44 double _sx = ox - sway_layer->geo.x;
45 double _sy = oy - sway_layer->geo.y;
46 struct wlr_surface *sub = wlr_layer_surface_v1_surface_at(
47 sway_layer->layer_surface, _sx, _sy, sx, sy);
48 if (sub) {
49 return sub;
50 }
51 }
52 return NULL;
53}
54
55static bool surface_is_xdg_popup(struct wlr_surface *surface) {
56 if (wlr_surface_is_xdg_surface(surface)) {
57 struct wlr_xdg_surface *xdg_surface =
58 wlr_xdg_surface_from_wlr_surface(surface);
59 return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP;
60 }
61 return false;
62}
63
64static struct wlr_surface *layer_surface_popup_at(struct sway_output *output,
65 struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
66 struct sway_layer_surface *sway_layer;
67 wl_list_for_each_reverse(sway_layer, layer, link) {
68 double _sx = ox - sway_layer->geo.x;
69 double _sy = oy - sway_layer->geo.y;
70 struct wlr_surface *sub = wlr_layer_surface_v1_surface_at(
71 sway_layer->layer_surface, _sx, _sy, sx, sy);
72 if (sub && surface_is_xdg_popup(sub)) {
73 return sub;
74 }
75 }
76 return NULL;
77}
78
79/** 40/**
80 * Returns the node at the cursor's position. If there is a surface at that 41 * Returns the node at the cursor's position. If there is a surface at that
81 * location, it is stored in **surface (it may not be a view). 42 * location, it is stored in **surface (it may not be a view).
@@ -83,116 +44,98 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output,
83struct sway_node *node_at_coords( 44struct sway_node *node_at_coords(
84 struct sway_seat *seat, double lx, double ly, 45 struct sway_seat *seat, double lx, double ly,
85 struct wlr_surface **surface, double *sx, double *sy) { 46 struct wlr_surface **surface, double *sx, double *sy) {
86 // find the output the cursor is on 47 struct wlr_scene_node *scene_node = NULL;
87 struct wlr_output *wlr_output = wlr_output_layout_output_at(
88 root->output_layout, lx, ly);
89 if (wlr_output == NULL) {
90 return NULL;
91 }
92 struct sway_output *output = wlr_output->data;
93 if (!output || !output->enabled) {
94 // output is being destroyed or is being enabled
95 return NULL;
96 }
97 double ox = lx, oy = ly;
98 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
99 48
100 // layer surfaces on the overlay layer are rendered on top 49 struct wlr_scene_node *node;
101 if ((*surface = layer_surface_at(output, 50 wl_list_for_each_reverse(node, &root->layer_tree->children, link) {
102 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 51 struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node);
103 ox, oy, sx, sy))) {
104 return NULL;
105 }
106 52
107 // check for unmanaged views 53 bool non_interactive = scene_descriptor_try_get(&layer->node,
108#if HAVE_XWAYLAND 54 SWAY_SCENE_DESC_NON_INTERACTIVE);
109 struct wl_list *unmanaged = &root->xwayland_unmanaged; 55 if (non_interactive) {
110 struct sway_xwayland_unmanaged *unmanaged_surface; 56 continue;
111 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
112 struct wlr_xwayland_surface *xsurface =
113 unmanaged_surface->wlr_xwayland_surface;
114
115 double _sx = lx - unmanaged_surface->lx;
116 double _sy = ly - unmanaged_surface->ly;
117 if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) {
118 *surface = xsurface->surface;
119 *sx = _sx;
120 *sy = _sy;
121 return NULL;
122 } 57 }
123 }
124#endif
125 58
126 if (root->fullscreen_global) { 59 scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy);
127 // Try fullscreen container 60 if (scene_node) {
128 struct sway_container *con = tiling_container_at( 61 break;
129 &root->fullscreen_global->node, lx, ly, surface, sx, sy);
130 if (con) {
131 return &con->node;
132 } 62 }
133 return NULL;
134 } 63 }
135 64
136 // find the focused workspace on the output for this seat 65 if (scene_node) {
137 struct sway_workspace *ws = output_get_active_workspace(output); 66 // determine what wlr_surface we clicked on
138 if (!ws) { 67 if (scene_node->type == WLR_SCENE_NODE_BUFFER) {
139 return NULL; 68 struct wlr_scene_buffer *scene_buffer =
140 } 69 wlr_scene_buffer_from_node(scene_node);
70 struct wlr_scene_surface *scene_surface =
71 wlr_scene_surface_try_from_buffer(scene_buffer);
141 72
142 if (ws->fullscreen) { 73 if (scene_surface) {
143 // Try transient containers 74 *surface = scene_surface->surface;
144 for (int i = 0; i < ws->floating->length; ++i) {
145 struct sway_container *floater = ws->floating->items[i];
146 if (container_is_transient_for(floater, ws->fullscreen)) {
147 struct sway_container *con = tiling_container_at(
148 &floater->node, lx, ly, surface, sx, sy);
149 if (con) {
150 return &con->node;
151 }
152 } 75 }
153 } 76 }
154 // Try fullscreen container 77
155 struct sway_container *con = 78 // determine what container we clicked on
156 tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); 79 struct wlr_scene_node *current = scene_node;
157 if (con) { 80 while (true) {
158 return &con->node; 81 struct sway_container *con = scene_descriptor_try_get(current,
82 SWAY_SCENE_DESC_CONTAINER);
83
84 if (!con) {
85 struct sway_view *view = scene_descriptor_try_get(current,
86 SWAY_SCENE_DESC_VIEW);
87 if (view) {
88 con = view->container;
89 }
90 }
91
92 if (!con) {
93 struct sway_popup_desc *popup =
94 scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP);
95 if (popup && popup->view) {
96 con = popup->view->container;
97 }
98 }
99
100 if (con && (!con->view || con->view->surface)) {
101 return &con->node;
102 }
103
104 if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) {
105 // We don't want to feed through the current workspace on
106 // layer shells
107 return NULL;
108 }
109
110#if HAVE_XWAYLAND
111 if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) {
112 return NULL;
113 }
114#endif
115
116 if (!current->parent) {
117 break;
118 }
119
120 current = &current->parent->node;
159 } 121 }
160 return NULL;
161 }
162 if ((*surface = layer_surface_popup_at(output,
163 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
164 ox, oy, sx, sy))) {
165 return NULL;
166 }
167 if ((*surface = layer_surface_popup_at(output,
168 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
169 ox, oy, sx, sy))) {
170 return NULL;
171 }
172 if ((*surface = layer_surface_popup_at(output,
173 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
174 ox, oy, sx, sy))) {
175 return NULL;
176 }
177 if ((*surface = layer_surface_at(output,
178 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
179 ox, oy, sx, sy))) {
180 return NULL;
181 } 122 }
182 123
183 struct sway_container *c; 124 // if we aren't on a container, determine what workspace we are on
184 if ((c = container_at(ws, lx, ly, surface, sx, sy))) { 125 struct wlr_output *wlr_output = wlr_output_layout_output_at(
185 return &c->node; 126 root->output_layout, lx, ly);
127 if (wlr_output == NULL) {
128 return NULL;
186 } 129 }
187 130
188 if ((*surface = layer_surface_at(output, 131 struct sway_output *output = wlr_output->data;
189 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], 132 if (!output || !output->enabled) {
190 ox, oy, sx, sy))) { 133 // output is being destroyed or is being enabled
191 return NULL; 134 return NULL;
192 } 135 }
193 if ((*surface = layer_surface_at(output, 136
194 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], 137 struct sway_workspace *ws = output_get_active_workspace(output);
195 ox, oy, sx, sy))) { 138 if (!ws) {
196 return NULL; 139 return NULL;
197 } 140 }
198 141
@@ -221,7 +164,7 @@ void cursor_update_image(struct sway_cursor *cursor,
221 // Try a node's resize edge 164 // Try a node's resize edge
222 enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor); 165 enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor);
223 if (edge == WLR_EDGE_NONE) { 166 if (edge == WLR_EDGE_NONE) {
224 cursor_set_image(cursor, "left_ptr", NULL); 167 cursor_set_image(cursor, "default", NULL);
225 } else if (container_is_floating(node->sway_container)) { 168 } else if (container_is_floating(node->sway_container)) {
226 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); 169 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL);
227 } else { 170 } else {
@@ -232,12 +175,12 @@ void cursor_update_image(struct sway_cursor *cursor,
232 } 175 }
233 } 176 }
234 } else { 177 } else {
235 cursor_set_image(cursor, "left_ptr", NULL); 178 cursor_set_image(cursor, "default", NULL);
236 } 179 }
237} 180}
238 181
239static void cursor_hide(struct sway_cursor *cursor) { 182static void cursor_hide(struct sway_cursor *cursor) {
240 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); 183 wlr_cursor_unset_image(cursor->cursor);
241 cursor->hidden = true; 184 cursor->hidden = true;
242 wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); 185 wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat);
243} 186}
@@ -300,7 +243,7 @@ static enum sway_input_idle_source idle_source_from_device(
300 return IDLE_SOURCE_POINTER; 243 return IDLE_SOURCE_POINTER;
301 case WLR_INPUT_DEVICE_TOUCH: 244 case WLR_INPUT_DEVICE_TOUCH:
302 return IDLE_SOURCE_TOUCH; 245 return IDLE_SOURCE_TOUCH;
303 case WLR_INPUT_DEVICE_TABLET_TOOL: 246 case WLR_INPUT_DEVICE_TABLET:
304 return IDLE_SOURCE_TABLET_TOOL; 247 return IDLE_SOURCE_TABLET_TOOL;
305 case WLR_INPUT_DEVICE_TABLET_PAD: 248 case WLR_INPUT_DEVICE_TABLET_PAD:
306 return IDLE_SOURCE_TABLET_PAD; 249 return IDLE_SOURCE_TABLET_PAD;
@@ -349,7 +292,7 @@ void cursor_unhide(struct sway_cursor *cursor) {
349 wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); 292 wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor));
350} 293}
351 294
352static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, 295void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
353 struct wlr_input_device *device, double dx, double dy, 296 struct wlr_input_device *device, double dx, double dy,
354 double dx_unaccel, double dy_unaccel) { 297 double dx_unaccel, double dy_unaccel) {
355 wlr_relative_pointer_manager_v1_send_relative_motion( 298 wlr_relative_pointer_manager_v1_send_relative_motion(
@@ -413,7 +356,7 @@ static void handle_pointer_motion_absolute(
413 356
414void dispatch_cursor_button(struct sway_cursor *cursor, 357void dispatch_cursor_button(struct sway_cursor *cursor,
415 struct wlr_input_device *device, uint32_t time_msec, uint32_t button, 358 struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
416 enum wlr_button_state state) { 359 enum wl_pointer_button_state state) {
417 if (time_msec == 0) { 360 if (time_msec == 0) {
418 time_msec = get_current_time_msec(); 361 time_msec = get_current_time_msec();
419 } 362 }
@@ -425,7 +368,7 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
425 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 368 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
426 struct wlr_pointer_button_event *event = data; 369 struct wlr_pointer_button_event *event = data;
427 370
428 if (event->state == WLR_BUTTON_PRESSED) { 371 if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) {
429 cursor->pressed_button_count++; 372 cursor->pressed_button_count++;
430 } else { 373 } else {
431 if (cursor->pressed_button_count > 0) { 374 if (cursor->pressed_button_count > 0) {
@@ -464,43 +407,16 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
464 cursor_hide(cursor); 407 cursor_hide(cursor);
465 408
466 struct sway_seat *seat = cursor->seat; 409 struct sway_seat *seat = cursor->seat;
467 struct wlr_seat *wlr_seat = seat->wlr_seat;
468 struct wlr_surface *surface = NULL;
469 410
470 double lx, ly; 411 double lx, ly;
471 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, 412 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
472 event->x, event->y, &lx, &ly); 413 event->x, event->y, &lx, &ly);
473 double sx, sy;
474 struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy);
475 414
476 seat->touch_id = event->touch_id; 415 seat->touch_id = event->touch_id;
477 seat->touch_x = lx; 416 seat->touch_x = lx;
478 seat->touch_y = ly; 417 seat->touch_y = ly;
479 418
480 if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { 419 seatop_touch_down(seat, event, lx, ly);
481 if (seat_is_input_allowed(seat, surface)) {
482 wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec,
483 event->touch_id, sx, sy);
484
485 if (focused_node) {
486 seat_set_focus(seat, focused_node);
487 }
488 }
489 } else if (!cursor->simulating_pointer_from_touch &&
490 (!surface || seat_is_input_allowed(seat, surface))) {
491 // Fallback to cursor simulation.
492 // The pointer_touch_id state is needed, so drags are not aborted when over
493 // a surface supporting touch and multi touch events don't interfere.
494 cursor->simulating_pointer_from_touch = true;
495 cursor->pointer_touch_id = seat->touch_id;
496 double dx, dy;
497 dx = lx - cursor->cursor->x;
498 dy = ly - cursor->cursor->y;
499 pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy,
500 dx, dy);
501 dispatch_cursor_button(cursor, &event->touch->base, event->time_msec,
502 BTN_LEFT, WLR_BUTTON_PRESSED);
503 }
504} 420}
505 421
506static void handle_touch_up(struct wl_listener *listener, void *data) { 422static void handle_touch_up(struct wl_listener *listener, void *data) {
@@ -508,16 +424,34 @@ static void handle_touch_up(struct wl_listener *listener, void *data) {
508 struct wlr_touch_up_event *event = data; 424 struct wlr_touch_up_event *event = data;
509 cursor_handle_activity_from_device(cursor, &event->touch->base); 425 cursor_handle_activity_from_device(cursor, &event->touch->base);
510 426
511 struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; 427 struct sway_seat *seat = cursor->seat;
512 428
513 if (cursor->simulating_pointer_from_touch) { 429 if (cursor->simulating_pointer_from_touch) {
514 if (cursor->pointer_touch_id == cursor->seat->touch_id) { 430 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
515 cursor->pointer_touch_up = true; 431 cursor->pointer_touch_up = true;
516 dispatch_cursor_button(cursor, &event->touch->base, 432 dispatch_cursor_button(cursor, &event->touch->base,
517 event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); 433 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
518 } 434 }
519 } else { 435 } else {
520 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); 436 seatop_touch_up(seat, event);
437 }
438}
439
440static void handle_touch_cancel(struct wl_listener *listener, void *data) {
441 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel);
442 struct wlr_touch_cancel_event *event = data;
443 cursor_handle_activity_from_device(cursor, &event->touch->base);
444
445 struct sway_seat *seat = cursor->seat;
446
447 if (cursor->simulating_pointer_from_touch) {
448 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
449 cursor->pointer_touch_up = true;
450 dispatch_cursor_button(cursor, &event->touch->base,
451 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
452 }
453 } else {
454 seatop_touch_cancel(seat, event);
521 } 455 }
522} 456}
523 457
@@ -528,25 +462,16 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
528 cursor_handle_activity_from_device(cursor, &event->touch->base); 462 cursor_handle_activity_from_device(cursor, &event->touch->base);
529 463
530 struct sway_seat *seat = cursor->seat; 464 struct sway_seat *seat = cursor->seat;
531 struct wlr_seat *wlr_seat = seat->wlr_seat;
532 struct wlr_surface *surface = NULL;
533 465
534 double lx, ly; 466 double lx, ly;
535 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, 467 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
536 event->x, event->y, &lx, &ly); 468 event->x, event->y, &lx, &ly);
537 double sx, sy;
538 node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy);
539 469
540 if (seat->touch_id == event->touch_id) { 470 if (seat->touch_id == event->touch_id) {
541 seat->touch_x = lx; 471 seat->touch_x = lx;
542 seat->touch_y = ly; 472 seat->touch_y = ly;
543 473
544 struct sway_drag_icon *drag_icon; 474 drag_icons_update_position(seat);
545 wl_list_for_each(drag_icon, &root->drag_icons, link) {
546 if (drag_icon->seat == seat) {
547 drag_icon_update_position(drag_icon);
548 }
549 }
550 } 475 }
551 476
552 if (cursor->simulating_pointer_from_touch) { 477 if (cursor->simulating_pointer_from_touch) {
@@ -557,9 +482,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
557 pointer_motion(cursor, event->time_msec, &event->touch->base, 482 pointer_motion(cursor, event->time_msec, &event->touch->base,
558 dx, dy, dx, dy); 483 dx, dy, dx, dy);
559 } 484 }
560 } else if (surface) { 485 } else {
561 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, 486 seatop_touch_motion(seat, event, lx, ly);
562 event->touch_id, sx, sy);
563 } 487 }
564} 488}
565 489
@@ -594,7 +518,7 @@ static void apply_mapping_from_region(struct wlr_input_device *device,
594 double x1 = region->x1, x2 = region->x2; 518 double x1 = region->x1, x2 = region->x2;
595 double y1 = region->y1, y2 = region->y2; 519 double y1 = region->y1, y2 = region->y2;
596 520
597 if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { 521 if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) {
598 struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); 522 struct wlr_tablet *tablet = wlr_tablet_from_input_device(device);
599 if (tablet->width_mm == 0 || tablet->height_mm == 0) { 523 if (tablet->width_mm == 0 || tablet->height_mm == 0) {
600 return; 524 return;
@@ -737,7 +661,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
737 event->state == WLR_TABLET_TOOL_TIP_UP) { 661 event->state == WLR_TABLET_TOOL_TIP_UP) {
738 cursor->simulating_pointer_from_tool_tip = false; 662 cursor->simulating_pointer_from_tool_tip = false;
739 dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, 663 dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec,
740 BTN_LEFT, WLR_BUTTON_RELEASED); 664 BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
741 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 665 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
742 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { 666 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) {
743 // If we started holding the tool tip down on a surface that accepts 667 // If we started holding the tool tip down on a surface that accepts
@@ -749,7 +673,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
749 } else { 673 } else {
750 cursor->simulating_pointer_from_tool_tip = true; 674 cursor->simulating_pointer_from_tool_tip = true;
751 dispatch_cursor_button(cursor, &event->tablet->base, 675 dispatch_cursor_button(cursor, &event->tablet->base,
752 event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED); 676 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
753 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 677 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
754 } 678 }
755 } else { 679 } else {
@@ -818,31 +742,71 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
818 node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, 742 node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y,
819 &surface, &sx, &sy); 743 &surface, &sx, &sy);
820 744
821 if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { 745 // TODO: floating resize should support graphics tablet events
746 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat);
747 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
748 bool mod_pressed = modifiers & config->floating_mod;
749
750 bool surface_supports_tablet_events =
751 surface && wlr_surface_accepts_tablet_v2(tablet_v2, surface);
752
753 // Simulate pointer when:
754 // 1. The modifier key is pressed, OR
755 // 2. The surface under the cursor does not support tablet events.
756 bool should_simulate_pointer = mod_pressed || !surface_supports_tablet_events;
757
758 // Similar to tool tip, we need to selectively simulate mouse events, but we
759 // want to make sure that it is always consistent. Because all tool buttons
760 // currently map to BTN_RIGHT, we need to keep count of how many tool
761 // buttons are currently pressed down so we can send consistent events.
762 //
763 // The logic follows:
764 // - If we are already simulating the pointer, we should continue to do so
765 // until at least no tool button is held down.
766 // - If we should simulate the pointer and no tool button is currently held
767 // down, begin simulating the pointer.
768 // - If neither of the above are true, send the tablet events.
769 if ((cursor->tool_buttons > 0 && cursor->simulating_pointer_from_tool_button)
770 || (cursor->tool_buttons == 0 && should_simulate_pointer)) {
771 cursor->simulating_pointer_from_tool_button = true;
772
822 // TODO: the user may want to configure which tool buttons are mapped to 773 // TODO: the user may want to configure which tool buttons are mapped to
823 // which simulated pointer buttons 774 // which simulated pointer buttons
824 switch (event->state) { 775 switch (event->state) {
825 case WLR_BUTTON_PRESSED: 776 case WLR_BUTTON_PRESSED:
826 if (cursor->tool_buttons == 0) { 777 if (cursor->tool_buttons == 0) {
827 dispatch_cursor_button(cursor, &event->tablet->base, 778 dispatch_cursor_button(cursor, &event->tablet->base,
828 event->time_msec, BTN_RIGHT, event->state); 779 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED);
829 } 780 }
830 cursor->tool_buttons++;
831 break; 781 break;
832 case WLR_BUTTON_RELEASED: 782 case WLR_BUTTON_RELEASED:
833 if (cursor->tool_buttons == 1) { 783 if (cursor->tool_buttons <= 1) {
834 dispatch_cursor_button(cursor, &event->tablet->base, 784 dispatch_cursor_button(cursor, &event->tablet->base,
835 event->time_msec, BTN_RIGHT, event->state); 785 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED);
836 } 786 }
837 cursor->tool_buttons--;
838 break; 787 break;
839 } 788 }
840 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 789 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
841 return; 790 } else {
791 cursor->simulating_pointer_from_tool_button = false;
792
793 wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool,
794 event->button, (enum zwp_tablet_pad_v2_button_state)event->state);
842 } 795 }
843 796
844 wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, 797 // Update tool button count.
845 event->button, (enum zwp_tablet_pad_v2_button_state)event->state); 798 switch (event->state) {
799 case WLR_BUTTON_PRESSED:
800 cursor->tool_buttons++;
801 break;
802 case WLR_BUTTON_RELEASED:
803 if (cursor->tool_buttons == 0) {
804 sway_log(SWAY_ERROR, "inconsistent tablet tool button events");
805 } else {
806 cursor->tool_buttons--;
807 }
808 break;
809 }
846} 810}
847 811
848static void check_constraint_region(struct sway_cursor *cursor) { 812static void check_constraint_region(struct sway_cursor *cursor) {
@@ -1028,10 +992,9 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image,
1028 } 992 }
1029 993
1030 if (!image) { 994 if (!image) {
1031 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); 995 wlr_cursor_unset_image(cursor->cursor);
1032 } else if (!current_image || strcmp(current_image, image) != 0) { 996 } else if (!current_image || strcmp(current_image, image) != 0) {
1033 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, 997 wlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image);
1034 cursor->cursor);
1035 } 998 }
1036} 999}
1037 1000
@@ -1078,6 +1041,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1078 wl_list_remove(&cursor->frame.link); 1041 wl_list_remove(&cursor->frame.link);
1079 wl_list_remove(&cursor->touch_down.link); 1042 wl_list_remove(&cursor->touch_down.link);
1080 wl_list_remove(&cursor->touch_up.link); 1043 wl_list_remove(&cursor->touch_up.link);
1044 wl_list_remove(&cursor->touch_cancel.link);
1081 wl_list_remove(&cursor->touch_motion.link); 1045 wl_list_remove(&cursor->touch_motion.link);
1082 wl_list_remove(&cursor->touch_frame.link); 1046 wl_list_remove(&cursor->touch_frame.link);
1083 wl_list_remove(&cursor->tool_axis.link); 1047 wl_list_remove(&cursor->tool_axis.link);
@@ -1114,9 +1078,6 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1114 wl_list_init(&cursor->image_surface_destroy.link); 1078 wl_list_init(&cursor->image_surface_destroy.link);
1115 cursor->image_surface_destroy.notify = handle_image_surface_destroy; 1079 cursor->image_surface_destroy.notify = handle_image_surface_destroy;
1116 1080
1117 // gesture events
1118 cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display);
1119
1120 wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin); 1081 wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
1121 cursor->hold_begin.notify = handle_pointer_hold_begin; 1082 cursor->hold_begin.notify = handle_pointer_hold_begin;
1122 wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end); 1083 wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
@@ -1159,6 +1120,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1159 wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); 1120 wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up);
1160 cursor->touch_up.notify = handle_touch_up; 1121 cursor->touch_up.notify = handle_touch_up;
1161 1122
1123 wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel);
1124 cursor->touch_cancel.notify = handle_touch_cancel;
1125
1162 wl_signal_add(&wlr_cursor->events.touch_motion, 1126 wl_signal_add(&wlr_cursor->events.touch_motion,
1163 &cursor->touch_motion); 1127 &cursor->touch_motion);
1164 cursor->touch_motion.notify = handle_touch_motion; 1128 cursor->touch_motion.notify = handle_touch_motion;
@@ -1251,11 +1215,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) {
1251 // Get event code from name 1215 // Get event code from name
1252 int code = libevdev_event_code_from_name(EV_KEY, name); 1216 int code = libevdev_event_code_from_name(EV_KEY, name);
1253 if (code == -1) { 1217 if (code == -1) {
1254 size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; 1218 *error = format_str("Unknown event %s", name);
1255 *error = malloc(len);
1256 if (*error) {
1257 snprintf(*error, len, "Unknown event %s", name);
1258 }
1259 return 0; 1219 return 0;
1260 } 1220 }
1261 return code; 1221 return code;
@@ -1277,13 +1237,8 @@ uint32_t get_mouse_bindcode(const char *name, char **error) {
1277 } 1237 }
1278 const char *event = libevdev_event_code_get_name(EV_KEY, code); 1238 const char *event = libevdev_event_code_get_name(EV_KEY, code);
1279 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { 1239 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) {
1280 size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", 1240 *error = format_str("Event code %d (%s) is not a button",
1281 code, event ? event : "(null)") + 1; 1241 code, event ? event : "(null)");
1282 *error = malloc(len);
1283 if (*error) {
1284 snprintf(*error, len, "Event code %d (%s) is not a button",
1285 code, event ? event : "(null)");
1286 }
1287 return 0; 1242 return 0;
1288 } 1243 }
1289 return code; 1244 return code;
@@ -1316,12 +1271,15 @@ const char *get_mouse_button_name(uint32_t button) {
1316static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { 1271static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) {
1317 struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; 1272 struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint;
1318 1273
1319 if (constraint->current.committed & 1274 if (constraint->current.cursor_hint.enabled) {
1320 WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
1321 double sx = constraint->current.cursor_hint.x; 1275 double sx = constraint->current.cursor_hint.x;
1322 double sy = constraint->current.cursor_hint.y; 1276 double sy = constraint->current.cursor_hint.y;
1323 1277
1324 struct sway_view *view = view_from_wlr_surface(constraint->surface); 1278 struct sway_view *view = view_from_wlr_surface(constraint->surface);
1279 if (!view) {
1280 return;
1281 }
1282
1325 struct sway_container *con = view->container; 1283 struct sway_container *con = view->container;
1326 1284
1327 double lx = sx + con->pending.content_x - view->geometry.x; 1285 double lx = sx + con->pending.content_x - view->geometry.x;
@@ -1372,12 +1330,9 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) {
1372 sway_constraint->destroy.notify = handle_constraint_destroy; 1330 sway_constraint->destroy.notify = handle_constraint_destroy;
1373 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); 1331 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy);
1374 1332
1375 struct sway_node *focus = seat_get_focus(seat); 1333 struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;
1376 if (focus && node_is_view(focus)) { 1334 if (surface && surface == constraint->surface) {
1377 struct wlr_surface *surface = focus->sway_container->view->surface; 1335 sway_cursor_constrain(seat->cursor, constraint);
1378 if (surface == constraint->surface) {
1379 sway_cursor_constrain(seat->cursor, constraint);
1380 }
1381 } 1336 }
1382} 1337}
1383 1338
@@ -1435,3 +1390,26 @@ void sway_cursor_constrain(struct sway_cursor *cursor,
1435 wl_signal_add(&constraint->surface->events.commit, 1390 wl_signal_add(&constraint->surface->events.commit,
1436 &cursor->constraint_commit); 1391 &cursor->constraint_commit);
1437} 1392}
1393
1394void handle_request_set_cursor_shape(struct wl_listener *listener, void *data) {
1395 const struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;
1396 struct sway_seat *seat = event->seat_client->seat->data;
1397
1398 if (!seatop_allows_set_cursor(seat)) {
1399 return;
1400 }
1401
1402 struct wl_client *focused_client = NULL;
1403 struct wlr_surface *focused_surface = seat->wlr_seat->pointer_state.focused_surface;
1404 if (focused_surface != NULL) {
1405 focused_client = wl_resource_get_client(focused_surface->resource);
1406 }
1407
1408 // TODO: check cursor mode
1409 if (focused_client == NULL || event->seat_client->client != focused_client) {
1410 sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client");
1411 return;
1412 }
1413
1414 cursor_set_image(seat->cursor, wlr_cursor_shape_v1_name(event->shape), focused_client);
1415}
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 4a0bce0e..248ca34e 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -1,12 +1,12 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <string.h> 3#include <string.h>
5#include <math.h> 4#include <math.h>
5#include <assert.h>
6#include <wlr/config.h>
6#include <wlr/backend/libinput.h> 7#include <wlr/backend/libinput.h>
7#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
8#include <wlr/types/wlr_keyboard_group.h> 9#include <wlr/types/wlr_keyboard_group.h>
9#include <wlr/types/wlr_input_inhibitor.h>
10#include <wlr/types/wlr_virtual_keyboard_v1.h> 10#include <wlr/types/wlr_virtual_keyboard_v1.h>
11#include <wlr/types/wlr_virtual_pointer_v1.h> 11#include <wlr/types/wlr_virtual_pointer_v1.h>
12#include "sway/config.h" 12#include "sway/config.h"
@@ -22,6 +22,10 @@
22#include "list.h" 22#include "list.h"
23#include "log.h" 23#include "log.h"
24 24
25#if WLR_HAS_LIBINPUT_BACKEND
26#include <wlr/backend/libinput.h>
27#endif
28
25#define DEFAULT_SEAT "seat0" 29#define DEFAULT_SEAT "seat0"
26 30
27struct input_config *current_input_config = NULL; 31struct 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
65char *input_device_get_identifier(struct wlr_input_device *device) { 69char *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
92static bool device_is_touchpad(struct sway_input_device *device) { 95static 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
104const char *input_device_get_type(struct sway_input_device *device) { 111const char *input_device_get_type(struct sway_input_device *device) {
@@ -113,7 +120,7 @@ const char *input_device_get_type(struct sway_input_device *device) {
113 return "keyboard"; 120 return "keyboard";
114 case WLR_INPUT_DEVICE_TOUCH: 121 case WLR_INPUT_DEVICE_TOUCH:
115 return "touch"; 122 return "touch";
116 case WLR_INPUT_DEVICE_TABLET_TOOL: 123 case WLR_INPUT_DEVICE_TABLET:
117 return "tablet_tool"; 124 return "tablet_tool";
118 case WLR_INPUT_DEVICE_TABLET_PAD: 125 case WLR_INPUT_DEVICE_TABLET_PAD:
119 return "tablet_pad"; 126 return "tablet_pad";
@@ -236,7 +243,11 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
236 243
237 apply_input_type_config(input_device); 244 apply_input_type_config(input_device);
238 245
239 sway_input_configure_libinput_device(input_device); 246#if WLR_HAS_LIBINPUT_BACKEND
247 bool config_changed = sway_input_configure_libinput_device(input_device);
248#else
249 bool config_changed = false;
250#endif
240 251
241 wl_signal_add(&device->events.destroy, &input_device->device_destroy); 252 wl_signal_add(&device->events.destroy, &input_device->device_destroy);
242 input_device->device_destroy.notify = handle_device_destroy; 253 input_device->device_destroy.notify = handle_device_destroy;
@@ -274,33 +285,9 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
274 } 285 }
275 286
276 ipc_event_input("added", input_device); 287 ipc_event_input("added", input_device);
277}
278
279static void handle_inhibit_activate(struct wl_listener *listener, void *data) {
280 struct sway_input_manager *input_manager = wl_container_of(
281 listener, input_manager, inhibit_activate);
282 struct sway_seat *seat;
283 wl_list_for_each(seat, &input_manager->seats, link) {
284 seat_set_exclusive_client(seat, input_manager->inhibit->active_client);
285 }
286}
287 288
288static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) { 289 if (config_changed) {
289 struct sway_input_manager *input_manager = wl_container_of( 290 ipc_event_input("libinput_config", input_device);
290 listener, input_manager, inhibit_deactivate);
291 struct sway_seat *seat;
292 if (server.session_lock.locked) {
293 // Don't deactivate the grab of a screenlocker
294 return;
295 }
296 wl_list_for_each(seat, &input_manager->seats, link) {
297 seat_set_exclusive_client(seat, NULL);
298 struct sway_node *previous = seat_get_focus(seat);
299 if (previous) {
300 // Hack to get seat to re-focus the return value of get_focus
301 seat_set_focus(seat, NULL);
302 seat_set_focus(seat, previous);
303 }
304 } 291 }
305} 292}
306 293
@@ -446,6 +433,20 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) {
446 } 433 }
447} 434}
448 435
436static 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
449struct sway_input_manager *input_manager_create(struct sway_server *server) { 450struct sway_input_manager *input_manager_create(struct sway_server *server) {
450 struct sway_input_manager *input = 451 struct sway_input_manager *input =
451 calloc(1, sizeof(struct sway_input_manager)); 452 calloc(1, sizeof(struct sway_input_manager));
@@ -472,14 +473,6 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) {
472 &input->virtual_pointer_new); 473 &input->virtual_pointer_new);
473 input->virtual_pointer_new.notify = handle_virtual_pointer; 474 input->virtual_pointer_new.notify = handle_virtual_pointer;
474 475
475 input->inhibit = wlr_input_inhibit_manager_create(server->wl_display);
476 input->inhibit_activate.notify = handle_inhibit_activate;
477 wl_signal_add(&input->inhibit->events.activate,
478 &input->inhibit_activate);
479 input->inhibit_deactivate.notify = handle_inhibit_deactivate;
480 wl_signal_add(&input->inhibit->events.deactivate,
481 &input->inhibit_deactivate);
482
483 input->keyboard_shortcuts_inhibit = 476 input->keyboard_shortcuts_inhibit =
484 wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); 477 wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display);
485 input->keyboard_shortcuts_inhibit_new_inhibitor.notify = 478 input->keyboard_shortcuts_inhibit_new_inhibitor.notify =
@@ -487,6 +480,17 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) {
487 wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, 480 wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor,
488 &input->keyboard_shortcuts_inhibit_new_inhibitor); 481 &input->keyboard_shortcuts_inhibit_new_inhibitor);
489 482
483 input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display);
484
485 input->transient_seat_manager =
486 wlr_transient_seat_manager_v1_create(server->wl_display);
487 assert(input->transient_seat_manager);
488
489 input->transient_seat_create.notify =
490 handle_transient_seat_manager_create_seat;
491 wl_signal_add(&input->transient_seat_manager->events.create_seat,
492 &input->transient_seat_create);
493
490 return input; 494 return input;
491} 495}
492 496
@@ -524,21 +528,50 @@ static void retranslate_keysyms(struct input_config *input_config) {
524 return; 528 return;
525 } 529 }
526 } 530 }
531
532 for (int i = 0; i < config->input_type_configs->length; ++i) {
533 struct input_config *ic = config->input_type_configs->items[i];
534 if (ic->xkb_layout || ic->xkb_file) {
535 // this is the first config with xkb_layout or xkb_file
536 if (ic->identifier == input_config->identifier) {
537 translate_keysyms(ic);
538 }
539
540 return;
541 }
542 }
527} 543}
528 544
529static void input_manager_configure_input( 545static void input_manager_configure_input(
530 struct sway_input_device *input_device) { 546 struct sway_input_device *input_device) {
531 sway_input_configure_libinput_device(input_device); 547#if WLR_HAS_LIBINPUT_BACKEND
548 bool config_changed = sway_input_configure_libinput_device(input_device);
549#else
550 bool config_changed = false;
551#endif
532 struct sway_seat *seat = NULL; 552 struct sway_seat *seat = NULL;
533 wl_list_for_each(seat, &server.input->seats, link) { 553 wl_list_for_each(seat, &server.input->seats, link) {
534 seat_configure_device(seat, input_device); 554 seat_configure_device(seat, input_device);
535 } 555 }
556 if (config_changed) {
557 ipc_event_input("libinput_config", input_device);
558 }
536} 559}
537 560
538void input_manager_configure_all_inputs(void) { 561void input_manager_configure_all_input_mappings(void) {
539 struct sway_input_device *input_device = NULL; 562 struct sway_input_device *input_device;
540 wl_list_for_each(input_device, &server.input->devices, link) { 563 wl_list_for_each(input_device, &server.input->devices, link) {
541 input_manager_configure_input(input_device); 564 struct sway_seat *seat;
565 wl_list_for_each(seat, &server.input->seats, link) {
566 seat_configure_device_mapping(seat, input_device);
567 }
568
569#if WLR_HAS_LIBINPUT_BACKEND
570 // Input devices mapped to unavailable outputs get their libinput
571 // send_events setting switched to false. We need to re-enable this
572 // when the output appears.
573 sway_input_configure_libinput_device_send_events(input_device);
574#endif
542 } 575 }
543} 576}
544 577
@@ -560,7 +593,9 @@ void input_manager_apply_input_config(struct input_config *input_config) {
560} 593}
561 594
562void input_manager_reset_input(struct sway_input_device *input_device) { 595void input_manager_reset_input(struct sway_input_device *input_device) {
596#if WLR_HAS_LIBINPUT_BACKEND
563 sway_input_reset_libinput_device(input_device); 597 sway_input_reset_libinput_device(input_device);
598#endif
564 struct sway_seat *seat = NULL; 599 struct sway_seat *seat = NULL;
565 wl_list_for_each(seat, &server.input->seats, link) { 600 wl_list_for_each(seat, &server.input->seats, link) {
566 seat_reset_device(seat, input_device); 601 seat_reset_device(seat, input_device);
@@ -568,6 +603,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) {
568} 603}
569 604
570void input_manager_reset_all_inputs(void) { 605void input_manager_reset_all_inputs(void) {
606 // Set the active keyboard to NULL to avoid spamming configuration updates
607 // for all keyboard devices.
608 struct sway_seat *seat;
609 wl_list_for_each(seat, &server.input->seats, link) {
610 wlr_seat_set_keyboard(seat->wlr_seat, NULL);
611 }
612
571 struct sway_input_device *input_device = NULL; 613 struct sway_input_device *input_device = NULL;
572 wl_list_for_each(input_device, &server.input->devices, link) { 614 wl_list_for_each(input_device, &server.input->devices, link) {
573 input_manager_reset_input(input_device); 615 input_manager_reset_input(input_device);
@@ -576,7 +618,6 @@ void input_manager_reset_all_inputs(void) {
576 // If there is at least one keyboard using the default keymap, repeat delay, 618 // If there is at least one keyboard using the default keymap, repeat delay,
577 // and repeat rate, then it is possible that there is a keyboard group that 619 // and repeat rate, then it is possible that there is a keyboard group that
578 // need their keyboard disarmed. 620 // need their keyboard disarmed.
579 struct sway_seat *seat;
580 wl_list_for_each(seat, &server.input->seats, link) { 621 wl_list_for_each(seat, &server.input->seats, link) {
581 struct sway_keyboard_group *group; 622 struct sway_keyboard_group *group;
582 wl_list_for_each(group, &seat->keyboard_groups, link) { 623 wl_list_for_each(group, &seat->keyboard_groups, link) {
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 5e5692f1..f74d0658 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -1,10 +1,9 @@
1#include <assert.h> 1#include <assert.h>
2#include <limits.h> 2#include <limits.h>
3#include <strings.h> 3#include <strings.h>
4#include <wlr/config.h>
4#include <wlr/backend/multi.h> 5#include <wlr/backend/multi.h>
5#include <wlr/backend/session.h>
6#include <wlr/interfaces/wlr_keyboard.h> 6#include <wlr/interfaces/wlr_keyboard.h>
7#include <wlr/types/wlr_idle.h>
8#include <wlr/types/wlr_keyboard.h> 7#include <wlr/types/wlr_keyboard.h>
9#include <wlr/types/wlr_keyboard_group.h> 8#include <wlr/types/wlr_keyboard_group.h>
10#include <xkbcommon/xkbcommon-names.h> 9#include <xkbcommon/xkbcommon-names.h>
@@ -16,6 +15,10 @@
16#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
17#include "log.h" 16#include "log.h"
18 17
18#if WLR_HAS_SESSION
19#include <wlr/backend/session.h>
20#endif
21
19static struct modifier_key { 22static struct modifier_key {
20 char *name; 23 char *name;
21 uint32_t mod; 24 uint32_t mod;
@@ -29,6 +32,7 @@ static struct modifier_key {
29 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, 32 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 },
30 { "Mod3", WLR_MODIFIER_MOD3 }, 33 { "Mod3", WLR_MODIFIER_MOD3 },
31 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, 34 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO },
35 { "Super", WLR_MODIFIER_LOGO },
32 { "Mod5", WLR_MODIFIER_MOD5 }, 36 { "Mod5", WLR_MODIFIER_MOD5 },
33}; 37};
34 38
@@ -264,14 +268,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
264 xkb_keysym_t keysym = pressed_keysyms[i]; 268 xkb_keysym_t keysym = pressed_keysyms[i];
265 if (keysym >= XKB_KEY_XF86Switch_VT_1 && 269 if (keysym >= XKB_KEY_XF86Switch_VT_1 &&
266 keysym <= XKB_KEY_XF86Switch_VT_12) { 270 keysym <= XKB_KEY_XF86Switch_VT_12) {
267 if (wlr_backend_is_multi(server.backend)) { 271#if WLR_HAS_SESSION
268 struct wlr_session *session = 272 if (server.session) {
269 wlr_backend_get_session(server.backend); 273 unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
270 if (session) { 274 wlr_session_change_vt(server.session, vt);
271 unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
272 wlr_session_change_vt(session, vt);
273 }
274 } 275 }
276#endif
275 return true; 277 return true;
276 } 278 }
277 } 279 }
@@ -404,8 +406,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
404 char *device_identifier = input_device_get_identifier(wlr_device); 406 char *device_identifier = input_device_get_identifier(wlr_device);
405 bool exact_identifier = keyboard->wlr->group != NULL; 407 bool exact_identifier = keyboard->wlr->group != NULL;
406 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); 408 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD);
407 bool input_inhibited = seat->exclusive_client != NULL || 409 bool locked = server.session_lock.lock;
408 server.session_lock.locked;
409 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = 410 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =
410 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); 411 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat);
411 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; 412 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active;
@@ -423,17 +424,17 @@ static void handle_key_event(struct sway_keyboard *keyboard,
423 struct sway_binding *binding_released = NULL; 424 struct sway_binding *binding_released = NULL;
424 get_active_binding(&keyboard->state_keycodes, 425 get_active_binding(&keyboard->state_keycodes,
425 config->current_mode->keycode_bindings, &binding_released, 426 config->current_mode->keycode_bindings, &binding_released,
426 keyinfo.code_modifiers, true, input_inhibited, 427 keyinfo.code_modifiers, true, locked,
427 shortcuts_inhibited, device_identifier, 428 shortcuts_inhibited, device_identifier,
428 exact_identifier, keyboard->effective_layout); 429 exact_identifier, keyboard->effective_layout);
429 get_active_binding(&keyboard->state_keysyms_raw, 430 get_active_binding(&keyboard->state_keysyms_raw,
430 config->current_mode->keysym_bindings, &binding_released, 431 config->current_mode->keysym_bindings, &binding_released,
431 keyinfo.raw_modifiers, true, input_inhibited, 432 keyinfo.raw_modifiers, true, locked,
432 shortcuts_inhibited, device_identifier, 433 shortcuts_inhibited, device_identifier,
433 exact_identifier, keyboard->effective_layout); 434 exact_identifier, keyboard->effective_layout);
434 get_active_binding(&keyboard->state_keysyms_translated, 435 get_active_binding(&keyboard->state_keysyms_translated,
435 config->current_mode->keysym_bindings, &binding_released, 436 config->current_mode->keysym_bindings, &binding_released,
436 keyinfo.translated_modifiers, true, input_inhibited, 437 keyinfo.translated_modifiers, true, locked,
437 shortcuts_inhibited, device_identifier, 438 shortcuts_inhibited, device_identifier,
438 exact_identifier, keyboard->effective_layout); 439 exact_identifier, keyboard->effective_layout);
439 440
@@ -455,17 +456,17 @@ static void handle_key_event(struct sway_keyboard *keyboard,
455 if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { 456 if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
456 get_active_binding(&keyboard->state_keycodes, 457 get_active_binding(&keyboard->state_keycodes,
457 config->current_mode->keycode_bindings, &binding, 458 config->current_mode->keycode_bindings, &binding,
458 keyinfo.code_modifiers, false, input_inhibited, 459 keyinfo.code_modifiers, false, locked,
459 shortcuts_inhibited, device_identifier, 460 shortcuts_inhibited, device_identifier,
460 exact_identifier, keyboard->effective_layout); 461 exact_identifier, keyboard->effective_layout);
461 get_active_binding(&keyboard->state_keysyms_raw, 462 get_active_binding(&keyboard->state_keysyms_raw,
462 config->current_mode->keysym_bindings, &binding, 463 config->current_mode->keysym_bindings, &binding,
463 keyinfo.raw_modifiers, false, input_inhibited, 464 keyinfo.raw_modifiers, false, locked,
464 shortcuts_inhibited, device_identifier, 465 shortcuts_inhibited, device_identifier,
465 exact_identifier, keyboard->effective_layout); 466 exact_identifier, keyboard->effective_layout);
466 get_active_binding(&keyboard->state_keysyms_translated, 467 get_active_binding(&keyboard->state_keysyms_translated,
467 config->current_mode->keysym_bindings, &binding, 468 config->current_mode->keysym_bindings, &binding,
468 keyinfo.translated_modifiers, false, input_inhibited, 469 keyinfo.translated_modifiers, false, locked,
469 shortcuts_inhibited, device_identifier, 470 shortcuts_inhibited, device_identifier,
470 exact_identifier, keyboard->effective_layout); 471 exact_identifier, keyboard->effective_layout);
471 } 472 }
@@ -715,23 +716,11 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
715 716
716static void handle_xkb_context_log(struct xkb_context *context, 717static void handle_xkb_context_log(struct xkb_context *context,
717 enum xkb_log_level level, const char *format, va_list args) { 718 enum xkb_log_level level, const char *format, va_list args) {
718 va_list args_copy; 719 char *error = vformat_str(format, args);
719 va_copy(args_copy, args);
720 size_t length = vsnprintf(NULL, 0, format, args_copy) + 1;
721 va_end(args_copy);
722
723 char *error = malloc(length);
724 if (!error) {
725 sway_log(SWAY_ERROR, "Failed to allocate libxkbcommon log message");
726 return;
727 }
728
729 va_copy(args_copy, args);
730 vsnprintf(error, length, format, args_copy);
731 va_end(args_copy);
732 720
733 if (error[length - 2] == '\n') { 721 size_t len = strlen(error);
734 error[length - 2] = '\0'; 722 if (error[len - 1] == '\n') {
723 error[len - 1] = '\0';
735 } 724 }
736 725
737 sway_log_importance_t importance = SWAY_DEBUG; 726 sway_log_importance_t importance = SWAY_DEBUG;
@@ -752,7 +741,7 @@ static void handle_xkb_context_log(struct xkb_context *context,
752 741
753struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, 742struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,
754 char **error) { 743 char **error) {
755 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); 744 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV);
756 if (!sway_assert(context, "cannot create XKB context")) { 745 if (!sway_assert(context, "cannot create XKB context")) {
757 return NULL; 746 return NULL;
758 } 747 }
@@ -766,13 +755,8 @@ struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,
766 if (!keymap_file) { 755 if (!keymap_file) {
767 sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file); 756 sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file);
768 if (error) { 757 if (error) {
769 size_t len = snprintf(NULL, 0, "cannot read xkb file %s: %s", 758 *error = format_str("cannot read xkb file %s: %s",
770 ic->xkb_file, strerror(errno)) + 1; 759 ic->xkb_file, strerror(errno));
771 *error = malloc(len);
772 if (*error) {
773 snprintf(*error, len, "cannot read xkb_file %s: %s",
774 ic->xkb_file, strerror(errno));
775 }
776 } 760 }
777 goto cleanup; 761 goto cleanup;
778 } 762 }
@@ -1068,8 +1052,12 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
1068 } 1052 }
1069 } 1053 }
1070 1054
1055 // If the seat has no active keyboard, set this one
1071 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; 1056 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
1072 wlr_seat_set_keyboard(seat, keyboard->wlr); 1057 struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard;
1058 if (current_keyboard == NULL) {
1059 wlr_seat_set_keyboard(seat, keyboard->wlr);
1060 }
1073 1061
1074 wl_list_remove(&keyboard->keyboard_key.link); 1062 wl_list_remove(&keyboard->keyboard_key.link);
1075 wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); 1063 wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key);
diff --git a/sway/input/libinput.c b/sway/input/libinput.c
index 3c0f359d..0266c7a9 100644
--- a/sway/input/libinput.c
+++ b/sway/input/libinput.c
@@ -79,6 +79,16 @@ static bool set_accel_speed(struct libinput_device *device, double speed) {
79 return true; 79 return true;
80} 80}
81 81
82static 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
82static bool set_accel_profile(struct libinput_device *device, 92static bool set_accel_profile(struct libinput_device *device,
83 enum libinput_config_accel_profile profile) { 93 enum libinput_config_accel_profile profile) {
84 if (!libinput_device_config_accel_is_available(device) || 94 if (!libinput_device_config_accel_is_available(device) ||
@@ -156,6 +166,18 @@ static bool set_scroll_button(struct libinput_device *dev, uint32_t button) {
156 return true; 166 return true;
157} 167}
158 168
169static 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
159static bool set_dwt(struct libinput_device *device, bool dwt) { 181static bool set_dwt(struct libinput_device *device, bool dwt) {
160 if (!libinput_device_config_dwt_is_available(device) || 182 if (!libinput_device_config_dwt_is_available(device) ||
161 libinput_device_config_dwt_get_enabled(device) == dwt) { 183 libinput_device_config_dwt_get_enabled(device) == dwt) {
@@ -166,6 +188,16 @@ static bool set_dwt(struct libinput_device *device, bool dwt) {
166 return true; 188 return true;
167} 189}
168 190
191static 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
169static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { 201static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) {
170 if (!libinput_device_config_calibration_has_matrix(dev)) { 202 if (!libinput_device_config_calibration_has_matrix(dev)) {
171 return false; 203 return false;
@@ -187,35 +219,38 @@ static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) {
187 return changed; 219 return changed;
188} 220}
189 221
190void sway_input_configure_libinput_device(struct sway_input_device *input_device) { 222static bool configure_send_events(struct libinput_device *device,
191 struct input_config *ic = input_device_get_config(input_device); 223 struct input_config *ic) {
192 if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) {
193 return;
194 }
195
196 struct libinput_device *device =
197 wlr_libinput_get_device_handle(input_device->wlr_device);
198 sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')",
199 ic->identifier, input_device->identifier);
200
201 bool changed = false;
202 if (ic->mapped_to_output && 224 if (ic->mapped_to_output &&
225 strcmp("*", ic->mapped_to_output) != 0 &&
203 !output_by_name_or_id(ic->mapped_to_output)) { 226 !output_by_name_or_id(ic->mapped_to_output)) {
204 sway_log(SWAY_DEBUG, 227 sway_log(SWAY_DEBUG,
205 "%s '%s' is mapped to offline output '%s'; disabling input", 228 "%s '%s' is mapped to offline output '%s'; disabling input",
206 ic->input_type, ic->identifier, ic->mapped_to_output); 229 ic->input_type, ic->identifier, ic->mapped_to_output);
207 changed |= set_send_events(device, 230 return set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
208 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
209 } else if (ic->send_events != INT_MIN) { 231 } else if (ic->send_events != INT_MIN) {
210 changed |= set_send_events(device, ic->send_events); 232 return set_send_events(device, ic->send_events);
211 } else { 233 } else {
212 // Have to reset to the default mode here, otherwise if ic->send_events 234 // Have to reset to the default mode here, otherwise if ic->send_events
213 // is unset and a mapped output just came online after being disabled, 235 // is unset and a mapped output just came online after being disabled,
214 // we'd remain stuck sending no events. 236 // we'd remain stuck sending no events.
215 changed |= set_send_events(device, 237 return set_send_events(device,
216 libinput_device_config_send_events_get_default_mode(device)); 238 libinput_device_config_send_events_get_default_mode(device));
217 } 239 }
240}
241
242bool sway_input_configure_libinput_device(struct sway_input_device *input_device) {
243 struct input_config *ic = input_device_get_config(input_device);
244 if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) {
245 return false;
246 }
218 247
248 struct libinput_device *device =
249 wlr_libinput_get_device_handle(input_device->wlr_device);
250 sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')",
251 ic->identifier, input_device->identifier);
252
253 bool changed = configure_send_events(device, ic);
219 if (ic->tap != INT_MIN) { 254 if (ic->tap != INT_MIN) {
220 changed |= set_tap(device, ic->tap); 255 changed |= set_tap(device, ic->tap);
221 } 256 }
@@ -231,6 +266,9 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device
231 if (ic->pointer_accel != FLT_MIN) { 266 if (ic->pointer_accel != FLT_MIN) {
232 changed |= set_accel_speed(device, ic->pointer_accel); 267 changed |= set_accel_speed(device, ic->pointer_accel);
233 } 268 }
269 if (ic->rotation_angle != FLT_MIN) {
270 changed |= set_rotation_angle(device, ic->rotation_angle);
271 }
234 if (ic->accel_profile != INT_MIN) { 272 if (ic->accel_profile != INT_MIN) {
235 changed |= set_accel_profile(device, ic->accel_profile); 273 changed |= set_accel_profile(device, ic->accel_profile);
236 } 274 }
@@ -252,13 +290,33 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device
252 if (ic->scroll_button != INT_MIN) { 290 if (ic->scroll_button != INT_MIN) {
253 changed |= set_scroll_button(device, ic->scroll_button); 291 changed |= set_scroll_button(device, ic->scroll_button);
254 } 292 }
293 if (ic->scroll_button_lock != INT_MIN) {
294 changed |= set_scroll_button_lock(device, ic->scroll_button_lock);
295 }
255 if (ic->dwt != INT_MIN) { 296 if (ic->dwt != INT_MIN) {
256 changed |= set_dwt(device, ic->dwt); 297 changed |= set_dwt(device, ic->dwt);
257 } 298 }
299 if (ic->dwtp != INT_MIN) {
300 changed |= set_dwtp(device, ic->dwtp);
301 }
258 if (ic->calibration_matrix.configured) { 302 if (ic->calibration_matrix.configured) {
259 changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); 303 changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix);
260 } 304 }
261 305
306 return changed;
307}
308
309void sway_input_configure_libinput_device_send_events(
310 struct sway_input_device *input_device) {
311 struct input_config *ic = input_device_get_config(input_device);
312 if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) {
313 return;
314 }
315
316 struct libinput_device *device =
317 wlr_libinput_get_device_handle(input_device->wlr_device);
318 bool changed = configure_send_events(device, ic);
319
262 if (changed) { 320 if (changed) {
263 ipc_event_input("libinput_config", input_device); 321 ipc_event_input("libinput_config", input_device);
264 } 322 }
@@ -287,6 +345,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) {
287 libinput_device_config_tap_get_default_drag_lock_enabled(device)); 345 libinput_device_config_tap_get_default_drag_lock_enabled(device));
288 changed |= set_accel_speed(device, 346 changed |= set_accel_speed(device,
289 libinput_device_config_accel_get_default_speed(device)); 347 libinput_device_config_accel_get_default_speed(device));
348 changed |= set_rotation_angle(device,
349 libinput_device_config_rotation_get_default_angle(device));
290 changed |= set_accel_profile(device, 350 changed |= set_accel_profile(device,
291 libinput_device_config_accel_get_default_profile(device)); 351 libinput_device_config_accel_get_default_profile(device));
292 changed |= set_natural_scroll(device, 352 changed |= set_natural_scroll(device,
@@ -304,6 +364,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) {
304 libinput_device_config_scroll_get_default_button(device)); 364 libinput_device_config_scroll_get_default_button(device));
305 changed |= set_dwt(device, 365 changed |= set_dwt(device,
306 libinput_device_config_dwt_get_default_enabled(device)); 366 libinput_device_config_dwt_get_default_enabled(device));
367 changed |= set_dwtp(device,
368 libinput_device_config_dwtp_get_default_enabled(device));
307 369
308 float matrix[6]; 370 float matrix[6];
309 libinput_device_config_calibration_get_default_matrix(device, matrix); 371 libinput_device_config_calibration_get_default_matrix(device, matrix);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index b21e1b86..0c5672bc 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,12 +1,12 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <string.h> 3#include <string.h>
5#include <strings.h> 4#include <strings.h>
6#include <time.h> 5#include <time.h>
6#include <wlr/config.h>
7#include <wlr/types/wlr_cursor.h> 7#include <wlr/types/wlr_cursor.h>
8#include <wlr/types/wlr_data_device.h> 8#include <wlr/types/wlr_data_device.h>
9#include <wlr/types/wlr_idle.h> 9#include <wlr/types/wlr_idle_notify_v1.h>
10#include <wlr/types/wlr_keyboard_group.h> 10#include <wlr/types/wlr_keyboard_group.h>
11#include <wlr/types/wlr_output_layout.h> 11#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_primary_selection.h> 12#include <wlr/types/wlr_primary_selection.h>
@@ -17,7 +17,7 @@
17#include "list.h" 17#include "list.h"
18#include "log.h" 18#include "log.h"
19#include "sway/config.h" 19#include "sway/config.h"
20#include "sway/desktop.h" 20#include "sway/scene_descriptor.h"
21#include "sway/input/cursor.h" 21#include "sway/input/cursor.h"
22#include "sway/input/input-manager.h" 22#include "sway/input/input-manager.h"
23#include "sway/input/keyboard.h" 23#include "sway/input/keyboard.h"
@@ -67,6 +67,12 @@ static void seat_node_destroy(struct sway_seat_node *seat_node) {
67} 67}
68 68
69void seat_destroy(struct sway_seat *seat) { 69void seat_destroy(struct sway_seat *seat) {
70 wlr_seat_destroy(seat->wlr_seat);
71}
72
73static void handle_seat_destroy(struct wl_listener *listener, void *data) {
74 struct sway_seat *seat = wl_container_of(listener, seat, destroy);
75
70 if (seat == config->handler_context.seat) { 76 if (seat == config->handler_context.seat) {
71 config->handler_context.seat = input_manager_get_default_seat(); 77 config->handler_context.seat = input_manager_get_default_seat();
72 } 78 }
@@ -87,10 +93,11 @@ void seat_destroy(struct sway_seat *seat) {
87 wl_list_remove(&seat->request_set_selection.link); 93 wl_list_remove(&seat->request_set_selection.link);
88 wl_list_remove(&seat->request_set_primary_selection.link); 94 wl_list_remove(&seat->request_set_primary_selection.link);
89 wl_list_remove(&seat->link); 95 wl_list_remove(&seat->link);
90 wlr_seat_destroy(seat->wlr_seat); 96 wl_list_remove(&seat->destroy.link);
91 for (int i = 0; i < seat->deferred_bindings->length; i++) { 97 for (int i = 0; i < seat->deferred_bindings->length; i++) {
92 free_sway_binding(seat->deferred_bindings->items[i]); 98 free_sway_binding(seat->deferred_bindings->items[i]);
93 } 99 }
100 wlr_scene_node_destroy(&seat->scene_tree->node);
94 list_free(seat->deferred_bindings); 101 list_free(seat->deferred_bindings);
95 free(seat->prev_workspace_name); 102 free(seat->prev_workspace_name);
96 free(seat); 103 free(seat);
@@ -98,21 +105,10 @@ void seat_destroy(struct sway_seat *seat) {
98 105
99void seat_idle_notify_activity(struct sway_seat *seat, 106void seat_idle_notify_activity(struct sway_seat *seat,
100 enum sway_input_idle_source source) { 107 enum sway_input_idle_source source) {
101 uint32_t mask = seat->idle_inhibit_sources; 108 if ((source & seat->idle_inhibit_sources) == 0) {
102 struct wlr_idle_timeout *timeout; 109 return;
103 int ntimers = 0, nidle = 0;
104 wl_list_for_each(timeout, &server.idle->idle_timers, link) {
105 ++ntimers;
106 if (timeout->idle_state) {
107 ++nidle;
108 }
109 }
110 if (nidle == ntimers) {
111 mask = seat->idle_wake_sources;
112 }
113 if ((source & mask) > 0) {
114 wlr_idle_notify_activity(server.idle, seat->wlr_seat);
115 } 110 }
111 wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat);
116} 112}
117 113
118/** 114/**
@@ -174,11 +170,11 @@ static void seat_keyboard_notify_enter(struct sway_seat *seat,
174 state->pressed_keycodes, state->npressed, &keyboard->modifiers); 170 state->pressed_keycodes, state->npressed, &keyboard->modifiers);
175} 171}
176 172
177static void seat_tablet_pads_notify_enter(struct sway_seat *seat, 173static void seat_tablet_pads_set_focus(struct sway_seat *seat,
178 struct wlr_surface *surface) { 174 struct wlr_surface *surface) {
179 struct sway_seat_device *seat_device; 175 struct sway_seat_device *seat_device;
180 wl_list_for_each(seat_device, &seat->devices, link) { 176 wl_list_for_each(seat_device, &seat->devices, link) {
181 sway_tablet_pad_notify_enter(seat_device->tablet_pad, surface); 177 sway_tablet_pad_set_focus(seat_device->tablet_pad, surface);
182 } 178 }
183} 179}
184 180
@@ -202,7 +198,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
202#endif 198#endif
203 199
204 seat_keyboard_notify_enter(seat, view->surface); 200 seat_keyboard_notify_enter(seat, view->surface);
205 seat_tablet_pads_notify_enter(seat, view->surface); 201 seat_tablet_pads_set_focus(seat, view->surface);
206 sway_input_method_relay_set_focus(&seat->im_relay, view->surface); 202 sway_input_method_relay_set_focus(&seat->im_relay, view->surface);
207 203
208 struct wlr_pointer_constraint_v1 *constraint = 204 struct wlr_pointer_constraint_v1 *constraint =
@@ -212,15 +208,6 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
212 } 208 }
213} 209}
214 210
215void sway_force_focus(struct wlr_surface *surface) {
216 struct sway_seat *seat;
217 wl_list_for_each(seat, &server.input->seats, link) {
218 seat_keyboard_notify_enter(seat, surface);
219 seat_tablet_pads_notify_enter(seat, surface);
220 sway_input_method_relay_set_focus(&seat->im_relay, surface);
221 }
222}
223
224void seat_for_each_node(struct sway_seat *seat, 211void seat_for_each_node(struct sway_seat *seat,
225 void (*f)(struct sway_node *node, void *data), void *data) { 212 void (*f)(struct sway_node *node, void *data), void *data) {
226 struct sway_seat_node *current = NULL; 213 struct sway_seat_node *current = NULL;
@@ -372,25 +359,15 @@ static void handle_new_node(struct wl_listener *listener, void *data) {
372 seat_node_from_node(seat, node); 359 seat_node_from_node(seat, node);
373} 360}
374 361
375static void drag_icon_damage_whole(struct sway_drag_icon *icon) { 362static void drag_icon_update_position(struct sway_seat *seat, struct wlr_scene_node *node) {
376 if (!icon->wlr_drag_icon->mapped) { 363 struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON);
377 return;
378 }
379 desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true);
380}
381
382void drag_icon_update_position(struct sway_drag_icon *icon) {
383 drag_icon_damage_whole(icon);
384
385 struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon;
386 struct sway_seat *seat = icon->seat;
387 struct wlr_cursor *cursor = seat->cursor->cursor; 364 struct wlr_cursor *cursor = seat->cursor->cursor;
365
388 switch (wlr_icon->drag->grab_type) { 366 switch (wlr_icon->drag->grab_type) {
389 case WLR_DRAG_GRAB_KEYBOARD: 367 case WLR_DRAG_GRAB_KEYBOARD:
390 return; 368 return;
391 case WLR_DRAG_GRAB_KEYBOARD_POINTER: 369 case WLR_DRAG_GRAB_KEYBOARD_POINTER:
392 icon->x = cursor->x + wlr_icon->surface->sx; 370 wlr_scene_node_set_position(node, cursor->x, cursor->y);
393 icon->y = cursor->y + wlr_icon->surface->sy;
394 break; 371 break;
395 case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; 372 case WLR_DRAG_GRAB_KEYBOARD_TOUCH:;
396 struct wlr_touch_point *point = 373 struct wlr_touch_point *point =
@@ -398,39 +375,15 @@ void drag_icon_update_position(struct sway_drag_icon *icon) {
398 if (point == NULL) { 375 if (point == NULL) {
399 return; 376 return;
400 } 377 }
401 icon->x = seat->touch_x + wlr_icon->surface->sx; 378 wlr_scene_node_set_position(node, seat->touch_x, seat->touch_y);
402 icon->y = seat->touch_y + wlr_icon->surface->sy;
403 } 379 }
404
405 drag_icon_damage_whole(icon);
406}
407
408static void drag_icon_handle_surface_commit(struct wl_listener *listener,
409 void *data) {
410 struct sway_drag_icon *icon =
411 wl_container_of(listener, icon, surface_commit);
412 drag_icon_update_position(icon);
413}
414
415static void drag_icon_handle_map(struct wl_listener *listener, void *data) {
416 struct sway_drag_icon *icon = wl_container_of(listener, icon, map);
417 drag_icon_damage_whole(icon);
418} 380}
419 381
420static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) { 382void drag_icons_update_position(struct sway_seat *seat) {
421 struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap); 383 struct wlr_scene_node *node;
422 drag_icon_damage_whole(icon); 384 wl_list_for_each(node, &seat->drag_icons->children, link) {
423} 385 drag_icon_update_position(seat, node);
424 386 }
425static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) {
426 struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy);
427 icon->wlr_drag_icon->data = NULL;
428 wl_list_remove(&icon->link);
429 wl_list_remove(&icon->surface_commit.link);
430 wl_list_remove(&icon->unmap.link);
431 wl_list_remove(&icon->map.link);
432 wl_list_remove(&icon->destroy.link);
433 free(icon);
434} 387}
435 388
436static void drag_handle_destroy(struct wl_listener *listener, void *data) { 389static void drag_handle_destroy(struct wl_listener *listener, void *data) {
@@ -502,27 +455,20 @@ static void handle_start_drag(struct wl_listener *listener, void *data) {
502 455
503 struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; 456 struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon;
504 if (wlr_drag_icon != NULL) { 457 if (wlr_drag_icon != NULL) {
505 struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); 458 struct wlr_scene_tree *tree = wlr_scene_drag_icon_create(seat->drag_icons, wlr_drag_icon);
506 if (icon == NULL) { 459 if (!tree) {
507 sway_log(SWAY_ERROR, "Allocation failed"); 460 sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree");
508 return; 461 return;
509 } 462 }
510 icon->seat = seat;
511 icon->wlr_drag_icon = wlr_drag_icon;
512 wlr_drag_icon->data = icon;
513 463
514 icon->surface_commit.notify = drag_icon_handle_surface_commit; 464 if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON,
515 wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); 465 wlr_drag_icon)) {
516 icon->unmap.notify = drag_icon_handle_unmap; 466 sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor");
517 wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); 467 wlr_scene_node_destroy(&tree->node);
518 icon->map.notify = drag_icon_handle_map; 468 return;
519 wl_signal_add(&wlr_drag_icon->events.map, &icon->map); 469 }
520 icon->destroy.notify = drag_icon_handle_destroy;
521 wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy);
522
523 wl_list_insert(&root->drag_icons, &icon->link);
524 470
525 drag_icon_update_position(icon); 471 drag_icon_update_position(seat, &tree->node);
526 } 472 }
527 seatop_begin_default(seat); 473 seatop_begin_default(seat);
528} 474}
@@ -569,8 +515,18 @@ struct sway_seat *seat_create(const char *seat_name) {
569 return NULL; 515 return NULL;
570 } 516 }
571 517
518 bool failed = false;
519 seat->scene_tree = alloc_scene_tree(root->layers.seat, &failed);
520 seat->drag_icons = alloc_scene_tree(seat->scene_tree, &failed);
521 if (failed) {
522 wlr_scene_node_destroy(&seat->scene_tree->node);
523 free(seat);
524 return NULL;
525 }
526
572 seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); 527 seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name);
573 if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { 528 if (!sway_assert(seat->wlr_seat, "could not allocate seat")) {
529 wlr_scene_node_destroy(&seat->scene_tree->node);
574 free(seat); 530 free(seat);
575 return NULL; 531 return NULL;
576 } 532 }
@@ -578,11 +534,15 @@ struct sway_seat *seat_create(const char *seat_name) {
578 534
579 seat->cursor = sway_cursor_create(seat); 535 seat->cursor = sway_cursor_create(seat);
580 if (!seat->cursor) { 536 if (!seat->cursor) {
537 wlr_scene_node_destroy(&seat->scene_tree->node);
581 wlr_seat_destroy(seat->wlr_seat); 538 wlr_seat_destroy(seat->wlr_seat);
582 free(seat); 539 free(seat);
583 return NULL; 540 return NULL;
584 } 541 }
585 542
543 seat->destroy.notify = handle_seat_destroy;
544 wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy);
545
586 seat->idle_inhibit_sources = seat->idle_wake_sources = 546 seat->idle_inhibit_sources = seat->idle_wake_sources =
587 IDLE_SOURCE_KEYBOARD | 547 IDLE_SOURCE_KEYBOARD |
588 IDLE_SOURCE_POINTER | 548 IDLE_SOURCE_POINTER |
@@ -656,7 +616,7 @@ static void seat_update_capabilities(struct sway_seat *seat) {
656 case WLR_INPUT_DEVICE_TOUCH: 616 case WLR_INPUT_DEVICE_TOUCH:
657 caps |= WL_SEAT_CAPABILITY_TOUCH; 617 caps |= WL_SEAT_CAPABILITY_TOUCH;
658 break; 618 break;
659 case WLR_INPUT_DEVICE_TABLET_TOOL: 619 case WLR_INPUT_DEVICE_TABLET:
660 caps |= WL_SEAT_CAPABILITY_POINTER; 620 caps |= WL_SEAT_CAPABILITY_POINTER;
661 break; 621 break;
662 case WLR_INPUT_DEVICE_SWITCH: 622 case WLR_INPUT_DEVICE_SWITCH:
@@ -674,7 +634,7 @@ static void seat_update_capabilities(struct sway_seat *seat) {
674 } else { 634 } else {
675 wlr_seat_set_capabilities(seat->wlr_seat, caps); 635 wlr_seat_set_capabilities(seat->wlr_seat, caps);
676 if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { 636 if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
677 cursor_set_image(seat->cursor, "left_ptr", NULL); 637 cursor_set_image(seat->cursor, "default", NULL);
678 } 638 }
679 } 639 }
680} 640}
@@ -714,19 +674,28 @@ static const char *get_builtin_output_name(void) {
714static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { 674static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) {
715 switch (seat_device->input_device->wlr_device->type) { 675 switch (seat_device->input_device->wlr_device->type) {
716 case WLR_INPUT_DEVICE_TOUCH: 676 case WLR_INPUT_DEVICE_TOUCH:
717 case WLR_INPUT_DEVICE_TABLET_TOOL: 677 case WLR_INPUT_DEVICE_TABLET:
718 return true; 678 return true;
719 default: 679 default:
720 return false; 680 return false;
721 } 681 }
722} 682}
723 683
724static void seat_apply_input_config(struct sway_seat *seat, 684static void seat_apply_input_mapping(struct sway_seat *seat,
725 struct sway_seat_device *sway_device) { 685 struct sway_seat_device *sway_device) {
726 struct input_config *ic = 686 struct input_config *ic =
727 input_device_get_config(sway_device->input_device); 687 input_device_get_config(sway_device->input_device);
728 688
729 sway_log(SWAY_DEBUG, "Applying input config to %s", 689 switch (sway_device->input_device->wlr_device->type) {
690 case WLR_INPUT_DEVICE_POINTER:
691 case WLR_INPUT_DEVICE_TOUCH:
692 case WLR_INPUT_DEVICE_TABLET:
693 break;
694 default:
695 return; // these devices don't support mappings
696 }
697
698 sway_log(SWAY_DEBUG, "Applying input mapping to %s",
730 sway_device->input_device->identifier); 699 sway_device->input_device->identifier);
731 700
732 const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output; 701 const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output;
@@ -754,6 +723,7 @@ static void seat_apply_input_config(struct sway_seat *seat,
754 mapped_to_output = NULL; 723 mapped_to_output = NULL;
755 break; 724 break;
756 } 725 }
726#if WLR_HAS_LIBINPUT_BACKEND
757 if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && 727 if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) &&
758 sway_libinput_device_is_builtin(sway_device->input_device)) { 728 sway_libinput_device_is_builtin(sway_device->input_device)) {
759 mapped_to_output = get_builtin_output_name(); 729 mapped_to_output = get_builtin_output_name();
@@ -762,6 +732,10 @@ static void seat_apply_input_config(struct sway_seat *seat,
762 mapped_to_output, sway_device->input_device->identifier); 732 mapped_to_output, sway_device->input_device->identifier);
763 } 733 }
764 } 734 }
735#else
736 (void)is_touch_or_tablet_tool;
737 (void)get_builtin_output_name;
738#endif
765 if (mapped_to_output == NULL) { 739 if (mapped_to_output == NULL) {
766 return; 740 return;
767 } 741 }
@@ -805,12 +779,9 @@ static void seat_apply_input_config(struct sway_seat *seat,
805 779
806static void seat_configure_pointer(struct sway_seat *seat, 780static void seat_configure_pointer(struct sway_seat *seat,
807 struct sway_seat_device *sway_device) { 781 struct sway_seat_device *sway_device) {
808 if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { 782 seat_configure_xcursor(seat);
809 seat_configure_xcursor(seat);
810 }
811 wlr_cursor_attach_input_device(seat->cursor->cursor, 783 wlr_cursor_attach_input_device(seat->cursor->cursor,
812 sway_device->input_device->wlr_device); 784 sway_device->input_device->wlr_device);
813 seat_apply_input_config(seat, sway_device);
814 wl_event_source_timer_update( 785 wl_event_source_timer_update(
815 seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); 786 seat->cursor->hide_source, cursor_get_timeout(seat->cursor));
816} 787}
@@ -821,8 +792,15 @@ static void seat_configure_keyboard(struct sway_seat *seat,
821 sway_keyboard_create(seat, seat_device); 792 sway_keyboard_create(seat, seat_device);
822 } 793 }
823 sway_keyboard_configure(seat_device->keyboard); 794 sway_keyboard_configure(seat_device->keyboard);
824 wlr_seat_set_keyboard(seat->wlr_seat, 795
825 wlr_keyboard_from_input_device(seat_device->input_device->wlr_device)); 796 // We only need to update the current keyboard, as the rest will be updated
797 // as they are activated.
798 struct wlr_keyboard *wlr_keyboard =
799 wlr_keyboard_from_input_device(seat_device->input_device->wlr_device);
800 struct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard;
801 if (wlr_keyboard != current_keyboard) {
802 return;
803 }
826 804
827 // force notify reenter to pick up the new configuration. This reuses 805 // force notify reenter to pick up the new configuration. This reuses
828 // the current focused surface to avoid breaking input grabs. 806 // the current focused surface to avoid breaking input grabs.
@@ -838,7 +816,6 @@ static void seat_configure_switch(struct sway_seat *seat,
838 if (!seat_device->switch_device) { 816 if (!seat_device->switch_device) {
839 sway_switch_create(seat, seat_device); 817 sway_switch_create(seat, seat_device);
840 } 818 }
841 seat_apply_input_config(seat, seat_device);
842 sway_switch_configure(seat_device->switch_device); 819 sway_switch_configure(seat_device->switch_device);
843} 820}
844 821
@@ -846,7 +823,6 @@ static void seat_configure_touch(struct sway_seat *seat,
846 struct sway_seat_device *sway_device) { 823 struct sway_seat_device *sway_device) {
847 wlr_cursor_attach_input_device(seat->cursor->cursor, 824 wlr_cursor_attach_input_device(seat->cursor->cursor,
848 sway_device->input_device->wlr_device); 825 sway_device->input_device->wlr_device);
849 seat_apply_input_config(seat, sway_device);
850} 826}
851 827
852static void seat_configure_tablet_tool(struct sway_seat *seat, 828static void seat_configure_tablet_tool(struct sway_seat *seat,
@@ -857,7 +833,6 @@ static void seat_configure_tablet_tool(struct sway_seat *seat,
857 sway_configure_tablet(sway_device->tablet); 833 sway_configure_tablet(sway_device->tablet);
858 wlr_cursor_attach_input_device(seat->cursor->cursor, 834 wlr_cursor_attach_input_device(seat->cursor->cursor,
859 sway_device->input_device->wlr_device); 835 sway_device->input_device->wlr_device);
860 seat_apply_input_config(seat, sway_device);
861} 836}
862 837
863static void seat_configure_tablet_pad(struct sway_seat *seat, 838static void seat_configure_tablet_pad(struct sway_seat *seat,
@@ -907,13 +882,25 @@ void seat_configure_device(struct sway_seat *seat,
907 case WLR_INPUT_DEVICE_TOUCH: 882 case WLR_INPUT_DEVICE_TOUCH:
908 seat_configure_touch(seat, seat_device); 883 seat_configure_touch(seat, seat_device);
909 break; 884 break;
910 case WLR_INPUT_DEVICE_TABLET_TOOL: 885 case WLR_INPUT_DEVICE_TABLET:
911 seat_configure_tablet_tool(seat, seat_device); 886 seat_configure_tablet_tool(seat, seat_device);
912 break; 887 break;
913 case WLR_INPUT_DEVICE_TABLET_PAD: 888 case WLR_INPUT_DEVICE_TABLET_PAD:
914 seat_configure_tablet_pad(seat, seat_device); 889 seat_configure_tablet_pad(seat, seat_device);
915 break; 890 break;
916 } 891 }
892
893 seat_apply_input_mapping(seat, seat_device);
894}
895
896void seat_configure_device_mapping(struct sway_seat *seat,
897 struct sway_input_device *input_device) {
898 struct sway_seat_device *seat_device = seat_get_device(seat, input_device);
899 if (!seat_device) {
900 return;
901 }
902
903 seat_apply_input_mapping(seat, seat_device);
917} 904}
918 905
919void seat_reset_device(struct sway_seat *seat, 906void seat_reset_device(struct sway_seat *seat,
@@ -934,7 +921,7 @@ void seat_reset_device(struct sway_seat *seat,
934 case WLR_INPUT_DEVICE_TOUCH: 921 case WLR_INPUT_DEVICE_TOUCH:
935 seat_reset_input_config(seat, seat_device); 922 seat_reset_input_config(seat, seat_device);
936 break; 923 break;
937 case WLR_INPUT_DEVICE_TABLET_TOOL: 924 case WLR_INPUT_DEVICE_TABLET:
938 seat_reset_input_config(seat, seat_device); 925 seat_reset_input_config(seat, seat_device);
939 break; 926 break;
940 case WLR_INPUT_DEVICE_TABLET_PAD: 927 case WLR_INPUT_DEVICE_TABLET_PAD:
@@ -1030,7 +1017,7 @@ void seat_configure_xcursor(struct sway_seat *seat) {
1030 1017
1031 wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); 1018 wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1);
1032 struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( 1019 struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
1033 server.xwayland.xcursor_manager, "left_ptr", 1); 1020 server.xwayland.xcursor_manager, "default", 1);
1034 if (xcursor != NULL) { 1021 if (xcursor != NULL) {
1035 struct wlr_xcursor_image *image = xcursor->images[0]; 1022 struct wlr_xcursor_image *image = xcursor->images[0];
1036 wlr_xwayland_set_cursor( 1023 wlr_xwayland_set_cursor(
@@ -1056,33 +1043,35 @@ void seat_configure_xcursor(struct sway_seat *seat) {
1056 sway_log(SWAY_ERROR, 1043 sway_log(SWAY_ERROR,
1057 "Cannot create XCursor manager for theme '%s'", cursor_theme); 1044 "Cannot create XCursor manager for theme '%s'", cursor_theme);
1058 } 1045 }
1059 }
1060 1046
1061 for (int i = 0; i < root->outputs->length; ++i) { 1047
1062 struct sway_output *sway_output = root->outputs->items[i]; 1048 for (int i = 0; i < root->outputs->length; ++i) {
1063 struct wlr_output *output = sway_output->wlr_output; 1049 struct sway_output *sway_output = root->outputs->items[i];
1064 bool result = 1050 struct wlr_output *output = sway_output->wlr_output;
1065 wlr_xcursor_manager_load(seat->cursor->xcursor_manager, 1051 bool result =
1066 output->scale); 1052 wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
1067 if (!result) { 1053 output->scale);
1068 sway_log(SWAY_ERROR, 1054 if (!result) {
1069 "Cannot load xcursor theme for output '%s' with scale %f", 1055 sway_log(SWAY_ERROR,
1070 output->name, output->scale); 1056 "Cannot load xcursor theme for output '%s' with scale %f",
1057 output->name, output->scale);
1058 }
1071 } 1059 }
1072 }
1073 1060
1074 // Reset the cursor so that we apply it to outputs that just appeared 1061 // Reset the cursor so that we apply it to outputs that just appeared
1075 cursor_set_image(seat->cursor, NULL, NULL); 1062 cursor_set_image(seat->cursor, NULL, NULL);
1076 cursor_set_image(seat->cursor, "left_ptr", NULL); 1063 cursor_set_image(seat->cursor, "default", NULL);
1077 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, 1064 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
1078 seat->cursor->cursor->y); 1065 seat->cursor->cursor->y);
1066 }
1079} 1067}
1080 1068
1081bool seat_is_input_allowed(struct sway_seat *seat, 1069bool seat_is_input_allowed(struct sway_seat *seat,
1082 struct wlr_surface *surface) { 1070 struct wlr_surface *surface) {
1083 struct wl_client *client = wl_resource_get_client(surface->resource); 1071 if (server.session_lock.lock) {
1084 return seat->exclusive_client == client || 1072 return sway_session_lock_has_surface(server.session_lock.lock, surface);
1085 (seat->exclusive_client == NULL && !server.session_lock.locked); 1073 }
1074 return true;
1086} 1075}
1087 1076
1088static void send_unfocus(struct sway_container *con, void *data) { 1077static void send_unfocus(struct sway_container *con, void *data) {
@@ -1141,15 +1130,7 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) {
1141 } 1130 }
1142} 1131}
1143 1132
1144void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { 1133static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *node) {
1145 if (seat->focused_layer) {
1146 struct wlr_layer_surface_v1 *layer = seat->focused_layer;
1147 seat_set_focus_layer(seat, NULL);
1148 seat_set_focus(seat, node);
1149 seat_set_focus_layer(seat, layer);
1150 return;
1151 }
1152
1153 struct sway_node *last_focus = seat_get_focus(seat); 1134 struct sway_node *last_focus = seat_get_focus(seat);
1154 if (last_focus == node) { 1135 if (last_focus == node) {
1155 return; 1136 return;
@@ -1183,11 +1164,6 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
1183 return; 1164 return;
1184 } 1165 }
1185 1166
1186 // Deny setting focus when an input grab or lockscreen is active
1187 if (container && container->view && !seat_is_input_allowed(seat, container->view->surface)) {
1188 return;
1189 }
1190
1191 struct sway_output *new_output = 1167 struct sway_output *new_output =
1192 new_workspace ? new_workspace->output : NULL; 1168 new_workspace ? new_workspace->output : NULL;
1193 1169
@@ -1287,6 +1263,24 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
1287 } 1263 }
1288} 1264}
1289 1265
1266void 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
1290void seat_set_focus_container(struct sway_seat *seat, 1284void seat_set_focus_container(struct sway_seat *seat,
1291 struct sway_container *con) { 1285 struct sway_container *con) {
1292 seat_set_focus(seat, con ? &con->node : NULL); 1286 seat_set_focus(seat, con ? &con->node : NULL);
@@ -1312,7 +1306,7 @@ void seat_set_focus_surface(struct sway_seat *seat,
1312 } 1306 }
1313 1307
1314 sway_input_method_relay_set_focus(&seat->im_relay, surface); 1308 sway_input_method_relay_set_focus(&seat->im_relay, surface);
1315 seat_tablet_pads_notify_enter(seat, surface); 1309 seat_tablet_pads_set_focus(seat, surface);
1316} 1310}
1317 1311
1318void seat_set_focus_layer(struct sway_seat *seat, 1312void seat_set_focus_layer(struct sway_seat *seat,
@@ -1326,28 +1320,23 @@ void seat_set_focus_layer(struct sway_seat *seat,
1326 seat_set_focus(seat, previous); 1320 seat_set_focus(seat, previous);
1327 } 1321 }
1328 return; 1322 return;
1329 } else if (!layer || seat->focused_layer == layer) { 1323 } else if (!layer) {
1330 return; 1324 return;
1331 } 1325 }
1332 assert(layer->mapped); 1326 assert(layer->surface->mapped);
1333 seat_set_focus_surface(seat, layer->surface, true); 1327 if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP &&
1334 if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { 1328 layer->current.keyboard_interactive
1335 seat->focused_layer = layer; 1329 == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
1330 seat->has_exclusive_layer = true;
1336 } 1331 }
1337} 1332 if (seat->focused_layer == layer) {
1338
1339void seat_set_exclusive_client(struct sway_seat *seat,
1340 struct wl_client *client) {
1341 if (!client) {
1342 seat->exclusive_client = client;
1343 // Triggers a refocus of the topmost surface layer if necessary
1344 // TODO: Make layer surface focus per-output based on cursor position
1345 for (int i = 0; i < root->outputs->length; ++i) {
1346 struct sway_output *output = root->outputs->items[i];
1347 arrange_layers(output);
1348 }
1349 return; 1333 return;
1350 } 1334 }
1335 seat_set_focus_surface(seat, layer->surface, true);
1336 seat->focused_layer = layer;
1337}
1338
1339void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client) {
1351 if (seat->focused_layer) { 1340 if (seat->focused_layer) {
1352 if (wl_resource_get_client(seat->focused_layer->resource) != client) { 1341 if (wl_resource_get_client(seat->focused_layer->resource) != client) {
1353 seat_set_focus_layer(seat, NULL); 1342 seat_set_focus_layer(seat, NULL);
@@ -1374,7 +1363,6 @@ void seat_set_exclusive_client(struct sway_seat *seat,
1374 now.tv_nsec / 1000, point->touch_id); 1363 now.tv_nsec / 1000, point->touch_id);
1375 } 1364 }
1376 } 1365 }
1377 seat->exclusive_client = client;
1378} 1366}
1379 1367
1380struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, 1368struct sway_node *seat_get_focus_inactive(struct sway_seat *seat,
@@ -1541,7 +1529,7 @@ struct seat_config *seat_get_config_by_name(const char *name) {
1541} 1529}
1542 1530
1543void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, 1531void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,
1544 uint32_t button, enum wlr_button_state state) { 1532 uint32_t button, enum wl_pointer_button_state state) {
1545 seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, 1533 seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat,
1546 time_msec, button, state); 1534 time_msec, button, state);
1547} 1535}
@@ -1578,7 +1566,7 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) {
1578 1566
1579void seatop_button(struct sway_seat *seat, uint32_t time_msec, 1567void seatop_button(struct sway_seat *seat, uint32_t time_msec,
1580 struct wlr_input_device *device, uint32_t button, 1568 struct wlr_input_device *device, uint32_t button,
1581 enum wlr_button_state state) { 1569 enum wl_pointer_button_state state) {
1582 if (seat->seatop_impl->button) { 1570 if (seat->seatop_impl->button) {
1583 seat->seatop_impl->button(seat, time_msec, device, button, state); 1571 seat->seatop_impl->button(seat, time_msec, device, button, state);
1584 } 1572 }
@@ -1597,6 +1585,32 @@ void seatop_pointer_axis(struct sway_seat *seat,
1597 } 1585 }
1598} 1586}
1599 1587
1588void 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
1595void 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
1601void 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
1608void 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
1600void seatop_tablet_tool_tip(struct sway_seat *seat, 1614void seatop_tablet_tool_tip(struct sway_seat *seat,
1601 struct sway_tablet_tool *tool, uint32_t time_msec, 1615 struct sway_tablet_tool *tool, uint32_t time_msec,
1602 enum wlr_tablet_tool_tip_state state) { 1616 enum wlr_tablet_tool_tip_state state) {
@@ -1685,13 +1699,6 @@ void seatop_end(struct sway_seat *seat) {
1685 seat->seatop_impl = NULL; 1699 seat->seatop_impl = NULL;
1686} 1700}
1687 1701
1688void seatop_render(struct sway_seat *seat, struct sway_output *output,
1689 pixman_region32_t *damage) {
1690 if (seat->seatop_impl->render) {
1691 seat->seatop_impl->render(seat, output, damage);
1692 }
1693}
1694
1695bool seatop_allows_set_cursor(struct sway_seat *seat) { 1702bool seatop_allows_set_cursor(struct sway_seat *seat) {
1696 return seat->seatop_impl->allow_set_cursor; 1703 return seat->seatop_impl->allow_set_cursor;
1697} 1704}
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c
index 875426bf..0c6f7c5e 100644
--- a/sway/input/seatop_default.c
+++ b/sway/input/seatop_default.c
@@ -1,7 +1,7 @@
1#define _POSIX_C_SOURCE 200809L
2#include <float.h> 1#include <float.h>
3#include <libevdev/libevdev.h> 2#include <libevdev/libevdev.h>
4#include <wlr/types/wlr_cursor.h> 3#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_subcompositor.h>
5#include <wlr/types/wlr_tablet_v2.h> 5#include <wlr/types/wlr_tablet_v2.h>
6#include <wlr/types/wlr_xcursor_manager.h> 6#include <wlr/types/wlr_xcursor_manager.h>
7#include "gesture.h" 7#include "gesture.h"
@@ -9,7 +9,9 @@
9#include "sway/input/cursor.h" 9#include "sway/input/cursor.h"
10#include "sway/input/seat.h" 10#include "sway/input/seat.h"
11#include "sway/input/tablet.h" 11#include "sway/input/tablet.h"
12#include "sway/layers.h"
12#include "sway/output.h" 13#include "sway/output.h"
14#include "sway/scene_descriptor.h"
13#include "sway/tree/view.h" 15#include "sway/tree/view.h"
14#include "sway/tree/workspace.h" 16#include "sway/tree/workspace.h"
15#include "log.h" 17#include "log.h"
@@ -53,6 +55,9 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
53 while (cont) { 55 while (cont) {
54 if (container_parent_layout(cont) == layout) { 56 if (container_parent_layout(cont) == layout) {
55 list_t *siblings = container_get_siblings(cont); 57 list_t *siblings = container_get_siblings(cont);
58 if (!siblings) {
59 return false;
60 }
56 int index = list_find(siblings, cont); 61 int index = list_find(siblings, cont);
57 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { 62 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) {
58 return false; 63 return false;
@@ -228,14 +233,15 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
228 struct sway_container *cont = node && node->type == N_CONTAINER ? 233 struct sway_container *cont = node && node->type == N_CONTAINER ?
229 node->sway_container : NULL; 234 node->sway_container : NULL;
230 235
231 if (wlr_surface_is_layer_surface(surface)) { 236 struct wlr_layer_surface_v1 *layer;
237#if HAVE_XWAYLAND
238 struct wlr_xwayland_surface *xsurface;
239#endif
240 if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) &&
241 layer->current.keyboard_interactive) {
232 // Handle tapping a layer surface 242 // Handle tapping a layer surface
233 struct wlr_layer_surface_v1 *layer = 243 seat_set_focus_layer(seat, layer);
234 wlr_layer_surface_v1_from_wlr_surface(surface); 244 transaction_commit_dirty();
235 if (layer->current.keyboard_interactive) {
236 seat_set_focus_layer(seat, layer);
237 transaction_commit_dirty();
238 }
239 } else if (cont) { 245 } else if (cont) {
240 bool is_floating_or_child = container_is_floating_or_child(cont); 246 bool is_floating_or_child = container_is_floating_or_child(cont);
241 bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); 247 bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont);
@@ -260,20 +266,17 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
260 266
261 // Handle tapping on a container surface 267 // Handle tapping on a container surface
262 seat_set_focus_container(seat, cont); 268 seat_set_focus_container(seat, cont);
263 seatop_begin_down(seat, node->sway_container, time_msec, sx, sy); 269 seatop_begin_down(seat, node->sway_container, sx, sy);
264 } 270 }
265#if HAVE_XWAYLAND 271#if HAVE_XWAYLAND
266 // Handle tapping on an xwayland unmanaged view 272 // Handle tapping on an xwayland unmanaged view
267 else if (wlr_surface_is_xwayland_surface(surface)) { 273 else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&
268 struct wlr_xwayland_surface *xsurface = 274 xsurface->override_redirect &&
269 wlr_xwayland_surface_from_wlr_surface(surface); 275 wlr_xwayland_or_surface_wants_focus(xsurface)) {
270 if (xsurface->override_redirect && 276 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
271 wlr_xwayland_or_surface_wants_focus(xsurface)) { 277 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
272 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 278 seat_set_focus_surface(seat, xsurface->surface, false);
273 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 279 transaction_commit_dirty();
274 seat_set_focus_surface(seat, xsurface->surface, false);
275 transaction_commit_dirty();
276 }
277 } 280 }
278#endif 281#endif
279 282
@@ -287,7 +290,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
287 290
288static bool trigger_pointer_button_binding(struct sway_seat *seat, 291static bool trigger_pointer_button_binding(struct sway_seat *seat,
289 struct wlr_input_device *device, uint32_t button, 292 struct wlr_input_device *device, uint32_t button,
290 enum wlr_button_state state, uint32_t modifiers, 293 enum wl_pointer_button_state state, uint32_t modifiers,
291 bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { 294 bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) {
292 // We can reach this for non-pointer devices if we're currently emulating 295 // We can reach this for non-pointer devices if we're currently emulating
293 // pointer input for one. Emulated input should not trigger bindings. The 296 // pointer input for one. Emulated input should not trigger bindings. The
@@ -301,7 +304,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
301 char *device_identifier = device ? input_device_get_identifier(device) 304 char *device_identifier = device ? input_device_get_identifier(device)
302 : strdup("*"); 305 : strdup("*");
303 struct sway_binding *binding = NULL; 306 struct sway_binding *binding = NULL;
304 if (state == WLR_BUTTON_PRESSED) { 307 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
305 state_add_button(e, button); 308 state_add_button(e, button);
306 binding = get_active_mouse_binding(e, 309 binding = get_active_mouse_binding(e,
307 config->current_mode->mouse_bindings, modifiers, false, 310 config->current_mode->mouse_bindings, modifiers, false,
@@ -326,7 +329,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
326 329
327static void handle_button(struct sway_seat *seat, uint32_t time_msec, 330static void handle_button(struct sway_seat *seat, uint32_t time_msec,
328 struct wlr_input_device *device, uint32_t button, 331 struct wlr_input_device *device, uint32_t button,
329 enum wlr_button_state state) { 332 enum wl_pointer_button_state state) {
330 struct sway_cursor *cursor = seat->cursor; 333 struct sway_cursor *cursor = seat->cursor;
331 334
332 // Determine what's under the cursor 335 // Determine what's under the cursor
@@ -359,7 +362,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
359 362
360 // Handle clicking an empty workspace 363 // Handle clicking an empty workspace
361 if (node && node->type == N_WORKSPACE) { 364 if (node && node->type == N_WORKSPACE) {
362 if (state == WLR_BUTTON_PRESSED) { 365 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
363 seat_set_focus(seat, node); 366 seat_set_focus(seat, node);
364 transaction_commit_dirty(); 367 transaction_commit_dirty();
365 } 368 }
@@ -367,16 +370,15 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
367 return; 370 return;
368 } 371 }
369 372
370 // Handle clicking a layer surface 373 // Handle clicking a layer surface and its popups/subsurfaces
371 if (surface && wlr_surface_is_layer_surface(surface)) { 374 struct wlr_layer_surface_v1 *layer = NULL;
372 struct wlr_layer_surface_v1 *layer = 375 if ((layer = toplevel_layer_surface_from_surface(surface))) {
373 wlr_layer_surface_v1_from_wlr_surface(surface);
374 if (layer->current.keyboard_interactive) { 376 if (layer->current.keyboard_interactive) {
375 seat_set_focus_layer(seat, layer); 377 seat_set_focus_layer(seat, layer);
376 transaction_commit_dirty(); 378 transaction_commit_dirty();
377 } 379 }
378 if (state == WLR_BUTTON_PRESSED) { 380 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
379 seatop_begin_down_on_surface(seat, surface, time_msec, sx, sy); 381 seatop_begin_down_on_surface(seat, surface, sx, sy);
380 } 382 }
381 seat_pointer_notify_button(seat, time_msec, button, state); 383 seat_pointer_notify_button(seat, time_msec, button, state);
382 return; 384 return;
@@ -384,7 +386,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
384 386
385 // Handle tiling resize via border 387 // Handle tiling resize via border
386 if (cont && resize_edge && button == BTN_LEFT && 388 if (cont && resize_edge && button == BTN_LEFT &&
387 state == WLR_BUTTON_PRESSED && !is_floating) { 389 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) {
388 // If a resize is triggered on a tabbed or stacked container, change 390 // If a resize is triggered on a tabbed or stacked container, change
389 // focus to the tab which already had inactive focus -- otherwise, we'd 391 // focus to the tab which already had inactive focus -- otherwise, we'd
390 // change the active tab when the user probably just wanted to resize. 392 // change the active tab when the user probably just wanted to resize.
@@ -402,7 +404,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
402 // Handle tiling resize via mod 404 // Handle tiling resize via mod
403 bool mod_pressed = modifiers & config->floating_mod; 405 bool mod_pressed = modifiers & config->floating_mod;
404 if (cont && !is_floating_or_child && mod_pressed && 406 if (cont && !is_floating_or_child && mod_pressed &&
405 state == WLR_BUTTON_PRESSED) { 407 state == WL_POINTER_BUTTON_STATE_PRESSED) {
406 uint32_t btn_resize = config->floating_mod_inverse ? 408 uint32_t btn_resize = config->floating_mod_inverse ?
407 BTN_LEFT : BTN_RIGHT; 409 BTN_LEFT : BTN_RIGHT;
408 if (button == btn_resize) { 410 if (button == btn_resize) {
@@ -429,13 +431,31 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
429 } 431 }
430 } 432 }
431 433
434 // Handle changing focus when clicking on a container
435 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
436 // Default case: focus the container that was just clicked.
437 node = &cont->node;
438
439 // If the container is a tab/stacked container and the click happened
440 // on a tab, switch to the tab. If the tab contents were already
441 // focused, focus the tab container itself. If the tab container was
442 // already focused, cycle back to focusing the tab contents.
443 if (on_titlebar) {
444 struct sway_container *focus = seat_get_focused_container(seat);
445 if (focus == cont || !container_has_ancestor(focus, cont)) {
446 node = seat_get_focus_inactive(seat, &cont->node);
447 }
448 }
449
450 seat_set_focus(seat, node);
451 transaction_commit_dirty();
452 }
453
432 // Handle beginning floating move 454 // Handle beginning floating move
433 if (cont && is_floating_or_child && !is_fullscreen_or_child && 455 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
434 state == WLR_BUTTON_PRESSED) { 456 state == WL_POINTER_BUTTON_STATE_PRESSED) {
435 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; 457 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
436 if (button == btn_move && (mod_pressed || on_titlebar)) { 458 if (button == btn_move && (mod_pressed || on_titlebar)) {
437 seat_set_focus_container(seat,
438 seat_get_focus_inactive_view(seat, &cont->node));
439 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); 459 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont));
440 return; 460 return;
441 } 461 }
@@ -443,9 +463,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
443 463
444 // Handle beginning floating resize 464 // Handle beginning floating resize
445 if (cont && is_floating_or_child && !is_fullscreen_or_child && 465 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
446 state == WLR_BUTTON_PRESSED) { 466 state == WL_POINTER_BUTTON_STATE_PRESSED) {
447 // Via border 467 // Via border
448 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { 468 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
469 seat_set_focus_container(seat, cont);
449 seatop_begin_resize_floating(seat, cont, resize_edge); 470 seatop_begin_resize_floating(seat, cont, resize_edge);
450 return; 471 return;
451 } 472 }
@@ -460,6 +481,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
460 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 481 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
461 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? 482 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ?
462 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 483 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
484 seat_set_focus_container(seat, floater);
463 seatop_begin_resize_floating(seat, floater, edge); 485 seatop_begin_resize_floating(seat, floater, edge);
464 return; 486 return;
465 } 487 }
@@ -467,55 +489,43 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
467 489
468 // Handle moving a tiling container 490 // Handle moving a tiling container
469 if (config->tiling_drag && (mod_pressed || on_titlebar) && 491 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
470 state == WLR_BUTTON_PRESSED && !is_floating_or_child && 492 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child &&
471 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { 493 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
472 struct sway_container *focus = seat_get_focused_container(seat);
473 bool focused = focus == cont || container_has_ancestor(focus, cont);
474 if (on_titlebar && !focused) {
475 node = seat_get_focus_inactive(seat, &cont->node);
476 seat_set_focus(seat, node);
477 }
478
479 // If moving a container by its title bar, use a threshold for the drag 494 // If moving a container by its title bar, use a threshold for the drag
480 if (!mod_pressed && config->tiling_drag_threshold > 0) { 495 if (!mod_pressed && config->tiling_drag_threshold > 0) {
481 seatop_begin_move_tiling_threshold(seat, cont); 496 seatop_begin_move_tiling_threshold(seat, cont);
482 } else { 497 } else {
483 seatop_begin_move_tiling(seat, cont); 498 seatop_begin_move_tiling(seat, cont);
484 } 499 }
500
485 return; 501 return;
486 } 502 }
487 503
488 // Handle mousedown on a container surface 504 // Handle mousedown on a container surface
489 if (surface && cont && state == WLR_BUTTON_PRESSED) { 505 if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
490 seat_set_focus_container(seat, cont); 506 seatop_begin_down(seat, cont, sx, sy);
491 seatop_begin_down(seat, cont, time_msec, sx, sy); 507 seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED);
492 seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED);
493 return; 508 return;
494 } 509 }
495 510
496 // Handle clicking a container surface or decorations 511 // Handle clicking a container surface or decorations
497 if (cont && state == WLR_BUTTON_PRESSED) { 512 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
498 node = seat_get_focus_inactive(seat, &cont->node);
499 seat_set_focus(seat, node);
500 transaction_commit_dirty();
501 seat_pointer_notify_button(seat, time_msec, button, state); 513 seat_pointer_notify_button(seat, time_msec, button, state);
502 return; 514 return;
503 } 515 }
504 516
505#if HAVE_XWAYLAND 517#if HAVE_XWAYLAND
506 // Handle clicking on xwayland unmanaged view 518 // Handle clicking on xwayland unmanaged view
507 if (surface && wlr_surface_is_xwayland_surface(surface)) { 519 struct wlr_xwayland_surface *xsurface;
508 struct wlr_xwayland_surface *xsurface = 520 if (surface &&
509 wlr_xwayland_surface_from_wlr_surface(surface); 521 (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&
510 if (xsurface->override_redirect && 522 xsurface->override_redirect &&
511 wlr_xwayland_or_surface_wants_focus(xsurface)) { 523 wlr_xwayland_or_surface_wants_focus(xsurface)) {
512 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 524 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
513 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 525 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
514 seat_set_focus_surface(seat, xsurface->surface, false); 526 seat_set_focus_surface(seat, xsurface->surface, false);
515 transaction_commit_dirty(); 527 transaction_commit_dirty();
516 seat_pointer_notify_button(seat, time_msec, button, state); 528 seat_pointer_notify_button(seat, time_msec, button, state);
517 return;
518 }
519 } 529 }
520#endif 530#endif
521 531
@@ -538,6 +548,21 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
538 if (wlr_output == NULL) { 548 if (wlr_output == NULL) {
539 return; 549 return;
540 } 550 }
551
552 struct wlr_surface *surface = NULL;
553 double sx, sy;
554 node_at_coords(seat, seat->cursor->cursor->x, seat->cursor->cursor->y,
555 &surface, &sx, &sy);
556
557 // Focus topmost layer surface
558 struct wlr_layer_surface_v1 *layer = NULL;
559 if ((layer = toplevel_layer_surface_from_surface(surface)) &&
560 layer->current.keyboard_interactive) {
561 seat_set_focus_layer(seat, layer);
562 transaction_commit_dirty();
563 return;
564 }
565
541 struct sway_output *hovered_output = wlr_output->data; 566 struct sway_output *hovered_output = wlr_output->data;
542 if (focus && hovered_output != node_get_output(focus)) { 567 if (focus && hovered_output != node_get_output(focus)) {
543 struct sway_workspace *ws = output_get_active_workspace(hovered_output); 568 struct sway_workspace *ws = output_get_active_workspace(hovered_output);
@@ -598,12 +623,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
598 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 623 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
599 } 624 }
600 625
601 struct sway_drag_icon *drag_icon; 626 drag_icons_update_position(seat);
602 wl_list_for_each(drag_icon, &root->drag_icons, link) {
603 if (drag_icon->seat == seat) {
604 drag_icon_update_position(drag_icon);
605 }
606 }
607 627
608 e->previous_node = node; 628 e->previous_node = node;
609} 629}
@@ -633,25 +653,50 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
633 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); 653 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool);
634 } 654 }
635 655
636 struct sway_drag_icon *drag_icon; 656 drag_icons_update_position(seat);
637 wl_list_for_each(drag_icon, &root->drag_icons, link) {
638 if (drag_icon->seat == seat) {
639 drag_icon_update_position(drag_icon);
640 }
641 }
642 657
643 e->previous_node = node; 658 e->previous_node = node;
644} 659}
645 660
661static void handle_touch_down(struct sway_seat *seat,
662 struct wlr_touch_down_event *event, double lx, double ly) {
663 struct wlr_surface *surface = NULL;
664 struct wlr_seat *wlr_seat = seat->wlr_seat;
665 struct sway_cursor *cursor = seat->cursor;
666 double sx, sy;
667 node_at_coords(seat, seat->touch_x, seat->touch_y, &surface, &sx, &sy);
668
669 if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) {
670 if (seat_is_input_allowed(seat, surface)) {
671 cursor->simulating_pointer_from_touch = false;
672 seatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly);
673 }
674 } else if (!cursor->simulating_pointer_from_touch &&
675 (!surface || seat_is_input_allowed(seat, surface))) {
676 // Fallback to cursor simulation.
677 // The pointer_touch_id state is needed, so drags are not aborted when over
678 // a surface supporting touch and multi touch events don't interfere.
679 cursor->simulating_pointer_from_touch = true;
680 cursor->pointer_touch_id = seat->touch_id;
681 double dx, dy;
682 dx = seat->touch_x - cursor->cursor->x;
683 dy = seat->touch_y - cursor->cursor->y;
684 pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy,
685 dx, dy);
686 dispatch_cursor_button(cursor, &event->touch->base, event->time_msec,
687 BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
688 }
689}
690
646/*----------------------------------------\ 691/*----------------------------------------\
647 * Functions used by handle_pointer_axis / 692 * Functions used by handle_pointer_axis /
648 *--------------------------------------*/ 693 *--------------------------------------*/
649 694
650static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { 695static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) {
651 switch (event->orientation) { 696 switch (event->orientation) {
652 case WLR_AXIS_ORIENTATION_VERTICAL: 697 case WL_POINTER_AXIS_VERTICAL_SCROLL:
653 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; 698 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
654 case WLR_AXIS_ORIENTATION_HORIZONTAL: 699 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
655 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; 700 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
656 default: 701 default:
657 sway_log(SWAY_DEBUG, "Unknown axis orientation"); 702 sway_log(SWAY_DEBUG, "Unknown axis orientation");
@@ -708,6 +753,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
708 753
709 // Scrolling on a tabbed or stacked title bar (handled as press event) 754 // Scrolling on a tabbed or stacked title bar (handled as press event)
710 if (!handled && (on_titlebar || on_titlebar_border)) { 755 if (!handled && (on_titlebar || on_titlebar_border)) {
756 struct sway_node *new_focus;
711 enum sway_container_layout layout = container_parent_layout(cont); 757 enum sway_container_layout layout = container_parent_layout(cont);
712 if (layout == L_TABBED || layout == L_STACKED) { 758 if (layout == L_TABBED || layout == L_STACKED) {
713 struct sway_node *tabcontainer = node_get_parent(node); 759 struct sway_node *tabcontainer = node_get_parent(node);
@@ -715,7 +761,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
715 seat_get_active_tiling_child(seat, tabcontainer); 761 seat_get_active_tiling_child(seat, tabcontainer);
716 list_t *siblings = container_get_siblings(cont); 762 list_t *siblings = container_get_siblings(cont);
717 int desired = list_find(siblings, active->sway_container) + 763 int desired = list_find(siblings, active->sway_container) +
718 round(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); 764 roundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP);
719 if (desired < 0) { 765 if (desired < 0) {
720 desired = 0; 766 desired = 0;
721 } else if (desired >= siblings->length) { 767 } else if (desired >= siblings->length) {
@@ -724,14 +770,16 @@ static void handle_pointer_axis(struct sway_seat *seat,
724 770
725 struct sway_container *new_sibling_con = siblings->items[desired]; 771 struct sway_container *new_sibling_con = siblings->items[desired];
726 struct sway_node *new_sibling = &new_sibling_con->node; 772 struct sway_node *new_sibling = &new_sibling_con->node;
727 struct sway_node *new_focus =
728 seat_get_focus_inactive(seat, new_sibling);
729 // Use the focused child of the tabbed/stacked container, not the 773 // Use the focused child of the tabbed/stacked container, not the
730 // container the user scrolled on. 774 // container the user scrolled on.
731 seat_set_focus(seat, new_focus); 775 new_focus = seat_get_focus_inactive(seat, new_sibling);
732 transaction_commit_dirty(); 776 } else {
733 handled = true; 777 new_focus = seat_get_focus_inactive(seat, &cont->node);
734 } 778 }
779
780 seat_set_focus(seat, new_focus);
781 transaction_commit_dirty();
782 handled = true;
735 } 783 }
736 784
737 // Handle mouse bindings - x11 mouse buttons 4-7 - release event 785 // Handle mouse bindings - x11 mouse buttons 4-7 - release event
@@ -747,8 +795,9 @@ static void handle_pointer_axis(struct sway_seat *seat,
747 795
748 if (!handled) { 796 if (!handled) {
749 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, 797 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
750 event->orientation, scroll_factor * event->delta, 798 event->orientation, scroll_factor * event->delta,
751 round(scroll_factor * event->delta_discrete), event->source); 799 roundf(scroll_factor * event->delta_discrete), event->source,
800 event->relative_direction);
752 } 801 }
753} 802}
754 803
@@ -894,7 +943,7 @@ static void handle_hold_begin(struct sway_seat *seat,
894 // ... otherwise forward to client 943 // ... otherwise forward to client
895 struct sway_cursor *cursor = seat->cursor; 944 struct sway_cursor *cursor = seat->cursor;
896 wlr_pointer_gestures_v1_send_hold_begin( 945 wlr_pointer_gestures_v1_send_hold_begin(
897 cursor->pointer_gestures, cursor->seat->wlr_seat, 946 server.input->pointer_gestures, cursor->seat->wlr_seat,
898 event->time_msec, event->fingers); 947 event->time_msec, event->fingers);
899 } 948 }
900} 949}
@@ -906,7 +955,7 @@ static void handle_hold_end(struct sway_seat *seat,
906 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) { 955 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) {
907 struct sway_cursor *cursor = seat->cursor; 956 struct sway_cursor *cursor = seat->cursor;
908 wlr_pointer_gestures_v1_send_hold_end( 957 wlr_pointer_gestures_v1_send_hold_end(
909 cursor->pointer_gestures, cursor->seat->wlr_seat, 958 server.input->pointer_gestures, cursor->seat->wlr_seat,
910 event->time_msec, event->cancelled); 959 event->time_msec, event->cancelled);
911 return; 960 return;
912 } 961 }
@@ -939,7 +988,7 @@ static void handle_pinch_begin(struct sway_seat *seat,
939 // ... otherwise forward to client 988 // ... otherwise forward to client
940 struct sway_cursor *cursor = seat->cursor; 989 struct sway_cursor *cursor = seat->cursor;
941 wlr_pointer_gestures_v1_send_pinch_begin( 990 wlr_pointer_gestures_v1_send_pinch_begin(
942 cursor->pointer_gestures, cursor->seat->wlr_seat, 991 server.input->pointer_gestures, cursor->seat->wlr_seat,
943 event->time_msec, event->fingers); 992 event->time_msec, event->fingers);
944 } 993 }
945} 994}
@@ -955,7 +1004,7 @@ static void handle_pinch_update(struct sway_seat *seat,
955 // ... otherwise forward to client 1004 // ... otherwise forward to client
956 struct sway_cursor *cursor = seat->cursor; 1005 struct sway_cursor *cursor = seat->cursor;
957 wlr_pointer_gestures_v1_send_pinch_update( 1006 wlr_pointer_gestures_v1_send_pinch_update(
958 cursor->pointer_gestures, 1007 server.input->pointer_gestures,
959 cursor->seat->wlr_seat, 1008 cursor->seat->wlr_seat,
960 event->time_msec, event->dx, event->dy, 1009 event->time_msec, event->dx, event->dy,
961 event->scale, event->rotation); 1010 event->scale, event->rotation);
@@ -969,7 +1018,7 @@ static void handle_pinch_end(struct sway_seat *seat,
969 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { 1018 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
970 struct sway_cursor *cursor = seat->cursor; 1019 struct sway_cursor *cursor = seat->cursor;
971 wlr_pointer_gestures_v1_send_pinch_end( 1020 wlr_pointer_gestures_v1_send_pinch_end(
972 cursor->pointer_gestures, cursor->seat->wlr_seat, 1021 server.input->pointer_gestures, cursor->seat->wlr_seat,
973 event->time_msec, event->cancelled); 1022 event->time_msec, event->cancelled);
974 return; 1023 return;
975 } 1024 }
@@ -1002,7 +1051,7 @@ static void handle_swipe_begin(struct sway_seat *seat,
1002 // ... otherwise forward to client 1051 // ... otherwise forward to client
1003 struct sway_cursor *cursor = seat->cursor; 1052 struct sway_cursor *cursor = seat->cursor;
1004 wlr_pointer_gestures_v1_send_swipe_begin( 1053 wlr_pointer_gestures_v1_send_swipe_begin(
1005 cursor->pointer_gestures, cursor->seat->wlr_seat, 1054 server.input->pointer_gestures, cursor->seat->wlr_seat,
1006 event->time_msec, event->fingers); 1055 event->time_msec, event->fingers);
1007 } 1056 }
1008} 1057}
@@ -1019,7 +1068,7 @@ static void handle_swipe_update(struct sway_seat *seat,
1019 // ... otherwise forward to client 1068 // ... otherwise forward to client
1020 struct sway_cursor *cursor = seat->cursor; 1069 struct sway_cursor *cursor = seat->cursor;
1021 wlr_pointer_gestures_v1_send_swipe_update( 1070 wlr_pointer_gestures_v1_send_swipe_update(
1022 cursor->pointer_gestures, cursor->seat->wlr_seat, 1071 server.input->pointer_gestures, cursor->seat->wlr_seat,
1023 event->time_msec, event->dx, event->dy); 1072 event->time_msec, event->dx, event->dy);
1024 } 1073 }
1025} 1074}
@@ -1030,7 +1079,7 @@ static void handle_swipe_end(struct sway_seat *seat,
1030 struct seatop_default_event *seatop = seat->seatop_data; 1079 struct seatop_default_event *seatop = seat->seatop_data;
1031 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { 1080 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
1032 struct sway_cursor *cursor = seat->cursor; 1081 struct sway_cursor *cursor = seat->cursor;
1033 wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures, 1082 wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures,
1034 cursor->seat->wlr_seat, event->time_msec, event->cancelled); 1083 cursor->seat->wlr_seat, event->time_msec, event->cancelled);
1035 return; 1084 return;
1036 } 1085 }
@@ -1087,6 +1136,7 @@ static const struct sway_seatop_impl seatop_impl = {
1087 .swipe_begin = handle_swipe_begin, 1136 .swipe_begin = handle_swipe_begin,
1088 .swipe_update = handle_swipe_update, 1137 .swipe_update = handle_swipe_update,
1089 .swipe_end = handle_swipe_end, 1138 .swipe_end = handle_swipe_end,
1139 .touch_down = handle_touch_down,
1090 .rebase = handle_rebase, 1140 .rebase = handle_rebase,
1091 .allow_set_cursor = true, 1141 .allow_set_cursor = true,
1092}; 1142};
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
index b40773d0..340e334b 100644
--- a/sway/input/seatop_down.c
+++ b/sway/input/seatop_down.c
@@ -1,13 +1,20 @@
1#define _POSIX_C_SOURCE 200809L
2#include <float.h> 1#include <float.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_tablet_v2.h> 3#include <wlr/types/wlr_tablet_v2.h>
4#include <wlr/types/wlr_touch.h>
5#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 6#include "sway/input/seat.h"
7#include "sway/tree/view.h" 7#include "sway/tree/view.h"
8#include "sway/desktop/transaction.h" 8#include "sway/desktop/transaction.h"
9#include "log.h" 9#include "log.h"
10 10
11struct 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
11struct seatop_down_event { 18struct seatop_down_event {
12 struct sway_container *con; 19 struct sway_container *con;
13 struct sway_seat *seat; 20 struct sway_seat *seat;
@@ -15,8 +22,113 @@ struct seatop_down_event {
15 struct wlr_surface *surface; 22 struct wlr_surface *surface;
16 double ref_lx, ref_ly; // cursor's x/y at start of op 23 double ref_lx, ref_ly; // cursor's x/y at start of op
17 double ref_con_lx, ref_con_ly; // container's x/y at start of op 24 double ref_con_lx, ref_con_ly; // container's x/y at start of op
25 struct wl_list point_events; // seatop_touch_point_event::link
18}; 26};
19 27
28static 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
53static 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
73static 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
106static 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
20static void handle_pointer_axis(struct sway_seat *seat, 132static void handle_pointer_axis(struct sway_seat *seat,
21 struct wlr_pointer_axis_event *event) { 133 struct wlr_pointer_axis_event *event) {
22 struct sway_input_device *input_device = 134 struct sway_input_device *input_device =
@@ -28,12 +140,13 @@ static void handle_pointer_axis(struct sway_seat *seat,
28 140
29 wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, 141 wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec,
30 event->orientation, scroll_factor * event->delta, 142 event->orientation, scroll_factor * event->delta,
31 round(scroll_factor * event->delta_discrete), event->source); 143 roundf(scroll_factor * event->delta_discrete), event->source,
144 event->relative_direction);
32} 145}
33 146
34static void handle_button(struct sway_seat *seat, uint32_t time_msec, 147static void handle_button(struct sway_seat *seat, uint32_t time_msec,
35 struct wlr_input_device *device, uint32_t button, 148 struct wlr_input_device *device, uint32_t button,
36 enum wlr_button_state state) { 149 enum wl_pointer_button_state state) {
37 seat_pointer_notify_button(seat, time_msec, button, state); 150 seat_pointer_notify_button(seat, time_msec, button, state);
38 151
39 if (seat->cursor->pressed_button_count == 0) { 152 if (seat->cursor->pressed_button_count == 0) {
@@ -99,14 +212,18 @@ static const struct sway_seatop_impl seatop_impl = {
99 .pointer_axis = handle_pointer_axis, 212 .pointer_axis = handle_pointer_axis,
100 .tablet_tool_tip = handle_tablet_tool_tip, 213 .tablet_tool_tip = handle_tablet_tool_tip,
101 .tablet_tool_motion = handle_tablet_tool_motion, 214 .tablet_tool_motion = handle_tablet_tool_motion,
215 .touch_motion = handle_touch_motion,
216 .touch_up = handle_touch_up,
217 .touch_down = handle_touch_down,
218 .touch_cancel = handle_touch_cancel,
102 .unref = handle_unref, 219 .unref = handle_unref,
103 .end = handle_end, 220 .end = handle_end,
104 .allow_set_cursor = true, 221 .allow_set_cursor = true,
105}; 222};
106 223
107void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 224void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
108 uint32_t time_msec, double sx, double sy) { 225 double sx, double sy) {
109 seatop_begin_down_on_surface(seat, con->view->surface, time_msec, sx, sy); 226 seatop_begin_down_on_surface(seat, con->view->surface, sx, sy);
110 struct seatop_down_event *e = seat->seatop_data; 227 struct seatop_down_event *e = seat->seatop_data;
111 e->con = con; 228 e->con = con;
112 229
@@ -114,13 +231,20 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
114 transaction_commit_dirty(); 231 transaction_commit_dirty();
115} 232}
116 233
234void 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
117void seatop_begin_down_on_surface(struct sway_seat *seat, 241void seatop_begin_down_on_surface(struct sway_seat *seat,
118 struct wlr_surface *surface, uint32_t time_msec, double sx, double sy) { 242 struct wlr_surface *surface, double sx, double sy) {
119 seatop_end(seat); 243 seatop_end(seat);
120 244
121 struct seatop_down_event *e = 245 struct seatop_down_event *e =
122 calloc(1, sizeof(struct seatop_down_event)); 246 calloc(1, sizeof(struct seatop_down_event));
123 if (!e) { 247 if (!sway_assert(e, "Unable to allocate e")) {
124 return; 248 return;
125 } 249 }
126 e->con = NULL; 250 e->con = NULL;
@@ -132,6 +256,7 @@ void seatop_begin_down_on_surface(struct sway_seat *seat,
132 e->ref_ly = seat->cursor->cursor->y; 256 e->ref_ly = seat->cursor->cursor->y;
133 e->ref_con_lx = sx; 257 e->ref_con_lx = sx;
134 e->ref_con_ly = sy; 258 e->ref_con_ly = sy;
259 wl_list_init(&e->point_events);
135 260
136 seat->seatop_impl = &seatop_impl; 261 seat->seatop_impl = &seatop_impl;
137 seat->seatop_data = e; 262 seat->seatop_data = e;
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c
index ddcd4c53..83668d88 100644
--- a/sway/input/seatop_move_floating.c
+++ b/sway/input/seatop_move_floating.c
@@ -1,6 +1,4 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h> 1#include <wlr/types/wlr_cursor.h>
3#include "sway/desktop.h"
4#include "sway/desktop/transaction.h" 2#include "sway/desktop/transaction.h"
5#include "sway/input/cursor.h" 3#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 4#include "sway/input/seat.h"
@@ -23,7 +21,7 @@ static void finalize_move(struct sway_seat *seat) {
23 21
24static void handle_button(struct sway_seat *seat, uint32_t time_msec, 22static void handle_button(struct sway_seat *seat, uint32_t time_msec,
25 struct wlr_input_device *device, uint32_t button, 23 struct wlr_input_device *device, uint32_t button,
26 enum wlr_button_state state) { 24 enum wl_pointer_button_state state) {
27 if (seat->cursor->pressed_button_count == 0) { 25 if (seat->cursor->pressed_button_count == 0) {
28 finalize_move(seat); 26 finalize_move(seat);
29 } 27 }
@@ -39,9 +37,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
39static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { 37static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
40 struct seatop_move_floating_event *e = seat->seatop_data; 38 struct seatop_move_floating_event *e = seat->seatop_data;
41 struct wlr_cursor *cursor = seat->cursor->cursor; 39 struct wlr_cursor *cursor = seat->cursor->cursor;
42 desktop_damage_whole_container(e->con);
43 container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); 40 container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy);
44 desktop_damage_whole_container(e->con);
45 transaction_commit_dirty(); 41 transaction_commit_dirty();
46} 42}
47 43
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c
index 223c6c08..c525b77a 100644
--- a/sway/input/seatop_move_tiling.c
+++ b/sway/input/seatop_move_tiling.c
@@ -1,8 +1,6 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/util/edges.h> 3#include <wlr/util/edges.h>
5#include "sway/desktop.h"
6#include "sway/desktop/transaction.h" 4#include "sway/desktop/transaction.h"
7#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
8#include "sway/input/seat.h" 6#include "sway/input/seat.h"
@@ -24,29 +22,17 @@ struct seatop_move_tiling_event {
24 struct sway_container *con; 22 struct sway_container *con;
25 struct sway_node *target_node; 23 struct sway_node *target_node;
26 enum wlr_edges target_edge; 24 enum wlr_edges target_edge;
27 struct wlr_box drop_box;
28 double ref_lx, ref_ly; // cursor's x/y at start of op 25 double ref_lx, ref_ly; // cursor's x/y at start of op
29 bool threshold_reached; 26 bool threshold_reached;
30 bool split_target; 27 bool split_target;
31 bool insert_after_target; 28 bool insert_after_target;
29 struct wlr_scene_rect *indicator_rect;
32}; 30};
33 31
34static void handle_render(struct sway_seat *seat, 32static void handle_end(struct sway_seat *seat) {
35 struct sway_output *output, pixman_region32_t *damage) {
36 struct seatop_move_tiling_event *e = seat->seatop_data; 33 struct seatop_move_tiling_event *e = seat->seatop_data;
37 if (!e->threshold_reached) { 34 wlr_scene_node_destroy(&e->indicator_rect->node);
38 return; 35 e->indicator_rect = NULL;
39 }
40 if (e->target_node && node_get_output(e->target_node) == output) {
41 float color[4];
42 memcpy(&color, config->border_colors.focused.indicator,
43 sizeof(float) * 4);
44 premultiply_alpha(color, 0.5);
45 struct wlr_box box;
46 memcpy(&box, &e->drop_box, sizeof(struct wlr_box));
47 scale_box(&box, output->wlr_output->scale);
48 render_rect(output, damage, &box, color);
49 }
50} 36}
51 37
52static void handle_motion_prethreshold(struct sway_seat *seat) { 38static void handle_motion_prethreshold(struct sway_seat *seat) {
@@ -67,6 +53,7 @@ static void handle_motion_prethreshold(struct sway_seat *seat) {
67 53
68 // If the threshold has been exceeded, start the actual drag 54 // If the threshold has been exceeded, start the actual drag
69 if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { 55 if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) {
56 wlr_scene_node_set_enabled(&e->indicator_rect->node, true);
70 e->threshold_reached = true; 57 e->threshold_reached = true;
71 cursor_set_image(seat->cursor, "grab", NULL); 58 cursor_set_image(seat->cursor, "grab", NULL);
72 } 59 }
@@ -165,6 +152,11 @@ static bool split_titlebar(struct sway_node *node, struct sway_container *avoid,
165 return false; 152 return false;
166} 153}
167 154
155static 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
168static void handle_motion_postthreshold(struct sway_seat *seat) { 160static void handle_motion_postthreshold(struct sway_seat *seat) {
169 struct seatop_move_tiling_event *e = seat->seatop_data; 161 struct seatop_move_tiling_event *e = seat->seatop_data;
170 e->split_target = false; 162 e->split_target = false;
@@ -173,8 +165,6 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
173 struct sway_cursor *cursor = seat->cursor; 165 struct sway_cursor *cursor = seat->cursor;
174 struct sway_node *node = node_at_coords(seat, 166 struct sway_node *node = node_at_coords(seat,
175 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); 167 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
176 // Damage the old location
177 desktop_damage_box(&e->drop_box);
178 168
179 if (!node) { 169 if (!node) {
180 // Eg. hovered over a layer surface such as swaybar 170 // Eg. hovered over a layer surface such as swaybar
@@ -187,8 +177,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
187 // Empty workspace 177 // Empty workspace
188 e->target_node = node; 178 e->target_node = node;
189 e->target_edge = WLR_EDGE_NONE; 179 e->target_edge = WLR_EDGE_NONE;
190 workspace_get_box(node->sway_workspace, &e->drop_box); 180
191 desktop_damage_box(&e->drop_box); 181 struct wlr_box drop_box;
182 workspace_get_box(node->sway_workspace, &drop_box);
183 update_indicator(e, &drop_box);
192 return; 184 return;
193 } 185 }
194 186
@@ -201,11 +193,18 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
201 return; 193 return;
202 } 194 }
203 195
196 struct wlr_box drop_box = {
197 .x = con->pending.content_x,
198 .y = con->pending.content_y,
199 .width = con->pending.content_width,
200 .height = con->pending.content_height,
201 };
202
204 // Check if the cursor is over a tilebar only if the destination 203 // Check if the cursor is over a tilebar only if the destination
205 // container is not a descendant of the source container. 204 // container is not a descendant of the source container.
206 if (!surface && !container_has_ancestor(con, e->con) && 205 if (!surface && !container_has_ancestor(con, e->con) &&
207 split_titlebar(node, e->con, cursor->cursor, 206 split_titlebar(node, e->con, cursor->cursor,
208 &e->drop_box, &e->insert_after_target)) { 207 &drop_box, &e->insert_after_target)) {
209 // Don't allow dropping over the source container's titlebar 208 // Don't allow dropping over the source container's titlebar
210 // to give users a chance to cancel a drag operation. 209 // to give users a chance to cancel a drag operation.
211 if (con == e->con) { 210 if (con == e->con) {
@@ -215,6 +214,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
215 e->split_target = true; 214 e->split_target = true;
216 } 215 }
217 e->target_edge = WLR_EDGE_NONE; 216 e->target_edge = WLR_EDGE_NONE;
217 update_indicator(e, &drop_box);
218 return; 218 return;
219 } 219 }
220 220
@@ -256,8 +256,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
256 e->target_node = node_get_parent(e->target_node); 256 e->target_node = node_get_parent(e->target_node);
257 } 257 }
258 e->target_edge = edge; 258 e->target_edge = edge;
259 e->drop_box = box; 259 update_indicator(e, &box);
260 desktop_damage_box(&e->drop_box);
261 return; 260 return;
262 } 261 }
263 con = con->pending.parent; 262 con = con->pending.parent;
@@ -299,12 +298,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
299 } 298 }
300 299
301 e->target_node = node; 300 e->target_node = node;
302 e->drop_box.x = con->pending.content_x; 301 resize_box(&drop_box, e->target_edge, thickness);
303 e->drop_box.y = con->pending.content_y; 302 update_indicator(e, &drop_box);
304 e->drop_box.width = con->pending.content_width;
305 e->drop_box.height = con->pending.content_height;
306 resize_box(&e->drop_box, e->target_edge, thickness);
307 desktop_damage_box(&e->drop_box);
308} 303}
309 304
310static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { 305static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
@@ -410,7 +405,7 @@ static void finalize_move(struct sway_seat *seat) {
410 405
411static void handle_button(struct sway_seat *seat, uint32_t time_msec, 406static void handle_button(struct sway_seat *seat, uint32_t time_msec,
412 struct wlr_input_device *device, uint32_t button, 407 struct wlr_input_device *device, uint32_t button,
413 enum wlr_button_state state) { 408 enum wl_pointer_button_state state) {
414 if (seat->cursor->pressed_button_count == 0) { 409 if (seat->cursor->pressed_button_count == 0) {
415 finalize_move(seat); 410 finalize_move(seat);
416 } 411 }
@@ -439,7 +434,7 @@ static const struct sway_seatop_impl seatop_impl = {
439 .pointer_motion = handle_pointer_motion, 434 .pointer_motion = handle_pointer_motion,
440 .tablet_tool_tip = handle_tablet_tool_tip, 435 .tablet_tool_tip = handle_tablet_tool_tip,
441 .unref = handle_unref, 436 .unref = handle_unref,
442 .render = handle_render, 437 .end = handle_end,
443}; 438};
444 439
445void seatop_begin_move_tiling_threshold(struct sway_seat *seat, 440void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
@@ -451,6 +446,20 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
451 if (!e) { 446 if (!e) {
452 return; 447 return;
453 } 448 }
449
450 const float *indicator = config->border_colors.focused.indicator;
451 float color[4] = {
452 indicator[0] * .5,
453 indicator[1] * .5,
454 indicator[2] * .5,
455 indicator[3] * .5,
456 };
457 e->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, color);
458 if (!e->indicator_rect) {
459 free(e);
460 return;
461 }
462
454 e->con = con; 463 e->con = con;
455 e->ref_lx = seat->cursor->cursor->x; 464 e->ref_lx = seat->cursor->cursor->x;
456 e->ref_ly = seat->cursor->cursor->y; 465 e->ref_ly = seat->cursor->cursor->y;
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c
index df683026..bec86e33 100644
--- a/sway/input/seatop_resize_floating.c
+++ b/sway/input/seatop_resize_floating.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_xcursor_manager.h> 3#include <wlr/types/wlr_xcursor_manager.h>
@@ -21,7 +20,7 @@ struct seatop_resize_floating_event {
21 20
22static void handle_button(struct sway_seat *seat, uint32_t time_msec, 21static void handle_button(struct sway_seat *seat, uint32_t time_msec,
23 struct wlr_input_device *device, uint32_t button, 22 struct wlr_input_device *device, uint32_t button,
24 enum wlr_button_state state) { 23 enum wl_pointer_button_state state) {
25 struct seatop_resize_floating_event *e = seat->seatop_data; 24 struct seatop_resize_floating_event *e = seat->seatop_data;
26 struct sway_container *con = e->con; 25 struct sway_container *con = e->con;
27 26
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c
index 869d11b5..15fd333b 100644
--- a/sway/input/seatop_resize_tiling.c
+++ b/sway/input/seatop_resize_tiling.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h> 1#include <wlr/types/wlr_cursor.h>
3#include <wlr/util/edges.h> 2#include <wlr/util/edges.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -46,7 +45,7 @@ static struct sway_container *container_get_resize_sibling(
46 45
47static void handle_button(struct sway_seat *seat, uint32_t time_msec, 46static void handle_button(struct sway_seat *seat, uint32_t time_msec,
48 struct wlr_input_device *device, uint32_t button, 47 struct wlr_input_device *device, uint32_t button,
49 enum wlr_button_state state) { 48 enum wl_pointer_button_state state) {
50 struct seatop_resize_tiling_event *e = seat->seatop_data; 49 struct seatop_resize_tiling_event *e = seat->seatop_data;
51 50
52 if (seat->cursor->pressed_button_count == 0) { 51 if (seat->cursor->pressed_button_count == 0) {
diff --git a/sway/input/switch.c b/sway/input/switch.c
index fc7dfaff..831f4dbf 100644
--- a/sway/input/switch.c
+++ b/sway/input/switch.c
@@ -1,6 +1,5 @@
1#include "sway/config.h" 1#include "sway/config.h"
2#include "sway/input/switch.h" 2#include "sway/input/switch.h"
3#include <wlr/types/wlr_idle.h>
4#include "log.h" 3#include "log.h"
5 4
6struct sway_switch *sway_switch_create(struct sway_seat *seat, 5struct sway_switch *sway_switch_create(struct sway_seat *seat,
@@ -34,9 +33,8 @@ static bool sway_switch_trigger_test(enum sway_switch_trigger trigger,
34} 33}
35 34
36static void execute_binding(struct sway_switch *sway_switch) { 35static void execute_binding(struct sway_switch *sway_switch) {
37 struct sway_seat* seat = sway_switch->seat_device->sway_seat; 36 struct sway_seat *seat = sway_switch->seat_device->sway_seat;
38 bool input_inhibited = seat->exclusive_client != NULL || 37 bool locked = server.session_lock.lock;
39 server.session_lock.locked;
40 38
41 list_t *bindings = config->current_mode->switch_bindings; 39 list_t *bindings = config->current_mode->switch_bindings;
42 struct sway_switch_binding *matched_binding = NULL; 40 struct sway_switch_binding *matched_binding = NULL;
@@ -53,13 +51,13 @@ static void execute_binding(struct sway_switch *sway_switch) {
53 continue; 51 continue;
54 } 52 }
55 bool binding_locked = binding->flags & BINDING_LOCKED; 53 bool binding_locked = binding->flags & BINDING_LOCKED;
56 if (!binding_locked && input_inhibited) { 54 if (!binding_locked && locked) {
57 continue; 55 continue;
58 } 56 }
59 57
60 matched_binding = binding; 58 matched_binding = binding;
61 59
62 if (binding_locked == input_inhibited) { 60 if (binding_locked == locked) {
63 break; 61 break;
64 } 62 }
65 } 63 }
diff --git a/sway/input/tablet.c b/sway/input/tablet.c
index 92ede3fa..2863642a 100644
--- a/sway/input/tablet.c
+++ b/sway/input/tablet.c
@@ -1,6 +1,5 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <wlr/backend/libinput.h> 2#include <wlr/config.h>
4#include <wlr/types/wlr_tablet_v2.h> 3#include <wlr/types/wlr_tablet_v2.h>
5#include <wlr/types/wlr_tablet_tool.h> 4#include <wlr/types/wlr_tablet_tool.h>
6#include <wlr/types/wlr_tablet_pad.h> 5#include <wlr/types/wlr_tablet_pad.h>
@@ -9,6 +8,10 @@
9#include "sway/input/seat.h" 8#include "sway/input/seat.h"
10#include "sway/input/tablet.h" 9#include "sway/input/tablet.h"
11 10
11#if WLR_HAS_LIBINPUT_BACKEND
12#include <wlr/backend/libinput.h>
13#endif
14
12static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { 15static 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
92void sway_tablet_destroy(struct sway_tablet *tablet) { 95void sway_tablet_destroy(struct sway_tablet *tablet) {
@@ -287,6 +290,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
287 tablet_pad->ring.notify = handle_tablet_pad_ring; 290 tablet_pad->ring.notify = handle_tablet_pad_ring;
288 wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring); 291 wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring);
289 292
293#if WLR_HAS_LIBINPUT_BACKEND
290 /* Search for a sibling tablet */ 294 /* Search for a sibling tablet */
291 if (!wlr_input_device_is_libinput(wlr_device)) { 295 if (!wlr_input_device_is_libinput(wlr_device)) {
292 /* We can only do this on libinput devices */ 296 /* We can only do this on libinput devices */
@@ -311,6 +315,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
311 break; 315 break;
312 } 316 }
313 } 317 }
318#endif
314} 319}
315 320
316void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { 321void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) {
@@ -334,14 +339,10 @@ static void handle_pad_tablet_surface_destroy(struct wl_listener *listener,
334 struct sway_tablet_pad *tablet_pad = 339 struct sway_tablet_pad *tablet_pad =
335 wl_container_of(listener, tablet_pad, surface_destroy); 340 wl_container_of(listener, tablet_pad, surface_destroy);
336 341
337 wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, 342 sway_tablet_pad_set_focus(tablet_pad, NULL);
338 tablet_pad->current_surface);
339 wl_list_remove(&tablet_pad->surface_destroy.link);
340 wl_list_init(&tablet_pad->surface_destroy.link);
341 tablet_pad->current_surface = NULL;
342} 343}
343 344
344void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, 345void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad,
345 struct wlr_surface *surface) { 346 struct wlr_surface *surface) {
346 if (!tablet_pad || !tablet_pad->tablet) { 347 if (!tablet_pad || !tablet_pad->tablet) {
347 return; 348 return;
@@ -360,7 +361,8 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad,
360 tablet_pad->current_surface = NULL; 361 tablet_pad->current_surface = NULL;
361 } 362 }
362 363
363 if (!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { 364 if (surface == NULL ||
365 !wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) {
364 return; 366 return;
365 } 367 }
366 368
@@ -368,7 +370,6 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad,
368 tablet_pad->tablet->tablet_v2, surface); 370 tablet_pad->tablet->tablet_v2, surface);
369 371
370 tablet_pad->current_surface = surface; 372 tablet_pad->current_surface = surface;
371 wl_list_remove(&tablet_pad->surface_destroy.link);
372 tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; 373 tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy;
373 wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); 374 wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy);
374} 375}
diff --git a/sway/input/text_input.c b/sway/input/text_input.c
index 58911c2d..c38a3bb2 100644
--- a/sway/input/text_input.c
+++ b/sway/input/text_input.c
@@ -2,7 +2,14 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include "log.h" 3#include "log.h"
4#include "sway/input/seat.h" 4#include "sway/input/seat.h"
5#include "sway/scene_descriptor.h"
6#include "sway/tree/root.h"
7#include "sway/tree/view.h"
8#include "sway/output.h"
5#include "sway/input/text_input.h" 9#include "sway/input/text_input.h"
10#include "sway/input/text_input_popup.h"
11#include "sway/layers.h"
12static void input_popup_update(struct sway_input_popup *popup);
6 13
7static struct sway_text_input *relay_get_focusable_text_input( 14static struct sway_text_input *relay_get_focusable_text_input(
8 struct sway_input_method_relay *relay) { 15 struct sway_input_method_relay *relay) {
@@ -102,6 +109,10 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) {
102 input_method_destroy); 109 input_method_destroy);
103 struct wlr_input_method_v2 *context = data; 110 struct wlr_input_method_v2 *context = data;
104 assert(context == relay->input_method); 111 assert(context == relay->input_method);
112 wl_list_remove(&relay->input_method_commit.link);
113 wl_list_remove(&relay->input_method_grab_keyboard.link);
114 wl_list_remove(&relay->input_method_destroy.link);
115 wl_list_remove(&relay->input_method_new_popup_surface.link);
105 relay->input_method = NULL; 116 relay->input_method = NULL;
106 struct sway_text_input *text_input = relay_get_focused_text_input(relay); 117 struct sway_text_input *text_input = relay_get_focused_text_input(relay);
107 if (text_input) { 118 if (text_input) {
@@ -133,6 +144,11 @@ static void relay_send_im_state(struct sway_input_method_relay *relay,
133 input->current.content_type.hint, 144 input->current.content_type.hint,
134 input->current.content_type.purpose); 145 input->current.content_type.purpose);
135 } 146 }
147 struct sway_input_popup *popup;
148 wl_list_for_each(popup, &relay->input_popups, link) {
149 // send_text_input_rectangle is called in this function
150 input_popup_update(popup);
151 }
136 wlr_input_method_v2_send_done(input_method); 152 wlr_input_method_v2_send_done(input_method);
137 // TODO: pass intent, display popup size 153 // TODO: pass intent, display popup size
138} 154}
@@ -255,6 +271,211 @@ static void relay_handle_text_input(struct wl_listener *listener,
255 sway_text_input_create(relay, wlr_text_input); 271 sway_text_input_create(relay, wlr_text_input);
256} 272}
257 273
274static 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
395static 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
417static 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
428static 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
435static 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
442static 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
470static 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
258static void relay_handle_input_method(struct wl_listener *listener, 479static void relay_handle_input_method(struct wl_listener *listener,
259 void *data) { 480 void *data) {
260 struct sway_input_method_relay *relay = wl_container_of(listener, relay, 481 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
@@ -280,10 +501,13 @@ static void relay_handle_input_method(struct wl_listener *listener,
280 wl_signal_add(&relay->input_method->events.destroy, 501 wl_signal_add(&relay->input_method->events.destroy,
281 &relay->input_method_destroy); 502 &relay->input_method_destroy);
282 relay->input_method_destroy.notify = handle_im_destroy; 503 relay->input_method_destroy.notify = handle_im_destroy;
504 wl_signal_add(&relay->input_method->events.new_popup_surface,
505 &relay->input_method_new_popup_surface);
506 relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface;
283 507
284 struct sway_text_input *text_input = relay_get_focusable_text_input(relay); 508 struct sway_text_input *text_input = relay_get_focusable_text_input(relay);
285 if (text_input) { 509 if (text_input) {
286 wlr_text_input_v3_send_enter(text_input->input, 510 text_input_send_enter(text_input,
287 text_input->pending_focused_surface); 511 text_input->pending_focused_surface);
288 text_input_set_pending_focused_surface(text_input, NULL); 512 text_input_set_pending_focused_surface(text_input, NULL);
289 } 513 }
@@ -293,6 +517,7 @@ void sway_input_method_relay_init(struct sway_seat *seat,
293 struct sway_input_method_relay *relay) { 517 struct sway_input_method_relay *relay) {
294 relay->seat = seat; 518 relay->seat = seat;
295 wl_list_init(&relay->text_inputs); 519 wl_list_init(&relay->text_inputs);
520 wl_list_init(&relay->input_popups);
296 521
297 relay->text_input_new.notify = relay_handle_text_input; 522 relay->text_input_new.notify = relay_handle_text_input;
298 wl_signal_add(&server.text_input->events.text_input, 523 wl_signal_add(&server.text_input->events.text_input,
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index e422b24d..81ca3483 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -3,7 +3,8 @@
3#include <json.h> 3#include <json.h>
4#include <libevdev/libevdev.h> 4#include <libevdev/libevdev.h>
5#include <stdio.h> 5#include <stdio.h>
6#include <wlr/backend/libinput.h> 6#include <wlr/config.h>
7#include <wlr/types/wlr_content_type_v1.h>
7#include <wlr/types/wlr_output.h> 8#include <wlr/types/wlr_output.h>
8#include <xkbcommon/xkbcommon.h> 9#include <xkbcommon/xkbcommon.h>
9#include "config.h" 10#include "config.h"
@@ -20,6 +21,10 @@
20#include "wlr-layer-shell-unstable-v1-protocol.h" 21#include "wlr-layer-shell-unstable-v1-protocol.h"
21#include "sway/desktop/idle_inhibit_v1.h" 22#include "sway/desktop/idle_inhibit_v1.h"
22 23
24#if WLR_HAS_LIBINPUT_BACKEND
25#include <wlr/backend/libinput.h>
26#endif
27
23static const int i3_output_id = INT32_MAX; 28static const int i3_output_id = INT32_MAX;
24static const int i3_scratch_id = INT32_MAX - 1; 29static const int i3_scratch_id = INT32_MAX - 1;
25 30
@@ -112,12 +117,43 @@ static const char *ipc_json_output_adaptive_sync_status_description(
112 return "disabled"; 117 return "disabled";
113 case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: 118 case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED:
114 return "enabled"; 119 return "enabled";
115 case WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN:
116 return "unknown";
117 } 120 }
118 return NULL; 121 return NULL;
119} 122}
120 123
124static 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
141static json_object *ipc_json_output_mode_description(
142 const struct wlr_output_mode *mode) {
143 const char *pic_ar =
144 ipc_json_output_mode_aspect_ratio_description(mode->picture_aspect_ratio);
145 json_object *mode_object = json_object_new_object();
146 json_object_object_add(mode_object, "width",
147 json_object_new_int(mode->width));
148 json_object_object_add(mode_object, "height",
149 json_object_new_int(mode->height));
150 json_object_object_add(mode_object, "refresh",
151 json_object_new_int(mode->refresh));
152 json_object_object_add(mode_object, "picture_aspect_ratio",
153 json_object_new_string(pic_ar));
154 return mode_object;
155}
156
121#if HAVE_XWAYLAND 157#if HAVE_XWAYLAND
122static const char *ipc_json_xwindow_type_description(struct sway_view *view) { 158static const char *ipc_json_xwindow_type_description(struct sway_view *view) {
123 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; 159 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
@@ -170,6 +206,20 @@ static const char *ipc_json_user_idle_inhibitor_description(enum sway_idle_inhib
170 return NULL; 206 return NULL;
171} 207}
172 208
209static 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
173json_object *ipc_json_get_version(void) { 223json_object *ipc_json_get_version(void) {
174 int major = 0, minor = 0, patch = 0; 224 int major = 0, minor = 0, patch = 0;
175 json_object *version = json_object_new_object(); 225 json_object *version = json_object_new_object();
@@ -238,14 +288,13 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name,
238 json_object_object_add(object, "focus", focus); 288 json_object_object_add(object, "focus", focus);
239 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); 289 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0));
240 json_object_object_add(object, "sticky", json_object_new_boolean(false)); 290 json_object_object_add(object, "sticky", json_object_new_boolean(false));
291 json_object_object_add(object, "floating", NULL);
292 json_object_object_add(object, "scratchpad_state", NULL);
241 293
242 return object; 294 return object;
243} 295}
244 296
245static void ipc_json_describe_output(struct sway_output *output, 297static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) {
246 json_object *object) {
247 struct wlr_output *wlr_output = output->wlr_output;
248
249 json_object_object_add(object, "primary", json_object_new_boolean(false)); 298 json_object_object_add(object, "primary", json_object_new_boolean(false));
250 json_object_object_add(object, "make", 299 json_object_object_add(object, "make",
251 json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); 300 json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown"));
@@ -269,11 +318,17 @@ static void ipc_json_describe_output(struct sway_output *output,
269 json_object_object_add(object, "modes", modes_array); 318 json_object_object_add(object, "modes", modes_array);
270} 319}
271 320
321static void ipc_json_describe_output(struct sway_output *output,
322 json_object *object) {
323 ipc_json_describe_wlr_output(output->wlr_output, object);
324}
325
272static void ipc_json_describe_enabled_output(struct sway_output *output, 326static void ipc_json_describe_enabled_output(struct sway_output *output,
273 json_object *object) { 327 json_object *object) {
274 ipc_json_describe_output(output, object); 328 ipc_json_describe_output(output, object);
275 329
276 struct wlr_output *wlr_output = output->wlr_output; 330 struct wlr_output *wlr_output = output->wlr_output;
331 json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
277 json_object_object_add(object, "active", json_object_new_boolean(true)); 332 json_object_object_add(object, "active", json_object_new_boolean(true));
278 json_object_object_add(object, "dpms", 333 json_object_object_add(object, "dpms",
279 json_object_new_boolean(wlr_output->enabled)); 334 json_object_new_boolean(wlr_output->enabled));
@@ -307,25 +362,26 @@ static void ipc_json_describe_enabled_output(struct sway_output *output,
307 json_object *modes_array = json_object_new_array(); 362 json_object *modes_array = json_object_new_array();
308 struct wlr_output_mode *mode; 363 struct wlr_output_mode *mode;
309 wl_list_for_each(mode, &wlr_output->modes, link) { 364 wl_list_for_each(mode, &wlr_output->modes, link) {
310 json_object *mode_object = json_object_new_object(); 365 json_object *mode_object =
311 json_object_object_add(mode_object, "width", 366 ipc_json_output_mode_description(mode);
312 json_object_new_int(mode->width));
313 json_object_object_add(mode_object, "height",
314 json_object_new_int(mode->height));
315 json_object_object_add(mode_object, "refresh",
316 json_object_new_int(mode->refresh));
317 json_object_array_add(modes_array, mode_object); 367 json_object_array_add(modes_array, mode_object);
318 } 368 }
319 369
320 json_object_object_add(object, "modes", modes_array); 370 json_object_object_add(object, "modes", modes_array);
321 371
322 json_object *current_mode_object = json_object_new_object(); 372 json_object *current_mode_object;
323 json_object_object_add(current_mode_object, "width", 373 if (wlr_output->current_mode != NULL) {
324 json_object_new_int(wlr_output->width)); 374 current_mode_object =
325 json_object_object_add(current_mode_object, "height", 375 ipc_json_output_mode_description(wlr_output->current_mode);
326 json_object_new_int(wlr_output->height)); 376 } else {
327 json_object_object_add(current_mode_object, "refresh", 377 current_mode_object = json_object_new_object();
328 json_object_new_int(wlr_output->refresh)); 378 json_object_object_add(current_mode_object, "width",
379 json_object_new_int(wlr_output->width));
380 json_object_object_add(current_mode_object, "height",
381 json_object_new_int(wlr_output->height));
382 json_object_object_add(current_mode_object, "refresh",
383 json_object_new_int(wlr_output->refresh));
384 }
329 json_object_object_add(object, "current_mode", current_mode_object); 385 json_object_object_add(object, "current_mode", current_mode_object);
330 386
331 struct sway_node *parent = node_get_parent(&output->node); 387 struct sway_node *parent = node_get_parent(&output->node);
@@ -351,6 +407,7 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
351 407
352 ipc_json_describe_output(output, object); 408 ipc_json_describe_output(output, object);
353 409
410 json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
354 json_object_object_add(object, "type", json_object_new_string("output")); 411 json_object_object_add(object, "type", json_object_new_string("output"));
355 json_object_object_add(object, "name", 412 json_object_object_add(object, "name",
356 json_object_new_string(wlr_output->name)); 413 json_object_new_string(wlr_output->name));
@@ -372,6 +429,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
372 return object; 429 return object;
373} 430}
374 431
432json_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
375static json_object *ipc_json_describe_scratchpad_output(void) { 447static json_object *ipc_json_describe_scratchpad_output(void) {
376 struct wlr_box box; 448 struct wlr_box box;
377 root_get_box(root, &box); 449 root_get_box(root, &box);
@@ -507,7 +579,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
507 579
508 struct wlr_box window_box = { 580 struct wlr_box window_box = {
509 c->pending.content_x - c->pending.x, 581 c->pending.content_x - c->pending.x,
510 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, 582 (c->current.border == B_PIXEL) ? c->pending.content_y - c->pending.y : 0,
511 c->pending.content_width, 583 c->pending.content_width,
512 c->pending.content_height 584 c->pending.content_height
513 }; 585 };
@@ -551,6 +623,16 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
551 623
552 json_object_object_add(object, "idle_inhibitors", idle_inhibitors); 624 json_object_object_add(object, "idle_inhibitors", idle_inhibitors);
553 625
626 enum wp_content_type_v1_type content_type = WP_CONTENT_TYPE_V1_TYPE_NONE;
627 if (c->view->surface != NULL) {
628 content_type = wlr_surface_get_content_type_v1(server.content_type_manager_v1,
629 c->view->surface);
630 }
631 if (content_type != WP_CONTENT_TYPE_V1_TYPE_NONE) {
632 json_object_object_add(object, "content_type",
633 json_object_new_string(ipc_json_content_type_description(content_type)));
634 }
635
554#if HAVE_XWAYLAND 636#if HAVE_XWAYLAND
555 if (c->view->type == SWAY_VIEW_XWAYLAND) { 637 if (c->view->type == SWAY_VIEW_XWAYLAND) {
556 json_object_object_add(object, "window", 638 json_object_object_add(object, "window",
@@ -595,7 +677,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
595static void ipc_json_describe_container(struct sway_container *c, json_object *object) { 677static void ipc_json_describe_container(struct sway_container *c, json_object *object) {
596 json_object_object_add(object, "name", 678 json_object_object_add(object, "name",
597 c->title ? json_object_new_string(c->title) : NULL); 679 c->title ? json_object_new_string(c->title) : NULL);
598 if (container_is_floating(c)) { 680 bool floating = container_is_floating(c);
681 if (floating) {
599 json_object_object_add(object, "type", 682 json_object_object_add(object, "type",
600 json_object_new_string("floating_con")); 683 json_object_new_string("floating_con"));
601 } 684 }
@@ -613,9 +696,17 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
613 json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 696 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
614 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); 697 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
615 698
699 // sway doesn't track the floating reason, so we can't use "auto_on" or "user_off"
700 json_object_object_add(object, "floating",
701 json_object_new_string(floating ? "user_on" : "auto_off"));
702
616 json_object_object_add(object, "fullscreen_mode", 703 json_object_object_add(object, "fullscreen_mode",
617 json_object_new_int(c->pending.fullscreen_mode)); 704 json_object_new_int(c->pending.fullscreen_mode));
618 705
706 // sway doesn't track if window was resized in scratchpad, so we can't use "changed"
707 json_object_object_add(object, "scratchpad_state",
708 json_object_new_string(!c->scratchpad ? "none" : "fresh"));
709
619 struct sway_node *parent = node_get_parent(&c->node); 710 struct sway_node *parent = node_get_parent(&c->node);
620 struct wlr_box parent_box = {0, 0, 0, 0}; 711 struct wlr_box parent_box = {0, 0, 0, 0};
621 712
@@ -771,6 +862,7 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
771 return object; 862 return object;
772} 863}
773 864
865#if WLR_HAS_LIBINPUT_BACKEND
774static json_object *describe_libinput_device(struct libinput_device *device) { 866static json_object *describe_libinput_device(struct libinput_device *device) {
775 json_object *object = json_object_new_object(); 867 json_object *object = json_object_new_object();
776 868
@@ -854,6 +946,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
854 case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: 946 case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
855 accel_profile = "adaptive"; 947 accel_profile = "adaptive";
856 break; 948 break;
949#if HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM
950 case LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM:
951 accel_profile = "custom";
952 break;
953#endif
857 } 954 }
858 json_object_object_add(object, "accel_profile", 955 json_object_object_add(object, "accel_profile",
859 json_object_new_string(accel_profile)); 956 json_object_new_string(accel_profile));
@@ -933,6 +1030,17 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
933 uint32_t button = libinput_device_config_scroll_get_button(device); 1030 uint32_t button = libinput_device_config_scroll_get_button(device);
934 json_object_object_add(object, "scroll_button", 1031 json_object_object_add(object, "scroll_button",
935 json_object_new_int(button)); 1032 json_object_new_int(button));
1033 const char *lock = "unknown";
1034 switch (libinput_device_config_scroll_get_button_lock(device)) {
1035 case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED:
1036 lock = "enabled";
1037 break;
1038 case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED:
1039 lock = "disabled";
1040 break;
1041 }
1042 json_object_object_add(object, "scroll_button_lock",
1043 json_object_new_string(lock));
936 } 1044 }
937 } 1045 }
938 1046
@@ -949,6 +1057,19 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
949 json_object_object_add(object, "dwt", json_object_new_string(dwt)); 1057 json_object_object_add(object, "dwt", json_object_new_string(dwt));
950 } 1058 }
951 1059
1060 if (libinput_device_config_dwtp_is_available(device)) {
1061 const char *dwtp = "unknown";
1062 switch (libinput_device_config_dwtp_get_enabled(device)) {
1063 case LIBINPUT_CONFIG_DWTP_ENABLED:
1064 dwtp = "enabled";
1065 break;
1066 case LIBINPUT_CONFIG_DWTP_DISABLED:
1067 dwtp = "disabled";
1068 break;
1069 }
1070 json_object_object_add(object, "dwtp", json_object_new_string(dwtp));
1071 }
1072
952 if (libinput_device_config_calibration_has_matrix(device)) { 1073 if (libinput_device_config_calibration_has_matrix(device)) {
953 float matrix[6]; 1074 float matrix[6];
954 libinput_device_config_calibration_get_matrix(device, matrix); 1075 libinput_device_config_calibration_get_matrix(device, matrix);
@@ -963,6 +1084,7 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
963 1084
964 return object; 1085 return object;
965} 1086}
1087#endif
966 1088
967json_object *ipc_json_describe_input(struct sway_input_device *device) { 1089json_object *ipc_json_describe_input(struct sway_input_device *device) {
968 if (!(sway_assert(device, "Device must not be null"))) { 1090 if (!(sway_assert(device, "Device must not be null"))) {
@@ -975,10 +1097,6 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
975 json_object_new_string(device->identifier)); 1097 json_object_new_string(device->identifier));
976 json_object_object_add(object, "name", 1098 json_object_object_add(object, "name",
977 json_object_new_string(device->wlr_device->name)); 1099 json_object_new_string(device->wlr_device->name));
978 json_object_object_add(object, "vendor",
979 json_object_new_int(device->wlr_device->vendor));
980 json_object_object_add(object, "product",
981 json_object_new_int(device->wlr_device->product));
982 json_object_object_add(object, "type", 1100 json_object_object_add(object, "type",
983 json_object_new_string( 1101 json_object_new_string(
984 input_device_get_type(device))); 1102 input_device_get_type(device)));
@@ -1026,12 +1144,18 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
1026 json_object_new_double(scroll_factor)); 1144 json_object_new_double(scroll_factor));
1027 } 1145 }
1028 1146
1147#if WLR_HAS_LIBINPUT_BACKEND
1029 if (wlr_input_device_is_libinput(device->wlr_device)) { 1148 if (wlr_input_device_is_libinput(device->wlr_device)) {
1030 struct libinput_device *libinput_dev; 1149 struct libinput_device *libinput_dev;
1031 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); 1150 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
1032 json_object_object_add(object, "libinput", 1151 json_object_object_add(object, "libinput",
1033 describe_libinput_device(libinput_dev)); 1152 describe_libinput_device(libinput_dev));
1153 json_object_object_add(object, "vendor",
1154 json_object_new_int(libinput_device_get_id_vendor(libinput_dev)));
1155 json_object_object_add(object, "product",
1156 json_object_new_int(libinput_device_get_id_product(libinput_dev)));
1034 } 1157 }
1158#endif
1035 1159
1036 return object; 1160 return object;
1037} 1161}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 3cbf7889..7f353c0e 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -1,5 +1,4 @@
1// See https://i3wm.org/docs/ipc.html for protocol information 1// See https://i3wm.org/docs/ipc.html for protocol information
2#define _POSIX_C_SOURCE 200112L
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <assert.h> 3#include <assert.h>
5#include <errno.h> 4#include <errno.h>
@@ -56,7 +55,6 @@ struct ipc_client {
56 enum ipc_command_type pending_type; 55 enum ipc_command_type pending_type;
57}; 56};
58 57
59struct sockaddr_un *ipc_user_sockaddr(void);
60int ipc_handle_connection(int fd, uint32_t mask, void *data); 58int ipc_handle_connection(int fd, uint32_t mask, void *data);
61int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); 59int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data);
62int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); 60int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data);
@@ -509,6 +507,20 @@ void ipc_event_input(const char *change, struct sway_input_device *device) {
509 json_object_put(json); 507 json_object_put(json);
510} 508}
511 509
510void 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
512int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { 524int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
513 struct ipc_client *client = data; 525 struct ipc_client *client = data;
514 526
@@ -685,6 +697,12 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
685 ipc_json_describe_disabled_output(output)); 697 ipc_json_describe_disabled_output(output));
686 } 698 }
687 } 699 }
700
701 for (int i = 0; i < root->non_desktop_outputs->length; i++) {
702 struct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i];
703 json_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output));
704 }
705
688 const char *json_string = json_object_to_json_string(outputs); 706 const char *json_string = json_object_to_json_string(outputs);
689 ipc_send_reply(client, payload_type, json_string, 707 ipc_send_reply(client, payload_type, json_string,
690 (uint32_t)strlen(json_string)); 708 (uint32_t)strlen(json_string));
@@ -720,6 +738,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
720 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); 738 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
721 if (strcmp(event_type, "workspace") == 0) { 739 if (strcmp(event_type, "workspace") == 0) {
722 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); 740 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE);
741 } else if (strcmp(event_type, "output") == 0) {
742 client->subscribed_events |= event_mask(IPC_EVENT_OUTPUT);
723 } else if (strcmp(event_type, "barconfig_update") == 0) { 743 } else if (strcmp(event_type, "barconfig_update") == 0) {
724 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); 744 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE);
725 } else if (strcmp(event_type, "bar_state_update") == 0) { 745 } else if (strcmp(event_type, "bar_state_update") == 0) {
@@ -904,7 +924,6 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
904 924
905exit_cleanup: 925exit_cleanup:
906 free(buf); 926 free(buf);
907 return;
908} 927}
909 928
910bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, 929bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type,
diff --git a/sway/lock.c b/sway/lock.c
index 04f80079..289e8ca4 100644
--- a/sway/lock.c
+++ b/sway/lock.c
@@ -1,101 +1,229 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
2#include <wlr/types/wlr_scene.h>
3#include <wlr/types/wlr_session_lock_v1.h>
3#include "log.h" 4#include "log.h"
5#include "sway/input/cursor.h"
4#include "sway/input/keyboard.h" 6#include "sway/input/keyboard.h"
5#include "sway/input/seat.h" 7#include "sway/input/seat.h"
8#include "sway/layers.h"
6#include "sway/output.h" 9#include "sway/output.h"
7#include "sway/server.h" 10#include "sway/server.h"
8 11
9struct sway_session_lock_surface { 12struct sway_session_lock_output {
10 struct wlr_session_lock_surface_v1 *lock_surface; 13 struct wlr_scene_tree *tree;
14 struct wlr_scene_rect *background;
15 struct sway_session_lock *lock;
16
11 struct sway_output *output; 17 struct sway_output *output;
12 struct wlr_surface *surface; 18
13 struct wl_listener map; 19 struct wl_list link; // sway_session_lock::outputs
20
14 struct wl_listener destroy; 21 struct wl_listener destroy;
15 struct wl_listener surface_commit; 22 struct wl_listener commit;
16 struct wl_listener output_mode; 23
17 struct wl_listener output_commit; 24 struct wlr_session_lock_surface_v1 *surface;
25
26 // invalid if surface is NULL
27 struct wl_listener surface_destroy;
28 struct wl_listener surface_map;
18}; 29};
19 30
31static 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
41static 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
20static void handle_surface_map(struct wl_listener *listener, void *data) { 62static void handle_surface_map(struct wl_listener *listener, void *data) {
21 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map); 63 struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map);
22 sway_force_focus(surf->surface); 64 if (surf->lock->focused == NULL) {
23 output_damage_whole(surf->output); 65 focus_surface(surf->lock, surf->surface->surface);
66 }
67 cursor_rebase_all();
68}
69
70static 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
81static 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
92static void handle_new_surface(struct wl_listener *listener, void *data) {
93 struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface);
94 struct wlr_session_lock_surface_v1 *lock_surface = data;
95 struct sway_output *output = lock_surface->output->data;
96
97 sway_log(SWAY_DEBUG, "new lock layer surface");
98
99 struct sway_session_lock_output *current_lock_output, *lock_output = NULL;
100 wl_list_for_each(current_lock_output, &lock->outputs, link) {
101 if (current_lock_output->output == output) {
102 lock_output = current_lock_output;
103 break;
104 }
105 }
106 sway_assert(lock_output, "Couldn't find output to lock");
107 sway_assert(!lock_output->surface, "Tried to reassign a surface to an existing output");
108
109 lock_output->surface = lock_surface;
110
111 wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface);
112
113 lock_output->surface_destroy.notify = handle_surface_destroy;
114 wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy);
115 lock_output->surface_map.notify = handle_surface_map;
116 wl_signal_add(&lock_surface->surface->events.map, &lock_output->surface_map);
117
118 lock_output_reconfigure(lock_output);
24} 119}
25 120
26static void handle_surface_commit(struct wl_listener *listener, void *data) { 121static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) {
27 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit); 122 if (output->surface) {
28 output_damage_surface(surf->output, 0, 0, surf->surface, false); 123 refocus_output(output);
124 wl_list_remove(&output->surface_destroy.link);
125 wl_list_remove(&output->surface_map.link);
126 }
127
128 wl_list_remove(&output->commit.link);
129 wl_list_remove(&output->destroy.link);
130 wl_list_remove(&output->link);
131
132 free(output);
29} 133}
30 134
31static void handle_output_mode(struct wl_listener *listener, void *data) { 135static void lock_node_handle_destroy(struct wl_listener *listener, void *data) {
32 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_mode); 136 struct sway_session_lock_output *output =
33 wlr_session_lock_surface_v1_configure(surf->lock_surface, 137 wl_container_of(listener, output, destroy);
34 surf->output->width, surf->output->height); 138 sway_session_lock_output_destroy(output);
35} 139}
36 140
37static void handle_output_commit(struct wl_listener *listener, void *data) { 141static void lock_output_handle_commit(struct wl_listener *listener, void *data) {
38 struct wlr_output_event_commit *event = data; 142 struct wlr_output_event_commit *event = data;
39 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit); 143 struct sway_session_lock_output *output =
40 if (event->committed & ( 144 wl_container_of(listener, output, commit);
145 if (event->state->committed & (
41 WLR_OUTPUT_STATE_MODE | 146 WLR_OUTPUT_STATE_MODE |
42 WLR_OUTPUT_STATE_SCALE | 147 WLR_OUTPUT_STATE_SCALE |
43 WLR_OUTPUT_STATE_TRANSFORM)) { 148 WLR_OUTPUT_STATE_TRANSFORM)) {
44 wlr_session_lock_surface_v1_configure(surf->lock_surface, 149 lock_output_reconfigure(output);
45 surf->output->width, surf->output->height);
46 } 150 }
47} 151}
48 152
49static void handle_surface_destroy(struct wl_listener *listener, void *data) { 153static struct sway_session_lock_output *session_lock_output_create(
50 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy); 154 struct sway_session_lock *lock, struct sway_output *output) {
51 wl_list_remove(&surf->map.link); 155 struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output));
52 wl_list_remove(&surf->destroy.link); 156 if (!lock_output) {
53 wl_list_remove(&surf->surface_commit.link); 157 sway_log(SWAY_ERROR, "failed to allocate a session lock output");
54 wl_list_remove(&surf->output_mode.link); 158 return NULL;
55 wl_list_remove(&surf->output_commit.link); 159 }
56 output_damage_whole(surf->output); 160
57 free(surf); 161 struct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock);
162 if (!tree) {
163 sway_log(SWAY_ERROR, "failed to allocate a session lock output scene tree");
164 free(lock_output);
165 return NULL;
166 }
167
168 struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){
169 lock->abandoned ? 1.f : 0.f,
170 0.f,
171 0.f,
172 1.f,
173 });
174 if (!background) {
175 sway_log(SWAY_ERROR, "failed to allocate a session lock output scene background");
176 wlr_scene_node_destroy(&tree->node);
177 free(lock_output);
178 return NULL;
179 }
180
181 lock_output->output = output;
182 lock_output->tree = tree;
183 lock_output->background = background;
184 lock_output->lock = lock;
185
186 lock_output->destroy.notify = lock_node_handle_destroy;
187 wl_signal_add(&tree->node.events.destroy, &lock_output->destroy);
188
189 lock_output->commit.notify = lock_output_handle_commit;
190 wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit);
191
192 lock_output_reconfigure(lock_output);
193
194 wl_list_insert(&lock->outputs, &lock_output->link);
195
196 return lock_output;
58} 197}
59 198
60static void handle_new_surface(struct wl_listener *listener, void *data) { 199static void sway_session_lock_destroy(struct sway_session_lock* lock) {
61 struct wlr_session_lock_surface_v1 *lock_surface = data; 200 struct sway_session_lock_output *lock_output, *tmp_lock_output;
62 struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf)); 201 wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) {
63 if (surf == NULL) { 202 // destroying the node will also destroy the whole lock output
64 return; 203 wlr_scene_node_destroy(&lock_output->tree->node);
65 } 204 }
66 205
67 sway_log(SWAY_DEBUG, "new lock layer surface"); 206 if (server.session_lock.lock == lock) {
207 server.session_lock.lock = NULL;
208 }
68 209
69 struct sway_output *output = lock_surface->output->data; 210 if (!lock->abandoned) {
70 wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height); 211 wl_list_remove(&lock->destroy.link);
71 212 wl_list_remove(&lock->unlock.link);
72 surf->lock_surface = lock_surface; 213 wl_list_remove(&lock->new_surface.link);
73 surf->surface = lock_surface->surface; 214 }
74 surf->output = output; 215
75 surf->map.notify = handle_surface_map; 216 free(lock);
76 wl_signal_add(&lock_surface->events.map, &surf->map);
77 surf->destroy.notify = handle_surface_destroy;
78 wl_signal_add(&lock_surface->events.destroy, &surf->destroy);
79 surf->surface_commit.notify = handle_surface_commit;
80 wl_signal_add(&surf->surface->events.commit, &surf->surface_commit);
81 surf->output_mode.notify = handle_output_mode;
82 wl_signal_add(&output->wlr_output->events.mode, &surf->output_mode);
83 surf->output_commit.notify = handle_output_commit;
84 wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit);
85} 217}
86 218
87static void handle_unlock(struct wl_listener *listener, void *data) { 219static void handle_unlock(struct wl_listener *listener, void *data) {
220 struct sway_session_lock *lock = wl_container_of(listener, lock, unlock);
88 sway_log(SWAY_DEBUG, "session unlocked"); 221 sway_log(SWAY_DEBUG, "session unlocked");
89 server.session_lock.locked = false;
90 server.session_lock.lock = NULL;
91 222
92 wl_list_remove(&server.session_lock.lock_new_surface.link); 223 sway_session_lock_destroy(lock);
93 wl_list_remove(&server.session_lock.lock_unlock.link);
94 wl_list_remove(&server.session_lock.lock_destroy.link);
95 224
96 struct sway_seat *seat; 225 struct sway_seat *seat;
97 wl_list_for_each(seat, &server.input->seats, link) { 226 wl_list_for_each(seat, &server.input->seats, link) {
98 seat_set_exclusive_client(seat, NULL);
99 // copied from seat_set_focus_layer -- deduplicate? 227 // copied from seat_set_focus_layer -- deduplicate?
100 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); 228 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
101 if (previous) { 229 if (previous) {
@@ -105,31 +233,28 @@ static void handle_unlock(struct wl_listener *listener, void *data) {
105 } 233 }
106 } 234 }
107 235
108 // redraw everything 236 // Triggers a refocus of the topmost surface layer if necessary
237 // TODO: Make layer surface focus per-output based on cursor position
109 for (int i = 0; i < root->outputs->length; ++i) { 238 for (int i = 0; i < root->outputs->length; ++i) {
110 struct sway_output *output = root->outputs->items[i]; 239 struct sway_output *output = root->outputs->items[i];
111 output_damage_whole(output); 240 arrange_layers(output);
112 } 241 }
113} 242}
114 243
115static void handle_abandon(struct wl_listener *listener, void *data) { 244static void handle_abandon(struct wl_listener *listener, void *data) {
245 struct sway_session_lock *lock = wl_container_of(listener, lock, destroy);
116 sway_log(SWAY_INFO, "session lock abandoned"); 246 sway_log(SWAY_INFO, "session lock abandoned");
117 server.session_lock.lock = NULL;
118
119 wl_list_remove(&server.session_lock.lock_new_surface.link);
120 wl_list_remove(&server.session_lock.lock_unlock.link);
121 wl_list_remove(&server.session_lock.lock_destroy.link);
122 247
123 struct sway_seat *seat; 248 struct sway_session_lock_output *lock_output;
124 wl_list_for_each(seat, &server.input->seats, link) { 249 wl_list_for_each(lock_output, &lock->outputs, link) {
125 seat->exclusive_client = NULL; 250 wlr_scene_rect_set_color(lock_output->background,
251 (float[4]){ 1.f, 0.f, 0.f, 1.f });
126 } 252 }
127 253
128 // redraw everything 254 lock->abandoned = true;
129 for (int i = 0; i < root->outputs->length; ++i) { 255 wl_list_remove(&lock->destroy.link);
130 struct sway_output *output = root->outputs->items[i]; 256 wl_list_remove(&lock->unlock.link);
131 output_damage_whole(output); 257 wl_list_remove(&lock->new_surface.link);
132 }
133} 258}
134 259
135static void handle_session_lock(struct wl_listener *listener, void *data) { 260static void handle_session_lock(struct wl_listener *listener, void *data) {
@@ -137,44 +262,89 @@ static void handle_session_lock(struct wl_listener *listener, void *data) {
137 struct wl_client *client = wl_resource_get_client(lock->resource); 262 struct wl_client *client = wl_resource_get_client(lock->resource);
138 263
139 if (server.session_lock.lock) { 264 if (server.session_lock.lock) {
265 if (server.session_lock.lock->abandoned) {
266 sway_log(SWAY_INFO, "Replacing abandoned lock");
267 sway_session_lock_destroy(server.session_lock.lock);
268 } else {
269 sway_log(SWAY_ERROR, "Cannot lock an already locked session");
270 wlr_session_lock_v1_destroy(lock);
271 return;
272 }
273 }
274
275 struct sway_session_lock *sway_lock = calloc(1, sizeof(*sway_lock));
276 if (!sway_lock) {
277 sway_log(SWAY_ERROR, "failed to allocate a session lock object");
140 wlr_session_lock_v1_destroy(lock); 278 wlr_session_lock_v1_destroy(lock);
141 return; 279 return;
142 } 280 }
143 281
282 wl_list_init(&sway_lock->outputs);
283
144 sway_log(SWAY_DEBUG, "session locked"); 284 sway_log(SWAY_DEBUG, "session locked");
145 server.session_lock.locked = true;
146 server.session_lock.lock = lock;
147 285
148 struct sway_seat *seat; 286 struct sway_seat *seat;
149 wl_list_for_each(seat, &server.input->seats, link) { 287 wl_list_for_each(seat, &server.input->seats, link) {
150 seat_set_exclusive_client(seat, client); 288 seat_unfocus_unless_client(seat, client);
151 } 289 }
152 290
153 wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface); 291 struct sway_output *output;
154 wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock); 292 wl_list_for_each(output, &root->all_outputs, link) {
155 wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy); 293 sway_session_lock_add_output(sway_lock, output);
294 }
156 295
157 wlr_session_lock_v1_send_locked(lock); 296 sway_lock->new_surface.notify = handle_new_surface;
297 wl_signal_add(&lock->events.new_surface, &sway_lock->new_surface);
298 sway_lock->unlock.notify = handle_unlock;
299 wl_signal_add(&lock->events.unlock, &sway_lock->unlock);
300 sway_lock->destroy.notify = handle_abandon;
301 wl_signal_add(&lock->events.destroy, &sway_lock->destroy);
158 302
159 // redraw everything 303 wlr_session_lock_v1_send_locked(lock);
160 for (int i = 0; i < root->outputs->length; ++i) { 304 server.session_lock.lock = sway_lock;
161 struct sway_output *output = root->outputs->items[i];
162 output_damage_whole(output);
163 }
164} 305}
165 306
166static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { 307static void handle_session_lock_destroy(struct wl_listener *listener, void *data) {
167 assert(server.session_lock.lock == NULL); 308 // if the server shuts down while a lock is active, destroy the lock
309 if (server.session_lock.lock) {
310 sway_session_lock_destroy(server.session_lock.lock);
311 }
312
168 wl_list_remove(&server.session_lock.new_lock.link); 313 wl_list_remove(&server.session_lock.new_lock.link);
169 wl_list_remove(&server.session_lock.manager_destroy.link); 314 wl_list_remove(&server.session_lock.manager_destroy.link);
315
316 server.session_lock.manager = NULL;
317}
318
319void 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
333bool sway_session_lock_has_surface(struct sway_session_lock *lock,
334 struct wlr_surface *surface) {
335 struct sway_session_lock_output *lock_output;
336 wl_list_for_each(lock_output, &lock->outputs, link) {
337 if (lock_output->surface && lock_output->surface->surface == surface) {
338 return true;
339 }
340 }
341
342 return false;
170} 343}
171 344
172void sway_session_lock_init(void) { 345void sway_session_lock_init(void) {
173 server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); 346 server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display);
174 347
175 server.session_lock.lock_new_surface.notify = handle_new_surface;
176 server.session_lock.lock_unlock.notify = handle_unlock;
177 server.session_lock.lock_destroy.notify = handle_abandon;
178 server.session_lock.new_lock.notify = handle_session_lock; 348 server.session_lock.new_lock.notify = handle_session_lock;
179 server.session_lock.manager_destroy.notify = handle_session_lock_destroy; 349 server.session_lock.manager_destroy.notify = handle_session_lock_destroy;
180 wl_signal_add(&server.session_lock.manager->events.new_lock, 350 wl_signal_add(&server.session_lock.manager->events.new_lock,
diff --git a/sway/main.c b/sway/main.c
index 85bc2f1c..1c4939aa 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <getopt.h> 1#include <getopt.h>
3#include <pango/pangocairo.h> 2#include <pango/pangocairo.h>
4#include <signal.h> 3#include <signal.h>
@@ -49,44 +48,6 @@ void sig_handler(int signal) {
49 sway_terminate(EXIT_SUCCESS); 48 sway_terminate(EXIT_SUCCESS);
50} 49}
51 50
52void detect_proprietary(int allow_unsupported_gpu) {
53 FILE *f = fopen("/proc/modules", "r");
54 if (!f) {
55 return;
56 }
57 char *line = NULL;
58 size_t line_size = 0;
59 while (getline(&line, &line_size, f) != -1) {
60 if (strncmp(line, "nvidia ", 7) == 0) {
61 if (allow_unsupported_gpu) {
62 sway_log(SWAY_ERROR,
63 "!!! Proprietary Nvidia drivers are in use !!!");
64 } else {
65 sway_log(SWAY_ERROR,
66 "Proprietary Nvidia drivers are NOT supported. "
67 "Use Nouveau. To launch sway anyway, launch with "
68 "--unsupported-gpu and DO NOT report issues.");
69 exit(EXIT_FAILURE);
70 }
71 break;
72 }
73 if (strstr(line, "fglrx")) {
74 if (allow_unsupported_gpu) {
75 sway_log(SWAY_ERROR,
76 "!!! Proprietary AMD drivers are in use !!!");
77 } else {
78 sway_log(SWAY_ERROR, "Proprietary AMD drivers do NOT support "
79 "Wayland. Use radeon. To try anyway, launch sway with "
80 "--unsupported-gpu and DO NOT report issues.");
81 exit(EXIT_FAILURE);
82 }
83 break;
84 }
85 }
86 free(line);
87 fclose(f);
88}
89
90void run_as_ipc_client(char *command, char *socket_path) { 51void run_as_ipc_client(char *command, char *socket_path) {
91 int socketfd = ipc_open_socket(socket_path); 52 int socketfd = ipc_open_socket(socket_path);
92 uint32_t len = strlen(command); 53 uint32_t len = strlen(command);
@@ -192,11 +153,7 @@ void restore_nofile_limit(void) {
192} 153}
193 154
194void enable_debug_flag(const char *flag) { 155void enable_debug_flag(const char *flag) {
195 if (strcmp(flag, "damage=highlight") == 0) { 156 if (strcmp(flag, "noatomic") == 0) {
196 debug.damage = DAMAGE_HIGHLIGHT;
197 } else if (strcmp(flag, "damage=rerender") == 0) {
198 debug.damage = DAMAGE_RERENDER;
199 } else if (strcmp(flag, "noatomic") == 0) {
200 debug.noatomic = true; 157 debug.noatomic = true;
201 } else if (strcmp(flag, "txn-wait") == 0) { 158 } else if (strcmp(flag, "txn-wait") == 0) {
202 debug.txn_wait = true; 159 debug.txn_wait = true;
@@ -204,8 +161,8 @@ void enable_debug_flag(const char *flag) {
204 debug.txn_timings = true; 161 debug.txn_timings = true;
205 } else if (strncmp(flag, "txn-timeout=", 12) == 0) { 162 } else if (strncmp(flag, "txn-timeout=", 12) == 0) {
206 server.txn_timeout_ms = atoi(&flag[12]); 163 server.txn_timeout_ms = atoi(&flag[12]);
207 } else if (strcmp(flag, "noscanout") == 0) { 164 } else if (strcmp(flag, "legacy-wl-drm") == 0) {
208 debug.noscanout = true; 165 debug.legacy_wl_drm = true;
209 } else { 166 } else {
210 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); 167 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag);
211 } 168 }
@@ -255,7 +212,7 @@ static const char usage[] =
255 "\n"; 212 "\n";
256 213
257int main(int argc, char **argv) { 214int main(int argc, char **argv) {
258 static bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false; 215 static bool verbose = false, debug = false, validate = false;
259 216
260 char *config_path = NULL; 217 char *config_path = NULL;
261 218
@@ -363,7 +320,6 @@ int main(int argc, char **argv) {
363 return 0; 320 return 0;
364 } 321 }
365 322
366 detect_proprietary(allow_unsupported_gpu);
367 increase_nofile_limit(); 323 increase_nofile_limit();
368 324
369 // handle SIGTERM signals 325 // handle SIGTERM signals
@@ -375,12 +331,14 @@ int main(int argc, char **argv) {
375 331
376 sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); 332 sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION);
377 333
378 root = root_create();
379
380 if (!server_init(&server)) { 334 if (!server_init(&server)) {
381 return 1; 335 return 1;
382 } 336 }
383 337
338 if (server.linux_dmabuf_v1) {
339 wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1);
340 }
341
384 if (validate) { 342 if (validate) {
385 bool valid = load_main_config(config_path, false, true); 343 bool valid = load_main_config(config_path, false, true);
386 free(config_path); 344 free(config_path);
diff --git a/sway/meson.build b/sway/meson.build
index ced7419c..d937e425 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -8,24 +8,23 @@ sway_sources = files(
8 'lock.c', 8 'lock.c',
9 'main.c', 9 'main.c',
10 'realtime.c', 10 'realtime.c',
11 'scene_descriptor.c',
11 'server.c', 12 'server.c',
13 'sway_text_node.c',
12 'swaynag.c', 14 'swaynag.c',
13 'xdg_activation_v1.c', 15 'xdg_activation_v1.c',
14 'xdg_decoration.c', 16 'xdg_decoration.c',
15 17
16 'desktop/desktop.c',
17 'desktop/idle_inhibit_v1.c', 18 'desktop/idle_inhibit_v1.c',
18 'desktop/layer_shell.c', 19 'desktop/layer_shell.c',
19 'desktop/output.c', 20 'desktop/output.c',
20 'desktop/render.c',
21 'desktop/surface.c',
22 'desktop/transaction.c', 21 'desktop/transaction.c',
23 'desktop/xdg_shell.c', 22 'desktop/xdg_shell.c',
23 'desktop/launcher.c',
24 24
25 'input/input-manager.c', 25 'input/input-manager.c',
26 'input/cursor.c', 26 'input/cursor.c',
27 'input/keyboard.c', 27 'input/keyboard.c',
28 'input/libinput.c',
29 'input/seat.c', 28 'input/seat.c',
30 'input/seatop_default.c', 29 'input/seatop_default.c',
31 'input/seatop_down.c', 30 'input/seatop_down.c',
@@ -86,6 +85,7 @@ sway_sources = files(
86 'commands/nop.c', 85 'commands/nop.c',
87 'commands/output.c', 86 'commands/output.c',
88 'commands/popup_during_fullscreen.c', 87 'commands/popup_during_fullscreen.c',
88 'commands/primary_selection.c',
89 'commands/reload.c', 89 'commands/reload.c',
90 'commands/rename.c', 90 'commands/rename.c',
91 'commands/resize.c', 91 'commands/resize.c',
@@ -157,6 +157,7 @@ sway_sources = files(
157 'commands/input/drag.c', 157 'commands/input/drag.c',
158 'commands/input/drag_lock.c', 158 'commands/input/drag_lock.c',
159 'commands/input/dwt.c', 159 'commands/input/dwt.c',
160 'commands/input/dwtp.c',
160 'commands/input/events.c', 161 'commands/input/events.c',
161 'commands/input/left_handed.c', 162 'commands/input/left_handed.c',
162 'commands/input/map_from_region.c', 163 'commands/input/map_from_region.c',
@@ -165,9 +166,11 @@ sway_sources = files(
165 'commands/input/middle_emulation.c', 166 'commands/input/middle_emulation.c',
166 'commands/input/natural_scroll.c', 167 'commands/input/natural_scroll.c',
167 'commands/input/pointer_accel.c', 168 'commands/input/pointer_accel.c',
169 'commands/input/rotation_angle.c',
168 'commands/input/repeat_delay.c', 170 'commands/input/repeat_delay.c',
169 'commands/input/repeat_rate.c', 171 'commands/input/repeat_rate.c',
170 'commands/input/scroll_button.c', 172 'commands/input/scroll_button.c',
173 'commands/input/scroll_button_lock.c',
171 'commands/input/scroll_factor.c', 174 'commands/input/scroll_factor.c',
172 'commands/input/scroll_method.c', 175 'commands/input/scroll_method.c',
173 'commands/input/tap.c', 176 'commands/input/tap.c',
@@ -198,6 +201,7 @@ sway_sources = files(
198 'commands/output/subpixel.c', 201 'commands/output/subpixel.c',
199 'commands/output/toggle.c', 202 'commands/output/toggle.c',
200 'commands/output/transform.c', 203 'commands/output/transform.c',
204 'commands/output/unplug.c',
201 205
202 'tree/arrange.c', 206 'tree/arrange.c',
203 'tree/container.c', 207 'tree/container.c',
@@ -218,24 +222,26 @@ sway_deps = [
218 math, 222 math,
219 pango, 223 pango,
220 pcre2, 224 pcre2,
221 glesv2,
222 pixman, 225 pixman,
223 server_protos,
224 threads, 226 threads,
225 wayland_server, 227 wayland_server,
226 wlroots, 228 wlroots,
227 xkbcommon, 229 xkbcommon,
230 xcb,
228 xcb_icccm, 231 xcb_icccm,
229] 232]
230 233
231if have_xwayland 234if have_xwayland
232 sway_sources += 'desktop/xwayland.c' 235 sway_sources += 'desktop/xwayland.c'
233 sway_deps += xcb 236endif
237
238if wlroots_features['libinput_backend']
239 sway_sources += 'input/libinput.c'
234endif 240endif
235 241
236executable( 242executable(
237 'sway', 243 'sway',
238 sway_sources, 244 sway_sources + wl_protos_src,
239 include_directories: [sway_inc], 245 include_directories: [sway_inc],
240 dependencies: sway_deps, 246 dependencies: sway_deps,
241 link_with: [lib_sway_common], 247 link_with: [lib_sway_common],
diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c
new file mode 100644
index 00000000..a30d4664
--- /dev/null
+++ b/sway/scene_descriptor.c
@@ -0,0 +1,66 @@
1#include <stdlib.h>
2#include <wlr/util/addon.h>
3#include "log.h"
4#include "sway/scene_descriptor.h"
5
6struct scene_descriptor {
7 void *data;
8 struct wlr_addon addon;
9};
10
11static const struct wlr_addon_interface addon_interface;
12
13static 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
24static void descriptor_destroy(struct scene_descriptor *desc) {
25 wlr_addon_finish(&desc->addon);
26 free(desc);
27}
28
29void *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
39void 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
45static 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
50static const struct wlr_addon_interface addon_interface = {
51 .name = "sway_scene_descriptor",
52 .destroy = addon_handle_destroy,
53};
54
55bool scene_descriptor_assign(struct wlr_scene_node *node,
56 enum sway_scene_descriptor_type type, void *data) {
57 struct scene_descriptor *desc = calloc(1, sizeof(*desc));
58 if (!desc) {
59 sway_log(SWAY_ERROR, "Could not allocate a scene descriptor");
60 return false;
61 }
62
63 wlr_addon_init(&desc->addon, &node->addons, (void *)type, &addon_interface);
64 desc->data = data;
65 return true;
66}
diff --git a/sway/server.c b/sway/server.c
index a87fc7cf..d159dc9b 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -7,24 +6,34 @@
7#include <wlr/backend.h> 6#include <wlr/backend.h>
8#include <wlr/backend/headless.h> 7#include <wlr/backend/headless.h>
9#include <wlr/backend/multi.h> 8#include <wlr/backend/multi.h>
10#include <wlr/backend/session.h>
11#include <wlr/config.h> 9#include <wlr/config.h>
10#include <wlr/render/allocator.h>
12#include <wlr/render/wlr_renderer.h> 11#include <wlr/render/wlr_renderer.h>
13#include <wlr/types/wlr_compositor.h> 12#include <wlr/types/wlr_compositor.h>
13#include <wlr/types/wlr_content_type_v1.h>
14#include <wlr/types/wlr_cursor_shape_v1.h>
14#include <wlr/types/wlr_data_control_v1.h> 15#include <wlr/types/wlr_data_control_v1.h>
15#include <wlr/types/wlr_drm_lease_v1.h> 16#include <wlr/types/wlr_data_device.h>
16#include <wlr/types/wlr_drm.h> 17#include <wlr/types/wlr_drm.h>
17#include <wlr/types/wlr_export_dmabuf_v1.h> 18#include <wlr/types/wlr_export_dmabuf_v1.h>
19#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
20#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
21#include <wlr/types/wlr_fractional_scale_v1.h>
18#include <wlr/types/wlr_gamma_control_v1.h> 22#include <wlr/types/wlr_gamma_control_v1.h>
19#include <wlr/types/wlr_idle.h> 23#include <wlr/types/wlr_idle_notify_v1.h>
20#include <wlr/types/wlr_layer_shell_v1.h> 24#include <wlr/types/wlr_layer_shell_v1.h>
21#include <wlr/types/wlr_linux_dmabuf_v1.h> 25#include <wlr/types/wlr_linux_dmabuf_v1.h>
26#include <wlr/types/wlr_output_management_v1.h>
27#include <wlr/types/wlr_output_power_management_v1.h>
22#include <wlr/types/wlr_pointer_constraints_v1.h> 28#include <wlr/types/wlr_pointer_constraints_v1.h>
29#include <wlr/types/wlr_presentation_time.h>
23#include <wlr/types/wlr_primary_selection_v1.h> 30#include <wlr/types/wlr_primary_selection_v1.h>
24#include <wlr/types/wlr_relative_pointer_v1.h> 31#include <wlr/types/wlr_relative_pointer_v1.h>
25#include <wlr/types/wlr_screencopy_v1.h> 32#include <wlr/types/wlr_screencopy_v1.h>
26#include <wlr/types/wlr_single_pixel_buffer_v1.h> 33#include <wlr/types/wlr_security_context_v1.h>
27#include <wlr/types/wlr_server_decoration.h> 34#include <wlr/types/wlr_server_decoration.h>
35#include <wlr/types/wlr_session_lock_v1.h>
36#include <wlr/types/wlr_single_pixel_buffer_v1.h>
28#include <wlr/types/wlr_subcompositor.h> 37#include <wlr/types/wlr_subcompositor.h>
29#include <wlr/types/wlr_tablet_v2.h> 38#include <wlr/types/wlr_tablet_v2.h>
30#include <wlr/types/wlr_viewporter.h> 39#include <wlr/types/wlr_viewporter.h>
@@ -35,6 +44,7 @@
35#include <wlr/types/wlr_xdg_foreign_v1.h> 44#include <wlr/types/wlr_xdg_foreign_v1.h>
36#include <wlr/types/wlr_xdg_foreign_v2.h> 45#include <wlr/types/wlr_xdg_foreign_v2.h>
37#include <wlr/types/wlr_xdg_output_v1.h> 46#include <wlr/types/wlr_xdg_output_v1.h>
47#include <xf86drm.h>
38#include "config.h" 48#include "config.h"
39#include "list.h" 49#include "list.h"
40#include "log.h" 50#include "log.h"
@@ -43,11 +53,25 @@
43#include "sway/input/input-manager.h" 53#include "sway/input/input-manager.h"
44#include "sway/output.h" 54#include "sway/output.h"
45#include "sway/server.h" 55#include "sway/server.h"
56#include "sway/input/cursor.h"
46#include "sway/tree/root.h" 57#include "sway/tree/root.h"
58
47#if HAVE_XWAYLAND 59#if HAVE_XWAYLAND
60#include <wlr/xwayland/shell.h>
48#include "sway/xwayland.h" 61#include "sway/xwayland.h"
49#endif 62#endif
50 63
64#if WLR_HAS_DRM_BACKEND
65#include <wlr/types/wlr_drm_lease_v1.h>
66#endif
67
68#define SWAY_XDG_SHELL_VERSION 5
69#define SWAY_LAYER_SHELL_VERSION 4
70#define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1
71
72bool allow_unsupported_gpu = false;
73
74#if WLR_HAS_DRM_BACKEND
51static void handle_drm_lease_request(struct wl_listener *listener, void *data) { 75static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
52 /* We only offer non-desktop outputs, but in the future we might want to do 76 /* We only offer non-desktop outputs, but in the future we might want to do
53 * more logic here. */ 77 * more logic here. */
@@ -59,32 +83,170 @@ static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
59 wlr_drm_lease_request_v1_reject(req); 83 wlr_drm_lease_request_v1_reject(req);
60 } 84 }
61} 85}
86#endif
87
88static 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
119static 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
140static 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
176static void handle_renderer_lost(struct wl_listener *listener, void *data) {
177 struct sway_server *server = wl_container_of(listener, server, renderer_lost);
178
179 sway_log(SWAY_INFO, "Re-creating renderer after GPU reset");
180
181 struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend);
182 if (renderer == NULL) {
183 sway_log(SWAY_ERROR, "Unable to create renderer");
184 return;
185 }
186
187 struct wlr_allocator *allocator =
188 wlr_allocator_autocreate(server->backend, renderer);
189 if (allocator == NULL) {
190 sway_log(SWAY_ERROR, "Unable to create allocator");
191 wlr_renderer_destroy(renderer);
192 return;
193 }
194
195 struct wlr_renderer *old_renderer = server->renderer;
196 struct wlr_allocator *old_allocator = server->allocator;
197 server->renderer = renderer;
198 server->allocator = allocator;
62 199
63#define SWAY_XDG_SHELL_VERSION 2 200 wl_list_remove(&server->renderer_lost.link);
201 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
202
203 wlr_compositor_set_renderer(server->compositor, renderer);
204
205 for (int i = 0; i < root->outputs->length; ++i) {
206 struct sway_output *output = root->outputs->items[i];
207 wlr_output_init_render(output->wlr_output,
208 server->allocator, server->renderer);
209 }
210
211 wlr_allocator_destroy(old_allocator);
212 wlr_renderer_destroy(old_renderer);
213}
64 214
65bool server_init(struct sway_server *server) { 215bool server_init(struct sway_server *server) {
66 sway_log(SWAY_DEBUG, "Initializing Wayland server"); 216 sway_log(SWAY_DEBUG, "Initializing Wayland server");
67 server->wl_display = wl_display_create(); 217 server->wl_display = wl_display_create();
68 server->wl_event_loop = wl_display_get_event_loop(server->wl_display); 218 server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
69 server->backend = wlr_backend_autocreate(server->wl_display);
70 219
220 wl_display_set_global_filter(server->wl_display, filter_global, NULL);
221
222 root = root_create(server->wl_display);
223
224 server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session);
71 if (!server->backend) { 225 if (!server->backend) {
72 sway_log(SWAY_ERROR, "Unable to create backend"); 226 sway_log(SWAY_ERROR, "Unable to create backend");
73 return false; 227 return false;
74 } 228 }
75 229
230 wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL);
231
76 server->renderer = wlr_renderer_autocreate(server->backend); 232 server->renderer = wlr_renderer_autocreate(server->backend);
77 if (!server->renderer) { 233 if (!server->renderer) {
78 sway_log(SWAY_ERROR, "Failed to create renderer"); 234 sway_log(SWAY_ERROR, "Failed to create renderer");
79 return false; 235 return false;
80 } 236 }
81 237
238 server->renderer_lost.notify = handle_renderer_lost;
239 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
240
82 wlr_renderer_init_wl_shm(server->renderer, server->wl_display); 241 wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
83 242
84 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { 243 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) {
244 server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(
245 server->wl_display, 4, server->renderer);
246 }
247 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL &&
248 debug.legacy_wl_drm) {
85 wlr_drm_create(server->wl_display, server->renderer); 249 wlr_drm_create(server->wl_display, server->renderer);
86 server->linux_dmabuf_v1 =
87 wlr_linux_dmabuf_v1_create(server->wl_display, server->renderer);
88 } 250 }
89 251
90 server->allocator = wlr_allocator_autocreate(server->backend, 252 server->allocator = wlr_allocator_autocreate(server->backend,
@@ -94,18 +256,19 @@ bool server_init(struct sway_server *server) {
94 return false; 256 return false;
95 } 257 }
96 258
97 server->compositor = wlr_compositor_create(server->wl_display, 259 server->compositor = wlr_compositor_create(server->wl_display, 6,
98 server->renderer); 260 server->renderer);
99 server->compositor_new_surface.notify = handle_compositor_new_surface;
100 wl_signal_add(&server->compositor->events.new_surface,
101 &server->compositor_new_surface);
102 261
103 wlr_subcompositor_create(server->wl_display); 262 wlr_subcompositor_create(server->wl_display);
104 263
105 server->data_device_manager = 264 server->data_device_manager =
106 wlr_data_device_manager_create(server->wl_display); 265 wlr_data_device_manager_create(server->wl_display);
107 266
108 wlr_gamma_control_manager_v1_create(server->wl_display); 267 server->gamma_control_manager_v1 =
268 wlr_gamma_control_manager_v1_create(server->wl_display);
269 server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma;
270 wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma,
271 &server->gamma_control_set_gamma);
109 272
110 server->new_output.notify = handle_new_output; 273 server->new_output.notify = handle_new_output;
111 wl_signal_add(&server->backend->events.new_output, &server->new_output); 274 wl_signal_add(&server->backend->events.new_output, &server->new_output);
@@ -115,20 +278,20 @@ bool server_init(struct sway_server *server) {
115 278
116 wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); 279 wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout);
117 280
118 server->idle = wlr_idle_create(server->wl_display); 281 server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display);
119 server->idle_inhibit_manager_v1 = 282 sway_idle_inhibit_manager_v1_init();
120 sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle);
121 283
122 server->layer_shell = wlr_layer_shell_v1_create(server->wl_display); 284 server->layer_shell = wlr_layer_shell_v1_create(server->wl_display,
285 SWAY_LAYER_SHELL_VERSION);
123 wl_signal_add(&server->layer_shell->events.new_surface, 286 wl_signal_add(&server->layer_shell->events.new_surface,
124 &server->layer_shell_surface); 287 &server->layer_shell_surface);
125 server->layer_shell_surface.notify = handle_layer_shell_surface; 288 server->layer_shell_surface.notify = handle_layer_shell_surface;
126 289
127 server->xdg_shell = wlr_xdg_shell_create(server->wl_display, 290 server->xdg_shell = wlr_xdg_shell_create(server->wl_display,
128 SWAY_XDG_SHELL_VERSION); 291 SWAY_XDG_SHELL_VERSION);
129 wl_signal_add(&server->xdg_shell->events.new_surface, 292 wl_signal_add(&server->xdg_shell->events.new_toplevel,
130 &server->xdg_shell_surface); 293 &server->xdg_shell_toplevel);
131 server->xdg_shell_surface.notify = handle_xdg_shell_surface; 294 server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel;
132 295
133 server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); 296 server->tablet_v2 = wlr_tablet_v2_create(server->wl_display);
134 297
@@ -159,8 +322,7 @@ bool server_init(struct sway_server *server) {
159 wl_signal_add(&server->pointer_constraints->events.new_constraint, 322 wl_signal_add(&server->pointer_constraints->events.new_constraint,
160 &server->pointer_constraint); 323 &server->pointer_constraint);
161 324
162 server->presentation = 325 wlr_presentation_create(server->wl_display, server->backend);
163 wlr_presentation_create(server->wl_display, server->backend);
164 326
165 server->output_manager_v1 = 327 server->output_manager_v1 =
166 wlr_output_manager_v1_create(server->wl_display); 328 wlr_output_manager_v1_create(server->wl_display);
@@ -179,11 +341,14 @@ bool server_init(struct sway_server *server) {
179 &server->output_power_manager_set_mode); 341 &server->output_power_manager_set_mode);
180 server->input_method = wlr_input_method_manager_v2_create(server->wl_display); 342 server->input_method = wlr_input_method_manager_v2_create(server->wl_display);
181 server->text_input = wlr_text_input_manager_v3_create(server->wl_display); 343 server->text_input = wlr_text_input_manager_v3_create(server->wl_display);
344 server->foreign_toplevel_list =
345 wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION);
182 server->foreign_toplevel_manager = 346 server->foreign_toplevel_manager =
183 wlr_foreign_toplevel_manager_v1_create(server->wl_display); 347 wlr_foreign_toplevel_manager_v1_create(server->wl_display);
184 348
185 sway_session_lock_init(); 349 sway_session_lock_init();
186 350
351#if WLR_HAS_DRM_BACKEND
187 server->drm_lease_manager= 352 server->drm_lease_manager=
188 wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); 353 wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
189 if (server->drm_lease_manager) { 354 if (server->drm_lease_manager) {
@@ -194,13 +359,17 @@ bool server_init(struct sway_server *server) {
194 sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); 359 sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1");
195 sway_log(SWAY_INFO, "VR will not be available"); 360 sway_log(SWAY_INFO, "VR will not be available");
196 } 361 }
362#endif
197 363
198 wlr_export_dmabuf_manager_v1_create(server->wl_display); 364 server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display);
199 wlr_screencopy_manager_v1_create(server->wl_display); 365 server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display);
200 wlr_data_control_manager_v1_create(server->wl_display); 366 server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display);
201 wlr_primary_selection_v1_device_manager_create(server->wl_display); 367 server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display);
202 wlr_viewporter_create(server->wl_display); 368 wlr_viewporter_create(server->wl_display);
203 wlr_single_pixel_buffer_manager_v1_create(server->wl_display); 369 wlr_single_pixel_buffer_manager_v1_create(server->wl_display);
370 server->content_type_manager_v1 =
371 wlr_content_type_manager_v1_create(server->wl_display, 1);
372 wlr_fractional_scale_manager_v1_create(server->wl_display, 1);
204 373
205 struct wlr_xdg_foreign_registry *foreign_registry = 374 struct wlr_xdg_foreign_registry *foreign_registry =
206 wlr_xdg_foreign_registry_create(server->wl_display); 375 wlr_xdg_foreign_registry_create(server->wl_display);
@@ -212,6 +381,17 @@ bool server_init(struct sway_server *server) {
212 xdg_activation_v1_handle_request_activate; 381 xdg_activation_v1_handle_request_activate;
213 wl_signal_add(&server->xdg_activation_v1->events.request_activate, 382 wl_signal_add(&server->xdg_activation_v1->events.request_activate,
214 &server->xdg_activation_v1_request_activate); 383 &server->xdg_activation_v1_request_activate);
384 server->xdg_activation_v1_new_token.notify =
385 xdg_activation_v1_handle_new_token;
386 wl_signal_add(&server->xdg_activation_v1->events.new_token,
387 &server->xdg_activation_v1_new_token);
388
389 struct wlr_cursor_shape_manager_v1 *cursor_shape_manager =
390 wlr_cursor_shape_manager_v1_create(server->wl_display, 1);
391 server->request_set_cursor_shape.notify = handle_request_set_cursor_shape;
392 wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape);
393
394 wl_list_init(&server->pending_launcher_ctxs);
215 395
216 // Avoid using "wayland-0" as display socket 396 // Avoid using "wayland-0" as display socket
217 char name_candidate[16]; 397 char name_candidate[16];
@@ -229,7 +409,7 @@ bool server_init(struct sway_server *server) {
229 return false; 409 return false;
230 } 410 }
231 411
232 server->headless_backend = wlr_headless_backend_create(server->wl_display); 412 server->headless_backend = wlr_headless_backend_create(server->wl_event_loop);
233 if (!server->headless_backend) { 413 if (!server->headless_backend) {
234 sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); 414 sway_log(SWAY_ERROR, "Failed to create secondary headless backend");
235 wlr_backend_destroy(server->backend); 415 wlr_backend_destroy(server->backend);
@@ -262,6 +442,7 @@ void server_fini(struct sway_server *server) {
262 wlr_xwayland_destroy(server->xwayland.wlr_xwayland); 442 wlr_xwayland_destroy(server->xwayland.wlr_xwayland);
263#endif 443#endif
264 wl_display_destroy_clients(server->wl_display); 444 wl_display_destroy_clients(server->wl_display);
445 wlr_backend_destroy(server->backend);
265 wl_display_destroy(server->wl_display); 446 wl_display_destroy(server->wl_display);
266 list_free(server->dirty_nodes); 447 list_free(server->dirty_nodes);
267} 448}
@@ -292,6 +473,10 @@ bool server_start(struct sway_server *server) {
292 } 473 }
293#endif 474#endif
294 475
476 if (config->primary_selection) {
477 wlr_primary_selection_v1_device_manager_create(server->wl_display);
478 }
479
295 sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", 480 sway_log(SWAY_INFO, "Starting backend on wayland display '%s'",
296 server->socket); 481 server->socket);
297 if (!wlr_backend_start(server->backend)) { 482 if (!wlr_backend_start(server->backend)) {
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index 8b702b77..442311bb 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -121,11 +121,16 @@ The following commands may only be used in the configuration file.
121 121
122*input* <identifier> map_from_region <X1xY1> <X2xY2> 122*input* <identifier> map_from_region <X1xY1> <X2xY2>
123 Ignores inputs from this device that do not occur within the specified 123 Ignores inputs from this device that do not occur within the specified
124 region. Can be in millimeters (e.g. 10x20mm 20x40mm) or in terms of 0..1 124 region. Can be in millimeters (e.g. 10x20mm 20x40mm) or the fraction of the
125 (e.g. 0.5x0.5 0.7x0.7). Not all devices support millimeters. Only meaningful 125 full available space in terms of 0..1 (e.g. 0.5x0.5 0.7x0.7). Not all
126 if the device is not a keyboard and provides events in absolute terms (such 126 devices support millimeters. Only meaningful if the device is not a
127 as a drawing tablet or touch screen - most pointers provide events relative 127 keyboard and provides events in absolute terms (such as a drawing tablet
128 to the previous frame). 128 or touch screen - most pointers provide events relative to the previous
129 frame).
130
131 Commonly used to maintain the aspect ratio of the input device and screen.
132 Cropping a 16:10 input region to match a 16:9 display can use 0x0 1x0.9 as
133 the argument.
129 134
130## LIBINPUT CONFIGURATION 135## LIBINPUT CONFIGURATION
131 136
@@ -147,6 +152,10 @@ The following commands may only be used in the configuration file.
147*input* <identifier> dwt enabled|disabled 152*input* <identifier> dwt enabled|disabled
148 Enables or disables disable-while-typing for the specified input device. 153 Enables or disables disable-while-typing for the specified input device.
149 154
155*input* <identifier> dwtp enabled|disabled
156 Enables or disables disable-while-trackpointing for the specified input
157 device.
158
150*input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>] 159*input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>]
151 Enables or disables send_events for specified input device. Disabling 160 Enables or disables send_events for specified input device. Disabling
152 send_events disables the input device. 161 send_events disables the input device.
@@ -171,12 +180,19 @@ The following commands may only be used in the configuration file.
171*input* <identifier> pointer_accel [<-1|1>] 180*input* <identifier> pointer_accel [<-1|1>]
172 Changes the pointer acceleration for the specified input device. 181 Changes the pointer acceleration for the specified input device.
173 182
183*input* <identifier> rotation_angle <angle>
184 Sets the rotation angle of the device to the given clockwise angle in
185 degrees. The angle must be between 0.0 (inclusive) and 360.0 (exclusive).
186
174*input* <identifier> scroll_button disable|button[1-3,8,9]|<event-code-or-name> 187*input* <identifier> scroll_button disable|button[1-3,8,9]|<event-code-or-name>
175 Sets the button used for scroll_method on_button_down. The button can 188 Sets the button used for scroll_method on_button_down. The button can
176 be given as an event name or code, which can be obtained from *libinput 189 be given as an event name or code, which can be obtained from *libinput
177 debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to 190 debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to
178 _disable_, it disables the scroll_method on_button_down. 191 _disable_, it disables the scroll_method on_button_down.
179 192
193*input* <identifier> scroll_button_lock enabled|disabled
194 Enables or disables scroll button lock for specified input device.
195
180*input* <identifier> scroll_factor <floating point value> 196*input* <identifier> scroll_factor <floating point value>
181 Changes the scroll factor for the specified input device. Scroll speed will 197 Changes the scroll factor for the specified input device. Scroll speed will
182 be scaled by the given value, which must be non-negative. 198 be scaled by the given value, which must be non-negative.
@@ -220,6 +236,8 @@ correct seat.
220 absolute coordinates (with respect to the global coordinate space). 236 absolute coordinates (with respect to the global coordinate space).
221 Specifying either value as 0 will not update that coordinate. 237 Specifying either value as 0 will not update that coordinate.
222 238
239 Deprecated: use the virtual-pointer Wayland protocol instead.
240
223*seat* <seat> cursor press|release button[1-9]|<event-name-or-code> 241*seat* <seat> cursor press|release button[1-9]|<event-name-or-code>
224 Simulate pressing (or releasing) the specified mouse button on the 242 Simulate pressing (or releasing) the specified mouse button on the
225 specified seat. The button can either be provided as a button event name or 243 specified seat. The button can either be provided as a button event name or
@@ -228,6 +246,8 @@ correct seat.
228 event will be simulated, however _press_ and _release_ will be ignored and 246 event will be simulated, however _press_ and _release_ will be ignored and
229 both will occur. 247 both will occur.
230 248
249 Deprecated: use the virtual-pointer Wayland protocol instead.
250
231*seat* <name> fallback true|false 251*seat* <name> fallback true|false
232 Set this seat as the fallback seat. A fallback seat will attach any device 252 Set this seat as the fallback seat. A fallback seat will attach any device
233 not explicitly attached to another seat (similar to a "default" seat). 253 not explicitly attached to another seat (similar to a "default" seat).
@@ -243,18 +263,16 @@ correct seat.
243 If _when-typing_ is enabled, then the cursor will be hidden whenever a key 263 If _when-typing_ is enabled, then the cursor will be hidden whenever a key
244 is pressed. 264 is pressed.
245 265
266 Be aware that this setting can interfere with input handling in games and
267 certain types of software (Gimp, Blender etc) that rely on simultaneous
268 input from mouse and keyboard.
269
246*seat* <name> idle_inhibit <sources...> 270*seat* <name> idle_inhibit <sources...>
247 Sets the set of input event sources which can prevent the seat from 271 Sets the set of input event sources which can prevent the seat from
248 becoming idle, as a space separated list of source names. Valid names are 272 becoming idle, as a space separated list of source names. Valid names are
249 "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", 273 "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool",
250 and "switch". The default behavior is to prevent idle on any event. 274 and "switch". The default behavior is to prevent idle on any event.
251 275
252*seat* <name> idle_wake <sources...>
253 Sets the set of input event sources which can wake the seat from
254 its idle state, as a space separated list of source names. Valid names are
255 "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool",
256 and "switch". The default behavior is to wake from idle on any event.
257
258*seat* <name> keyboard_grouping none|smart 276*seat* <name> keyboard_grouping none|smart
259 Set how the keyboards in the seat are grouped together. Currently, there 277 Set how the keyboards in the seat are grouped together. Currently, there
260 are two options. _none_ will disable all keyboard grouping. This will make 278 are two options. _none_ will disable all keyboard grouping. This will make
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd
index c7a2c473..2f697248 100644
--- a/sway/sway-ipc.7.scd
+++ b/sway/sway-ipc.7.scd
@@ -337,8 +337,9 @@ node and will have the following properties:
337 this, but borders are included. 337 this, but borders are included.
338|- window_rect 338|- window_rect
339: object 339: object
340: The geometry of the contents inside the node. The window decorations are 340: The geometry of the content inside the node. These coordinates are relative
341 excluded from this calculation, but borders are included. 341 to the node itself. Window decorations and borders are outside the
342 _window_rect_
342|- deco_rect 343|- deco_rect
343: object 344: object
344: The geometry of the decorations for the node relative to the parent node 345: The geometry of the decorations for the node relative to the parent node
@@ -375,6 +376,12 @@ node and will have the following properties:
375: integer 376: integer
376: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means 377: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means
377 full workspace, and 2 means global fullscreen 378 full workspace, and 2 means global fullscreen
379|- floating
380: string
381: Floating state of container. Can be either "auto_off" or "user_on"
382|- scratchpad_state
383: string
384: Whether the window is in the scratchpad. Can be either "none" or "fresh"
378|- app_id 385|- app_id
379: string 386: string
380: (Only views) For an xdg-shell view, the name of the application, if set. 387: (Only views) For an xdg-shell view, the name of the application, if set.
@@ -1039,7 +1046,7 @@ An object with a single string property containing the contents of the config
1039*Example Reply:* 1046*Example Reply:*
1040``` 1047```
1041{ 1048{
1042 "config": "set $mod Mod4\nbindsym $mod+q exit\n" 1049 "config": "set $mod Mod4\\nbindsym $mod+q exit\\n"
1043} 1050}
1044``` 1051```
1045 1052
@@ -1194,9 +1201,16 @@ following properties will be included for devices that support them:
1194: int 1201: int
1195: The scroll button to use when _scroll_method_ is _on_button_down_. This 1202: The scroll button to use when _scroll_method_ is _on_button_down_. This
1196 will be given as an input event code 1203 will be given as an input event code
1204|- scroll_button_lock
1205: string
1206: Whether scroll button lock is enabled. It can be _enabled_ or _disabled_
1197|- dwt 1207|- dwt
1198: string 1208: string
1199: Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ 1209: Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_
1210|- dwtp
1211: string
1212: Whether disable-while-trackpointing is enabled. It can be _enabled_ or
1213 _disabled_
1200|- calibration_matrix 1214|- calibration_matrix
1201: array 1215: array
1202: An array of 6 floats representing the calibration matrix for absolute 1216: An array of 6 floats representing the calibration matrix for absolute
@@ -1236,7 +1250,8 @@ following properties will be included for devices that support them:
1236 "click_method": "button_areas", 1250 "click_method": "button_areas",
1237 "middle_emulation": "disabled", 1251 "middle_emulation": "disabled",
1238 "scroll_method": "edge", 1252 "scroll_method": "edge",
1239 "dwt": "enabled" 1253 "dwt": "enabled",
1254 "dwtp": "enabled"
1240 } 1255 }
1241 }, 1256 },
1242 { 1257 {
@@ -1363,7 +1378,8 @@ one seat. Each object has the following properties:
1363 "click_method": "button_areas", 1378 "click_method": "button_areas",
1364 "middle_emulation": "disabled", 1379 "middle_emulation": "disabled",
1365 "scroll_method": "edge", 1380 "scroll_method": "edge",
1366 "dwt": "enabled" 1381 "dwt": "enabled",
1382 "dwtp": "enabled"
1367 } 1383 }
1368 }, 1384 },
1369 { 1385 {
@@ -1439,6 +1455,9 @@ available:
1439: workspace 1455: workspace
1440:[ Sent whenever an event involving a workspace occurs such as initialization 1456:[ Sent whenever an event involving a workspace occurs such as initialization
1441 of a new workspace or a different workspace gains focus 1457 of a new workspace or a different workspace gains focus
1458|- 0x80000001
1459: output
1460: Sent when outputs are updated
1442|- 0x80000002 1461|- 0x80000002
1443: mode 1462: mode
1444: Sent whenever the binding mode changes 1463: Sent whenever the binding mode changes
@@ -1559,6 +1578,20 @@ The following change types are currently available:
1559} 1578}
1560``` 1579```
1561 1580
1581## 0x80000001. OUTPUT
1582
1583Sent whenever an output is added, removed, or its configuration is changed.
1584The event is a single object with the property _change_, which is a string
1585containing the reason for the change. Currently, the only value for _change_ is
1586_unspecified_.
1587
1588*Example Event:*
1589```
1590{
1591 "change": "unspecified"
1592}
1593```
1594
1562## 0x80000002. MODE 1595## 0x80000002. MODE
1563 1596
1564Sent whenever the binding mode changes. The event consists of a single object 1597Sent whenever the binding mode changes. The event consists of a single object
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd
index b7d5e577..7d088d5d 100644
--- a/sway/sway-output.5.scd
+++ b/sway/sway-output.5.scd
@@ -72,13 +72,11 @@ must be separated by one space. For example:
72 72
73*output* <name> scale <factor> 73*output* <name> scale <factor>
74 Scales the specified output by the specified scale _factor_. An integer is 74 Scales the specified output by the specified scale _factor_. An integer is
75 recommended, but fractional values are also supported. If a fractional 75 recommended, but fractional values are also supported. You may be better
76 value are specified, be warned that it is not possible to faithfully 76 served by setting an integer scale factor and adjusting the font size of
77 represent the contents of your windows - they will be rendered at the next 77 your applications to taste. HiDPI isn't supported with Xwayland clients
78 highest integer scale factor and downscaled. You may be better served by 78 (windows will blur). A fractional scale may be slightly adjusted to match
79 setting an integer scale factor and adjusting the font size of your 79 requirements of the protocol.
80 applications to taste. HiDPI isn't supported with Xwayland clients (windows
81 will blur).
82 80
83*output* <name> scale_filter linear|nearest|smart 81*output* <name> scale_filter linear|nearest|smart
84 Indicates how to scale application buffers that are rendered at a scale 82 Indicates how to scale application buffers that are rendered at a scale
@@ -119,7 +117,7 @@ must be separated by one space. For example:
119 Enables or disables the specified output (all outputs are enabled by 117 Enables or disables the specified output (all outputs are enabled by
120 default). 118 default).
121 119
122 As opposed to the _power_ command, the output will loose its current 120 As opposed to the _power_ command, the output will lose its current
123 workspace and windows. 121 workspace and windows.
124 122
125*output* <name> toggle 123*output* <name> toggle
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index d8a462d3..9f823947 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -389,8 +389,8 @@ runtime.
389 for_window <criteria> move container to output <output> 389 for_window <criteria> move container to output <output>
390 390
391*bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ 391*bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
392[--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [Group<1-4>+]<key combo> \ 392[--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \
393<command> 393[Group<1-4>+]<key combo> <command>
394 Binds _key combo_ to execute the sway command _command_ when pressed. You 394 Binds _key combo_ to execute the sway command _command_ when pressed. You
395 may use XKB key names here (*wev*(1) is a good tool for discovering these). 395 may use XKB key names here (*wev*(1) is a good tool for discovering these).
396 With the flag _--release_, the command is executed when the key combo is 396 With the flag _--release_, the command is executed when the key combo is
@@ -400,6 +400,12 @@ runtime.
400 only be available for that group. By default, if you overwrite a binding, 400 only be available for that group. By default, if you overwrite a binding,
401 swaynag will give you a warning. To silence this, use the _--no-warn_ flag. 401 swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
402 402
403 For specifying modifier keys, you can use the XKB modifier names _Shift_,
404 _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock),
405 _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for
406 AltGr). In addition, you can use the aliases _Ctrl_ (for Control), _Alt_
407 (for Alt), and _Super_ (for the Logo key).
408
403 Unless the flag _--locked_ is set, the command will not be run when a 409 Unless the flag _--locked_ is set, the command will not be run when a
404 screen locking program is active. If there is a matching binding with 410 screen locking program is active. If there is a matching binding with
405 and without _--locked_, the one with will be preferred when locked and the 411 and without _--locked_, the one with will be preferred when locked and the
@@ -454,7 +460,8 @@ runtime.
454``` 460```
455 461
456 *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ 462 *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \
457[--locked] [--input-device=<device>] [--no-warn] [Group<1-4>+]<code> <command> 463[--locked] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \
464[Group<1-4>+]<code> <command>
458 is also available for binding with key/button codes instead of key/button names. 465 is also available for binding with key/button codes instead of key/button names.
459 466
460*bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> 467*bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command>
@@ -648,7 +655,8 @@ The default colors are:
648 655
649 656
650*default_border* normal|none|pixel [<n>] 657*default_border* normal|none|pixel [<n>]
651 Set default border style for new tiled windows. 658 Set default border style for new tiled windows. Config reload won't affect
659 existing windows, only newly created ones after the reload.
652 660
653*default_floating_border* normal|none|pixel [<n>] 661*default_floating_border* normal|none|pixel [<n>]
654 Set default border style for new floating windows. This only applies to 662 Set default border style for new floating windows. This only applies to
@@ -807,6 +815,10 @@ The default colors are:
807 dialog will not be rendered. If _leave_fullscreen_, the view will exit 815 dialog will not be rendered. If _leave_fullscreen_, the view will exit
808 fullscreen mode and the dialog will be rendered. 816 fullscreen mode and the dialog will be rendered.
809 817
818*primary_selection* enabled|disabled
819 Enable or disable the primary selection clipboard. May only be configured
820 at launch. Default is _enabled_.
821
810*set* $<name> <value> 822*set* $<name> <value>
811 Sets variable $_name_ to _value_. You can use the new variable in the 823 Sets variable $_name_ to _value_. You can use the new variable in the
812 arguments of future commands. When the variable is used, it can be escaped 824 arguments of future commands. When the variable is used, it can be escaped
@@ -957,6 +969,9 @@ properties in practice for your applications.
957 969
958The following attributes may be matched with: 970The following attributes may be matched with:
959 971
972*all*
973 Matches all windows.
974
960*app_id* 975*app_id*
961 Compare value against the app id. Can be a regular expression. If value is 976 Compare value against the app id. Can be a regular expression. If value is
962 \_\_focused\_\_, then the app id must be the same as that of the currently 977 \_\_focused\_\_, then the app id must be the same as that of the currently
@@ -965,7 +980,8 @@ The following attributes may be matched with:
965*class* 980*class*
966 Compare value against the window class. Can be a regular expression. If 981 Compare value against the window class. Can be a regular expression. If
967 value is \_\_focused\_\_, then the window class must be the same as that of 982 value is \_\_focused\_\_, then the window class must be the same as that of
968 the currently focused window. _class_ are specific to X11 applications. 983 the currently focused window. _class_ are specific to X11 applications and
984 require XWayland.
969 985
970*con_id* 986*con_id*
971 Compare against the internal container ID, which you can find via IPC. If 987 Compare against the internal container ID, which you can find via IPC. If
@@ -979,12 +995,14 @@ The following attributes may be matched with:
979 Matches floating windows. 995 Matches floating windows.
980 996
981*id* 997*id*
982 Compare value against the X11 window ID. Must be numeric. 998 Compare value against the X11 window ID. Must be numeric. id is specific to
999 X11 applications and requires XWayland.
983 1000
984*instance* 1001*instance*
985 Compare value against the window instance. Can be a regular expression. If 1002 Compare value against the window instance. Can be a regular expression. If
986 value is \_\_focused\_\_, then the window instance must be the same as that 1003 value is \_\_focused\_\_, then the window instance must be the same as that
987 of the currently focused window. 1004 of the currently focused window. instance is specific to X11 applications and
1005 requires XWayland.
988 1006
989*pid* 1007*pid*
990 Compare value against the window's process ID. Must be numeric. 1008 Compare value against the window's process ID. Must be numeric.
@@ -1009,12 +1027,14 @@ The following attributes may be matched with:
1009*window_role* 1027*window_role*
1010 Compare against the window role (WM_WINDOW_ROLE). Can be a regular 1028 Compare against the window role (WM_WINDOW_ROLE). Can be a regular
1011 expression. If value is \_\_focused\_\_, then the window role must be the 1029 expression. If value is \_\_focused\_\_, then the window role must be the
1012 same as that of the currently focused window. 1030 same as that of the currently focused window. window_role is specific to X11
1031 applications and requires XWayland.
1013 1032
1014*window_type* 1033*window_type*
1015 Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values 1034 Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values
1016 are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, 1035 are normal, dialog, utility, toolbar, splash, menu, dropdown_menu,
1017 popup_menu, tooltip and notification. 1036 popup_menu, tooltip and notification. window_type is specific to X11
1037 applications and requires XWayland.
1018 1038
1019*workspace* 1039*workspace*
1020 Compare against the workspace name for this view. Can be a regular 1040 Compare against the workspace name for this view. Can be a regular
diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c
new file mode 100644
index 00000000..5eba53ba
--- /dev/null
+++ b/sway/sway_text_node.c
@@ -0,0 +1,302 @@
1#include <drm_fourcc.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <wlr/types/wlr_buffer.h>
6#include <wlr/interfaces/wlr_buffer.h>
7#include "cairo_util.h"
8#include "log.h"
9#include "pango.h"
10#include "sway/config.h"
11#include "sway/sway_text_node.h"
12
13struct cairo_buffer {
14 struct wlr_buffer base;
15 cairo_surface_t *surface;
16 cairo_t *cairo;
17};
18
19static 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
27static 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
36static void cairo_buffer_handle_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {
37 // This space is intentionally left blank
38}
39
40static 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
46struct 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
59static 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
67static 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
79static 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
156err:
157 if (pango) g_object_unref(pango);
158 cairo_font_options_destroy(fo);
159}
160
161static 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
196static 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
206static 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
224struct 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
260void 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
271void 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
289void 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
298void sway_text_node_set_background(struct sway_text_node *node, float background[4]) {
299 struct text_buffer *buffer = wl_container_of(node, buffer, props);
300 memcpy(&node->background, background, sizeof(*background) * 4);
301 render_backing_buffer(buffer);
302}
diff --git a/sway/swaynag.c b/sway/swaynag.c
index 4a0a6d30..bc5e23ea 100644
--- a/sway/swaynag.c
+++ b/sway/swaynag.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <signal.h> 1#include <signal.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -145,22 +144,16 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag,
145 144
146 va_list args; 145 va_list args;
147 va_start(args, fmt); 146 va_start(args, fmt);
148 size_t length = vsnprintf(NULL, 0, fmt, args) + 1; 147 char *str = vformat_str(fmt, args);
149 va_end(args); 148 va_end(args);
150 149 if (!str) {
151 char *temp = malloc(length + 1);
152 if (!temp) {
153 sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry."); 150 sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry.");
154 return; 151 return;
155 } 152 }
156 153
157 va_start(args, fmt); 154 write(swaynag->fd[1], str, strlen(str));
158 vsnprintf(temp, length, fmt, args);
159 va_end(args);
160
161 write(swaynag->fd[1], temp, length);
162 155
163 free(temp); 156 free(str);
164} 157}
165 158
166void swaynag_show(struct swaynag_instance *swaynag) { 159void swaynag_show(struct swaynag_instance *swaynag) {
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 9c1a11e5..d4003fe6 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -264,6 +263,9 @@ void arrange_workspace(struct sway_workspace *workspace) {
264 area->width, area->height, area->x, area->y); 263 area->width, area->height, area->x, area->y);
265 264
266 bool first_arrange = workspace->width == 0 && workspace->height == 0; 265 bool first_arrange = workspace->width == 0 && workspace->height == 0;
266 struct wlr_box prev_box;
267 workspace_get_box(workspace, &prev_box);
268
267 double prev_x = workspace->x - workspace->current_gaps.left; 269 double prev_x = workspace->x - workspace->current_gaps.left;
268 double prev_y = workspace->y - workspace->current_gaps.top; 270 double prev_y = workspace->y - workspace->current_gaps.top;
269 workspace->width = area->width; 271 workspace->width = area->width;
@@ -277,13 +279,14 @@ void arrange_workspace(struct sway_workspace *workspace) {
277 if (!first_arrange && (diff_x != 0 || diff_y != 0)) { 279 if (!first_arrange && (diff_x != 0 || diff_y != 0)) {
278 for (int i = 0; i < workspace->floating->length; ++i) { 280 for (int i = 0; i < workspace->floating->length; ++i) {
279 struct sway_container *floater = workspace->floating->items[i]; 281 struct sway_container *floater = workspace->floating->items[i];
280 container_floating_translate(floater, diff_x, diff_y);
281 double center_x = floater->pending.x + floater->pending.width / 2;
282 double center_y = floater->pending.y + floater->pending.height / 2;
283 struct wlr_box workspace_box; 282 struct wlr_box workspace_box;
284 workspace_get_box(workspace, &workspace_box); 283 workspace_get_box(workspace, &workspace_box);
285 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { 284 floating_fix_coordinates(floater, &prev_box, &workspace_box);
286 container_floating_move_to_center(floater); 285 // Set transformation for scratchpad windows.
286 if (floater->scratchpad) {
287 struct wlr_box output_box;
288 output_get_box(output, &output_box);
289 floater->transform = output_box;
287 } 290 }
288 } 291 }
289 } 292 }
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 04ef965f..9224b4fb 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1,25 +1,20 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <drm_fourcc.h> 2#include <drm_fourcc.h>
4#include <stdint.h> 3#include <stdint.h>
5#include <stdlib.h> 4#include <stdlib.h>
6#include <string.h>
7#include <strings.h>
8#include <sys/stat.h>
9#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
10#include <wlr/types/wlr_linux_dmabuf_v1.h> 7#include <wlr/types/wlr_linux_dmabuf_v1.h>
11#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_subcompositor.h> 9#include <wlr/types/wlr_subcompositor.h>
13#include <wlr/render/drm_format_set.h>
14#include "linux-dmabuf-unstable-v1-protocol.h" 10#include "linux-dmabuf-unstable-v1-protocol.h"
15#include "cairo_util.h"
16#include "pango.h"
17#include "sway/config.h" 11#include "sway/config.h"
18#include "sway/desktop.h"
19#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
20#include "sway/input/input-manager.h" 13#include "sway/input/input-manager.h"
21#include "sway/input/seat.h" 14#include "sway/input/seat.h"
22#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
16#include "sway/scene_descriptor.h"
17#include "sway/sway_text_node.h"
23#include "sway/output.h" 18#include "sway/output.h"
24#include "sway/server.h" 19#include "sway/server.h"
25#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
@@ -30,6 +25,53 @@
30#include "log.h" 25#include "log.h"
31#include "stringop.h" 26#include "stringop.h"
32 27
28static 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
40static 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
52static bool handle_point_accepts_input(
53 struct wlr_scene_buffer *buffer, double *x, double *y) {
54 return false;
55}
56
57static 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
33struct sway_container *container_create(struct sway_view *view) { 75struct sway_container *container_create(struct sway_view *view) {
34 struct sway_container *c = calloc(1, sizeof(struct sway_container)); 76 struct sway_container *c = calloc(1, sizeof(struct sway_container));
35 if (!c) { 77 if (!c) {
@@ -37,23 +79,411 @@ struct sway_container *container_create(struct sway_view *view) {
37 return NULL; 79 return NULL;
38 } 80 }
39 node_init(&c->node, N_CONTAINER, c); 81 node_init(&c->node, N_CONTAINER, c);
40 c->pending.layout = L_NONE; 82
41 c->view = view; 83 // Container tree structure
42 c->alpha = 1.0f; 84 // - scene tree
85 // - title bar
86 // - border
87 // - background
88 // - title text
89 // - marks text
90 // - border
91 // - border top/bottom/left/right
92 // - content_tree (we put the content node here so when we disable the
93 // border everything gets disabled. We only render the content iff there
94 // is a border as well)
95 // - buffer used for output enter/leave events for foreign_toplevel
96 bool failed = false;
97 c->scene_tree = alloc_scene_tree(root->staging, &failed);
98
99 c->title_bar.tree = alloc_scene_tree(c->scene_tree, &failed);
100 c->title_bar.border = alloc_scene_tree(c->title_bar.tree, &failed);
101 c->title_bar.background = alloc_scene_tree(c->title_bar.tree, &failed);
102
103 // for opacity purposes we need to carfully create the scene such that
104 // none of our rect nodes as well as text buffers don't overlap. To do
105 // this we have to create rects such that they go around text buffers
106 for (int i = 0; i < 4; i++) {
107 alloc_rect_node(c->title_bar.border, &failed);
108 }
109
110 for (int i = 0; i < 5; i++) {
111 alloc_rect_node(c->title_bar.background, &failed);
112 }
113
114 c->border.tree = alloc_scene_tree(c->scene_tree, &failed);
115 c->content_tree = alloc_scene_tree(c->border.tree, &failed);
116
117 if (view) {
118 // only containers with views can have borders
119 c->border.top = alloc_rect_node(c->border.tree, &failed);
120 c->border.bottom = alloc_rect_node(c->border.tree, &failed);
121 c->border.left = alloc_rect_node(c->border.tree, &failed);
122 c->border.right = alloc_rect_node(c->border.tree, &failed);
123
124 c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL);
125 if (!c->output_handler) {
126 sway_log(SWAY_ERROR, "Failed to allocate a scene node");
127 failed = true;
128 }
129
130 if (!failed) {
131 c->output_enter.notify = handle_output_enter;
132 wl_signal_add(&c->output_handler->events.output_enter,
133 &c->output_enter);
134 c->output_leave.notify = handle_output_leave;
135 wl_signal_add(&c->output_handler->events.output_leave,
136 &c->output_leave);
137 c->output_handler->point_accepts_input = handle_point_accepts_input;
138 }
139 }
140
141 if (!failed && !scene_descriptor_assign(&c->scene_tree->node,
142 SWAY_SCENE_DESC_CONTAINER, c)) {
143 failed = true;
144 }
145
146 if (failed) {
147 wlr_scene_node_destroy(&c->scene_tree->node);
148 free(c);
149 return NULL;
150 }
43 151
44 if (!view) { 152 if (!view) {
45 c->pending.children = create_list(); 153 c->pending.children = create_list();
46 c->current.children = create_list(); 154 c->current.children = create_list();
47 } 155 }
156
157 c->pending.layout = L_NONE;
158 c->view = view;
159 c->alpha = 1.0f;
48 c->marks = create_list(); 160 c->marks = create_list();
49 c->outputs = create_list();
50 161
51 wl_signal_init(&c->events.destroy); 162 wl_signal_init(&c->events.destroy);
52 wl_signal_emit(&root->events.new_node, &c->node); 163 wl_signal_emit_mutable(&root->events.new_node, &c->node);
164
165 container_update(c);
53 166
54 return c; 167 return c;
55} 168}
56 169
170static bool container_is_focused(struct sway_container *con, void *data) {
171 return con->current.focused;
172}
173
174static bool container_has_focused_child(struct sway_container *con) {
175 return container_find_child(con, container_is_focused, NULL);
176}
177
178static 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
190static 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
221static 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
233static 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
245void 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
300void 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
308static 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
331void 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
414void 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
461void 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
57void container_destroy(struct sway_container *con) { 487void container_destroy(struct sway_container *con) {
58 if (!sway_assert(con->node.destroying, 488 if (!sway_assert(con->node.destroying,
59 "Tried to free container which wasn't marked as destroying")) { 489 "Tried to free container which wasn't marked as destroying")) {
@@ -65,29 +495,21 @@ void container_destroy(struct sway_container *con) {
65 } 495 }
66 free(con->title); 496 free(con->title);
67 free(con->formatted_title); 497 free(con->formatted_title);
68 wlr_texture_destroy(con->title_focused);
69 wlr_texture_destroy(con->title_focused_inactive);
70 wlr_texture_destroy(con->title_unfocused);
71 wlr_texture_destroy(con->title_urgent);
72 wlr_texture_destroy(con->title_focused_tab_title);
73 list_free(con->pending.children); 498 list_free(con->pending.children);
74 list_free(con->current.children); 499 list_free(con->current.children);
75 list_free(con->outputs);
76 500
77 list_free_items_and_destroy(con->marks); 501 list_free_items_and_destroy(con->marks);
78 wlr_texture_destroy(con->marks_focused);
79 wlr_texture_destroy(con->marks_focused_inactive);
80 wlr_texture_destroy(con->marks_unfocused);
81 wlr_texture_destroy(con->marks_urgent);
82 wlr_texture_destroy(con->marks_focused_tab_title);
83 502
84 if (con->view && con->view->container == con) { 503 if (con->view && con->view->container == con) {
85 con->view->container = NULL; 504 con->view->container = NULL;
505 wlr_scene_node_destroy(&con->output_handler->node);
86 if (con->view->destroying) { 506 if (con->view->destroying) {
87 view_destroy(con->view); 507 view_destroy(con->view);
88 } 508 }
89 } 509 }
90 510
511 scene_node_disown_children(con->content_tree);
512 wlr_scene_node_destroy(&con->scene_tree->node);
91 free(con); 513 free(con);
92} 514}
93 515
@@ -104,7 +526,7 @@ void container_begin_destroy(struct sway_container *con) {
104 container_fullscreen_disable(con); 526 container_fullscreen_disable(con);
105 } 527 }
106 528
107 wl_signal_emit(&con->node.events.destroy, &con->node); 529 wl_signal_emit_mutable(&con->node.events.destroy, &con->node);
108 530
109 container_end_mouse_operation(con); 531 container_end_mouse_operation(con);
110 532
@@ -174,265 +596,6 @@ struct sway_container *container_find_child(struct sway_container *container,
174 return NULL; 596 return NULL;
175} 597}
176 598
177static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly,
178 struct wlr_surface **surface, double *sx, double *sy) {
179 if (!sway_assert(con->view, "Expected a view")) {
180 return NULL;
181 }
182 struct sway_view *view = con->view;
183 double view_sx = lx - con->surface_x + view->geometry.x;
184 double view_sy = ly - con->surface_y + view->geometry.y;
185
186 double _sx, _sy;
187 struct wlr_surface *_surface = NULL;
188 switch (view->type) {
189#if HAVE_XWAYLAND
190 case SWAY_VIEW_XWAYLAND:
191 _surface = wlr_surface_surface_at(view->surface,
192 view_sx, view_sy, &_sx, &_sy);
193 break;
194#endif
195 case SWAY_VIEW_XDG_SHELL:
196 _surface = wlr_xdg_surface_surface_at(
197 view->wlr_xdg_toplevel->base,
198 view_sx, view_sy, &_sx, &_sy);
199 break;
200 }
201 if (_surface) {
202 *sx = _sx;
203 *sy = _sy;
204 *surface = _surface;
205 return con;
206 }
207 return NULL;
208}
209
210/**
211 * container_at for a container with layout L_TABBED.
212 */
213static struct sway_container *container_at_tabbed(struct sway_node *parent,
214 double lx, double ly,
215 struct wlr_surface **surface, double *sx, double *sy) {
216 struct wlr_box box;
217 node_get_box(parent, &box);
218 if (lx < box.x || lx > box.x + box.width ||
219 ly < box.y || ly > box.y + box.height) {
220 return NULL;
221 }
222 struct sway_seat *seat = input_manager_current_seat();
223 list_t *children = node_get_children(parent);
224 if (!children->length) {
225 return NULL;
226 }
227
228 // Tab titles
229 int title_height = container_titlebar_height();
230 if (ly < box.y + title_height) {
231 int tab_width = box.width / children->length;
232 int child_index = (lx - box.x) / tab_width;
233 if (child_index >= children->length) {
234 child_index = children->length - 1;
235 }
236 struct sway_container *child = children->items[child_index];
237 return child;
238 }
239
240 // Surfaces
241 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
242 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
243}
244
245/**
246 * container_at for a container with layout L_STACKED.
247 */
248static struct sway_container *container_at_stacked(struct sway_node *parent,
249 double lx, double ly,
250 struct wlr_surface **surface, double *sx, double *sy) {
251 struct wlr_box box;
252 node_get_box(parent, &box);
253 if (lx < box.x || lx > box.x + box.width ||
254 ly < box.y || ly > box.y + box.height) {
255 return NULL;
256 }
257 struct sway_seat *seat = input_manager_current_seat();
258 list_t *children = node_get_children(parent);
259
260 // Title bars
261 int title_height = container_titlebar_height();
262 if (title_height > 0) {
263 int child_index = (ly - box.y) / title_height;
264 if (child_index < children->length) {
265 struct sway_container *child = children->items[child_index];
266 return child;
267 }
268 }
269
270 // Surfaces
271 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
272 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
273}
274
275/**
276 * container_at for a container with layout L_HORIZ or L_VERT.
277 */
278static struct sway_container *container_at_linear(struct sway_node *parent,
279 double lx, double ly,
280 struct wlr_surface **surface, double *sx, double *sy) {
281 list_t *children = node_get_children(parent);
282 for (int i = 0; i < children->length; ++i) {
283 struct sway_container *child = children->items[i];
284 struct sway_container *container =
285 tiling_container_at(&child->node, lx, ly, surface, sx, sy);
286 if (container) {
287 return container;
288 }
289 }
290 return NULL;
291}
292
293static struct sway_container *floating_container_at(double lx, double ly,
294 struct wlr_surface **surface, double *sx, double *sy) {
295 // For outputs with floating containers that overhang the output bounds,
296 // those at the end of the output list appear on top of floating
297 // containers from other outputs, so iterate the list in reverse.
298 for (int i = root->outputs->length - 1; i >= 0; --i) {
299 struct sway_output *output = root->outputs->items[i];
300 for (int j = 0; j < output->workspaces->length; ++j) {
301 struct sway_workspace *ws = output->workspaces->items[j];
302 if (!workspace_is_visible(ws)) {
303 continue;
304 }
305 // Items at the end of the list are on top, so iterate the list in
306 // reverse.
307 for (int k = ws->floating->length - 1; k >= 0; --k) {
308 struct sway_container *floater = ws->floating->items[k];
309 struct sway_container *container =
310 tiling_container_at(&floater->node, lx, ly, surface, sx, sy);
311 if (container) {
312 return container;
313 }
314 }
315 }
316 }
317 return NULL;
318}
319
320static struct sway_container *view_container_content_at(struct sway_node *parent,
321 double lx, double ly,
322 struct wlr_surface **surface, double *sx, double *sy) {
323 if (!sway_assert(node_is_view(parent), "Expected a view")) {
324 return NULL;
325 }
326
327 struct sway_container *container = parent->sway_container;
328 struct wlr_box box = {
329 .x = container->pending.content_x,
330 .y = container->pending.content_y,
331 .width = container->pending.content_width,
332 .height = container->pending.content_height,
333 };
334
335 if (wlr_box_contains_point(&box, lx, ly)) {
336 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
337 return container;
338 }
339
340 return NULL;
341}
342
343static struct sway_container *view_container_at(struct sway_node *parent,
344 double lx, double ly,
345 struct wlr_surface **surface, double *sx, double *sy) {
346 if (!sway_assert(node_is_view(parent), "Expected a view")) {
347 return NULL;
348 }
349
350 struct sway_container *container = parent->sway_container;
351 struct wlr_box box = {
352 .x = container->pending.x,
353 .y = container->pending.y,
354 .width = container->pending.width,
355 .height = container->pending.height,
356 };
357
358 if (wlr_box_contains_point(&box, lx, ly)) {
359 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
360 return container;
361 }
362
363 return NULL;
364}
365
366struct sway_container *tiling_container_at(struct sway_node *parent,
367 double lx, double ly,
368 struct wlr_surface **surface, double *sx, double *sy) {
369 if (node_is_view(parent)) {
370 return view_container_at(parent, lx, ly, surface, sx, sy);
371 }
372 if (!node_get_children(parent)) {
373 return NULL;
374 }
375 switch (node_get_layout(parent)) {
376 case L_HORIZ:
377 case L_VERT:
378 return container_at_linear(parent, lx, ly, surface, sx, sy);
379 case L_TABBED:
380 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
381 case L_STACKED:
382 return container_at_stacked(parent, lx, ly, surface, sx, sy);
383 case L_NONE:
384 return NULL;
385 }
386 return NULL;
387}
388
389static bool surface_is_popup(struct wlr_surface *surface) {
390 while (!wlr_surface_is_xdg_surface(surface)) {
391 if (!wlr_surface_is_subsurface(surface)) {
392 return false;
393 }
394 struct wlr_subsurface *subsurface =
395 wlr_subsurface_from_wlr_surface(surface);
396 surface = subsurface->parent;
397 }
398 struct wlr_xdg_surface *xdg_surface =
399 wlr_xdg_surface_from_wlr_surface(surface);
400 return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP;
401}
402
403struct sway_container *container_at(struct sway_workspace *workspace,
404 double lx, double ly,
405 struct wlr_surface **surface, double *sx, double *sy) {
406 struct sway_container *c;
407
408 struct sway_seat *seat = input_manager_current_seat();
409 struct sway_container *focus = seat_get_focused_container(seat);
410 bool is_floating = focus && container_is_floating_or_child(focus);
411 // Focused view's popups
412 if (focus && focus->view) {
413 c = surface_at_view(focus, lx, ly, surface, sx, sy);
414 if (c && surface_is_popup(*surface)) {
415 return c;
416 }
417 *surface = NULL;
418 }
419 // Floating
420 if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) {
421 return c;
422 }
423 // Tiling (focused)
424 if (focus && focus->view && !is_floating) {
425 if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) {
426 return c;
427 }
428 }
429 // Tiling (non-focused)
430 if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) {
431 return c;
432 }
433 return NULL;
434}
435
436void container_for_each_child(struct sway_container *container, 599void container_for_each_child(struct sway_container *container,
437 void (*f)(struct sway_container *container, void *data), 600 void (*f)(struct sway_container *container, void *data),
438 void *data) { 601 void *data) {
@@ -478,127 +641,6 @@ bool container_has_ancestor(struct sway_container *descendant,
478 return false; 641 return false;
479} 642}
480 643
481void container_damage_whole(struct sway_container *container) {
482 for (int i = 0; i < root->outputs->length; ++i) {
483 struct sway_output *output = root->outputs->items[i];
484 output_damage_whole_container(output, container);
485 }
486}
487
488/**
489 * Return the output which will be used for scale purposes.
490 * This is the most recently entered output.
491 */
492struct sway_output *container_get_effective_output(struct sway_container *con) {
493 if (con->outputs->length == 0) {
494 return NULL;
495 }
496 return con->outputs->items[con->outputs->length - 1];
497}
498
499static void render_titlebar_text_texture(struct sway_output *output,
500 struct sway_container *con, struct wlr_texture **texture,
501 struct border_colors *class, bool pango_markup, char *text) {
502 double scale = output->wlr_output->scale;
503 int width = 0;
504 int height = config->font_height * scale;
505 int baseline;
506
507 // We must use a non-nil cairo_t for cairo_set_font_options to work.
508 // Therefore, we cannot use cairo_create(NULL).
509 cairo_surface_t *dummy_surface = cairo_image_surface_create(
510 CAIRO_FORMAT_ARGB32, 0, 0);
511 cairo_t *c = cairo_create(dummy_surface);
512 cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
513 cairo_font_options_t *fo = cairo_font_options_create();
514 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
515 if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
516 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
517 } else {
518 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
519 cairo_font_options_set_subpixel_order(fo,
520 to_cairo_subpixel_order(output->wlr_output->subpixel));
521 }
522 cairo_set_font_options(c, fo);
523 get_text_size(c, config->font_description, &width, NULL, &baseline, scale,
524 config->pango_markup, "%s", text);
525 cairo_surface_destroy(dummy_surface);
526 cairo_destroy(c);
527
528 if (width == 0 || height == 0) {
529 return;
530 }
531
532 if (height > config->font_height * scale) {
533 height = config->font_height * scale;
534 }
535
536 cairo_surface_t *surface = cairo_image_surface_create(
537 CAIRO_FORMAT_ARGB32, width, height);
538 cairo_status_t status = cairo_surface_status(surface);
539 if (status != CAIRO_STATUS_SUCCESS) {
540 sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s",
541 cairo_status_to_string(status));
542 return;
543 }
544
545 cairo_t *cairo = cairo_create(surface);
546 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
547 cairo_set_font_options(cairo, fo);
548 cairo_font_options_destroy(fo);
549 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
550 class->background[2], class->background[3]);
551 cairo_paint(cairo);
552 PangoContext *pango = pango_cairo_create_context(cairo);
553 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
554 class->text[2], class->text[3]);
555 cairo_move_to(cairo, 0, config->font_baseline * scale - baseline);
556
557 render_text(cairo, config->font_description, scale, pango_markup, "%s", text);
558
559 cairo_surface_flush(surface);
560 unsigned char *data = cairo_image_surface_get_data(surface);
561 int stride = cairo_image_surface_get_stride(surface);
562 struct wlr_renderer *renderer = output->wlr_output->renderer;
563 *texture = wlr_texture_from_pixels(
564 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
565 cairo_surface_destroy(surface);
566 g_object_unref(pango);
567 cairo_destroy(cairo);
568}
569
570static void update_title_texture(struct sway_container *con,
571 struct wlr_texture **texture, struct border_colors *class) {
572 struct sway_output *output = container_get_effective_output(con);
573 if (!output) {
574 return;
575 }
576 if (*texture) {
577 wlr_texture_destroy(*texture);
578 *texture = NULL;
579 }
580 if (!con->formatted_title) {
581 return;
582 }
583
584 render_titlebar_text_texture(output, con, texture, class,
585 config->pango_markup, con->formatted_title);
586}
587
588void container_update_title_textures(struct sway_container *container) {
589 update_title_texture(container, &container->title_focused,
590 &config->border_colors.focused);
591 update_title_texture(container, &container->title_focused_inactive,
592 &config->border_colors.focused_inactive);
593 update_title_texture(container, &container->title_unfocused,
594 &config->border_colors.unfocused);
595 update_title_texture(container, &container->title_urgent,
596 &config->border_colors.urgent);
597 update_title_texture(container, &container->title_focused_tab_title,
598 &config->border_colors.focused_tab_title);
599 container_damage_whole(container);
600}
601
602/** 644/**
603 * Calculate and return the length of the tree representation. 645 * Calculate and return the length of the tree representation.
604 * An example tree representation is: V[Terminal, Firefox] 646 * An example tree representation is: V[Terminal, Firefox]
@@ -664,7 +706,13 @@ void container_update_representation(struct sway_container *con) {
664 } 706 }
665 container_build_representation(con->pending.layout, con->pending.children, 707 container_build_representation(con->pending.layout, con->pending.children,
666 con->formatted_title); 708 con->formatted_title);
667 container_update_title_textures(con); 709
710 if (con->title_bar.title_text) {
711 sway_text_node_set_text(con->title_bar.title_text, con->formatted_title);
712 container_arrange_title_bar(con);
713 } else {
714 container_update_title_bar(con);
715 }
668 } 716 }
669 if (con->pending.parent) { 717 if (con->pending.parent) {
670 container_update_representation(con->pending.parent); 718 container_update_representation(con->pending.parent);
@@ -716,6 +764,21 @@ void floating_calculate_constraints(int *min_width, int *max_width,
716 764
717} 765}
718 766
767void 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
719static void floating_natural_resize(struct sway_container *con) { 782static void floating_natural_resize(struct sway_container *con) {
720 int min_width, max_width, min_height, max_height; 783 int min_width, max_width, min_height, max_height;
721 floating_calculate_constraints(&min_width, &max_width, 784 floating_calculate_constraints(&min_width, &max_width,
@@ -787,11 +850,11 @@ void container_floating_set_default_size(struct sway_container *con) {
787 int min_width, max_width, min_height, max_height; 850 int min_width, max_width, min_height, max_height;
788 floating_calculate_constraints(&min_width, &max_width, 851 floating_calculate_constraints(&min_width, &max_width,
789 &min_height, &max_height); 852 &min_height, &max_height);
790 struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); 853 struct wlr_box box;
791 workspace_get_box(con->pending.workspace, box); 854 workspace_get_box(con->pending.workspace, &box);
792 855
793 double width = fmax(min_width, fmin(box->width * 0.5, max_width)); 856 double width = fmax(min_width, fmin(box.width * 0.5, max_width));
794 double height = fmax(min_height, fmin(box->height * 0.75, max_height)); 857 double height = fmax(min_height, fmin(box.height * 0.75, max_height));
795 if (!con->view) { 858 if (!con->view) {
796 con->pending.width = width; 859 con->pending.width = width;
797 con->pending.height = height; 860 con->pending.height = height;
@@ -800,8 +863,6 @@ void container_floating_set_default_size(struct sway_container *con) {
800 con->pending.content_height = height; 863 con->pending.content_height = height;
801 container_set_geometry_from_content(con); 864 container_set_geometry_from_content(con);
802 } 865 }
803
804 free(box);
805} 866}
806 867
807 868
@@ -937,17 +998,6 @@ bool container_is_floating(struct sway_container *container) {
937 return false; 998 return false;
938} 999}
939 1000
940bool container_is_current_floating(struct sway_container *container) {
941 if (!container->current.parent && container->current.workspace &&
942 list_find(container->current.workspace->floating, container) != -1) {
943 return true;
944 }
945 if (container->scratchpad) {
946 return true;
947 }
948 return false;
949}
950
951void container_get_box(struct sway_container *container, struct wlr_box *box) { 1001void container_get_box(struct sway_container *container, struct wlr_box *box) {
952 box->x = container->pending.x; 1002 box->x = container->pending.x;
953 box->y = container->pending.y; 1003 box->y = container->pending.y;
@@ -1031,6 +1081,13 @@ void container_floating_move_to(struct sway_container *con,
1031 workspace_add_floating(new_workspace, con); 1081 workspace_add_floating(new_workspace, con);
1032 arrange_workspace(old_workspace); 1082 arrange_workspace(old_workspace);
1033 arrange_workspace(new_workspace); 1083 arrange_workspace(new_workspace);
1084 // If the moved container was a visible scratchpad container, then
1085 // update its transform.
1086 if (con->scratchpad) {
1087 struct wlr_box output_box;
1088 output_get_box(new_output, &output_box);
1089 con->transform = output_box;
1090 }
1034 workspace_detect_urgent(old_workspace); 1091 workspace_detect_urgent(old_workspace);
1035 workspace_detect_urgent(new_workspace); 1092 workspace_detect_urgent(new_workspace);
1036 } 1093 }
@@ -1062,16 +1119,6 @@ void container_end_mouse_operation(struct sway_container *container) {
1062 } 1119 }
1063} 1120}
1064 1121
1065static bool devid_from_fd(int fd, dev_t *devid) {
1066 struct stat stat;
1067 if (fstat(fd, &stat) != 0) {
1068 sway_log_errno(SWAY_ERROR, "fstat failed");
1069 return false;
1070 }
1071 *devid = stat.st_rdev;
1072 return true;
1073}
1074
1075static void set_fullscreen(struct sway_container *con, bool enable) { 1122static void set_fullscreen(struct sway_container *con, bool enable) {
1076 if (!con->view) { 1123 if (!con->view) {
1077 return; 1124 return;
@@ -1083,75 +1130,6 @@ static void set_fullscreen(struct sway_container *con, bool enable) {
1083 con->view->foreign_toplevel, enable); 1130 con->view->foreign_toplevel, enable);
1084 } 1131 }
1085 } 1132 }
1086
1087 if (!server.linux_dmabuf_v1 || !con->view->surface) {
1088 return;
1089 }
1090 if (!enable) {
1091 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1092 con->view->surface, NULL);
1093 return;
1094 }
1095
1096 if (!con->pending.workspace || !con->pending.workspace->output) {
1097 return;
1098 }
1099
1100 struct sway_output *output = con->pending.workspace->output;
1101 struct wlr_output *wlr_output = output->wlr_output;
1102
1103 // TODO: add wlroots helpers for all of this stuff
1104
1105 const struct wlr_drm_format_set *renderer_formats =
1106 wlr_renderer_get_dmabuf_texture_formats(server.renderer);
1107 assert(renderer_formats);
1108
1109 int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer);
1110 int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend);
1111 if (renderer_drm_fd < 0 || backend_drm_fd < 0) {
1112 return;
1113 }
1114
1115 dev_t render_dev, scanout_dev;
1116 if (!devid_from_fd(renderer_drm_fd, &render_dev) ||
1117 !devid_from_fd(backend_drm_fd, &scanout_dev)) {
1118 return;
1119 }
1120
1121 const struct wlr_drm_format_set *output_formats =
1122 wlr_output_get_primary_formats(output->wlr_output,
1123 WLR_BUFFER_CAP_DMABUF);
1124 if (!output_formats) {
1125 return;
1126 }
1127
1128 struct wlr_drm_format_set scanout_formats = {0};
1129 if (!wlr_drm_format_set_intersect(&scanout_formats,
1130 output_formats, renderer_formats)) {
1131 return;
1132 }
1133
1134 struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = {
1135 {
1136 .target_device = scanout_dev,
1137 .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
1138 .formats = &scanout_formats,
1139 },
1140 {
1141 .target_device = render_dev,
1142 .formats = renderer_formats,
1143 },
1144 };
1145
1146 const struct wlr_linux_dmabuf_feedback_v1 feedback = {
1147 .main_device = render_dev,
1148 .tranches = tranches,
1149 .tranches_len = sizeof(tranches) / sizeof(tranches[0]),
1150 };
1151 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1152 con->view->surface, &feedback);
1153
1154 wlr_drm_format_set_finish(&scanout_formats);
1155} 1133}
1156 1134
1157static void container_fullscreen_workspace(struct sway_container *con) { 1135static void container_fullscreen_workspace(struct sway_container *con) {
@@ -1321,72 +1299,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) {
1321 return false; 1299 return false;
1322} 1300}
1323 1301
1324static void surface_send_enter_iterator(struct wlr_surface *surface,
1325 int x, int y, void *data) {
1326 struct wlr_output *wlr_output = data;
1327 wlr_surface_send_enter(surface, wlr_output);
1328}
1329
1330static void surface_send_leave_iterator(struct wlr_surface *surface,
1331 int x, int y, void *data) {
1332 struct wlr_output *wlr_output = data;
1333 wlr_surface_send_leave(surface, wlr_output);
1334}
1335
1336void container_discover_outputs(struct sway_container *con) {
1337 struct wlr_box con_box = {
1338 .x = con->current.x,
1339 .y = con->current.y,
1340 .width = con->current.width,
1341 .height = con->current.height,
1342 };
1343 struct sway_output *old_output = container_get_effective_output(con);
1344
1345 for (int i = 0; i < root->outputs->length; ++i) {
1346 struct sway_output *output = root->outputs->items[i];
1347 struct wlr_box output_box;
1348 output_get_box(output, &output_box);
1349 struct wlr_box intersection;
1350 bool intersects =
1351 wlr_box_intersection(&intersection, &con_box, &output_box);
1352 int index = list_find(con->outputs, output);
1353
1354 if (intersects && index == -1) {
1355 // Send enter
1356 sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output);
1357 if (con->view) {
1358 view_for_each_surface(con->view,
1359 surface_send_enter_iterator, output->wlr_output);
1360 if (con->view->foreign_toplevel) {
1361 wlr_foreign_toplevel_handle_v1_output_enter(
1362 con->view->foreign_toplevel, output->wlr_output);
1363 }
1364 }
1365 list_add(con->outputs, output);
1366 } else if (!intersects && index != -1) {
1367 // Send leave
1368 sway_log(SWAY_DEBUG, "Container %p left output %p", con, output);
1369 if (con->view) {
1370 view_for_each_surface(con->view,
1371 surface_send_leave_iterator, output->wlr_output);
1372 if (con->view->foreign_toplevel) {
1373 wlr_foreign_toplevel_handle_v1_output_leave(
1374 con->view->foreign_toplevel, output->wlr_output);
1375 }
1376 }
1377 list_del(con->outputs, index);
1378 }
1379 }
1380 struct sway_output *new_output = container_get_effective_output(con);
1381 double old_scale = old_output && old_output->enabled ?
1382 old_output->wlr_output->scale : -1;
1383 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1384 if (old_scale != new_scale) {
1385 container_update_title_textures(con);
1386 container_update_marks_textures(con);
1387 }
1388}
1389
1390enum sway_container_layout container_parent_layout(struct sway_container *con) { 1302enum sway_container_layout container_parent_layout(struct sway_container *con) {
1391 if (con->pending.parent) { 1303 if (con->pending.parent) {
1392 return con->pending.parent->pending.layout; 1304 return con->pending.parent->pending.layout;
@@ -1397,19 +1309,11 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) {
1397 return L_NONE; 1309 return L_NONE;
1398} 1310}
1399 1311
1400enum sway_container_layout container_current_parent_layout(
1401 struct sway_container *con) {
1402 if (con->current.parent) {
1403 return con->current.parent->current.layout;
1404 }
1405 return con->current.workspace->current.layout;
1406}
1407
1408list_t *container_get_siblings(struct sway_container *container) { 1312list_t *container_get_siblings(struct sway_container *container) {
1409 if (container->pending.parent) { 1313 if (container->pending.parent) {
1410 return container->pending.parent->pending.children; 1314 return container->pending.parent->pending.children;
1411 } 1315 }
1412 if (container_is_scratchpad_hidden(container)) { 1316 if (!container->pending.workspace) {
1413 return NULL; 1317 return NULL;
1414 } 1318 }
1415 if (list_find(container->pending.workspace->tiling, container) != -1) { 1319 if (list_find(container->pending.workspace->tiling, container) != -1) {
@@ -1422,13 +1326,6 @@ int container_sibling_index(struct sway_container *child) {
1422 return list_find(container_get_siblings(child), child); 1326 return list_find(container_get_siblings(child), child);
1423} 1327}
1424 1328
1425list_t *container_get_current_siblings(struct sway_container *container) {
1426 if (container->current.parent) {
1427 return container->current.parent->current.children;
1428 }
1429 return container->current.workspace->current.tiling;
1430}
1431
1432void container_handle_fullscreen_reparent(struct sway_container *con) { 1329void container_handle_fullscreen_reparent(struct sway_container *con) {
1433 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || 1330 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||
1434 con->pending.workspace->fullscreen == con) { 1331 con->pending.workspace->fullscreen == con) {
@@ -1643,7 +1540,7 @@ bool container_find_and_unmark(char *mark) {
1643 if (strcmp(con_mark, mark) == 0) { 1540 if (strcmp(con_mark, mark) == 0) {
1644 free(con_mark); 1541 free(con_mark);
1645 list_del(con->marks, i); 1542 list_del(con->marks, i);
1646 container_update_marks_textures(con); 1543 container_update_marks(con);
1647 ipc_event_window(con, "mark"); 1544 ipc_event_window(con, "mark");
1648 return true; 1545 return true;
1649 } 1546 }
@@ -1674,70 +1571,15 @@ void container_add_mark(struct sway_container *con, char *mark) {
1674 ipc_event_window(con, "mark"); 1571 ipc_event_window(con, "mark");
1675} 1572}
1676 1573
1677static void update_marks_texture(struct sway_container *con,
1678 struct wlr_texture **texture, struct border_colors *class) {
1679 struct sway_output *output = container_get_effective_output(con);
1680 if (!output) {
1681 return;
1682 }
1683 if (*texture) {
1684 wlr_texture_destroy(*texture);
1685 *texture = NULL;
1686 }
1687 if (!con->marks->length) {
1688 return;
1689 }
1690
1691 size_t len = 0;
1692 for (int i = 0; i < con->marks->length; ++i) {
1693 char *mark = con->marks->items[i];
1694 if (mark[0] != '_') {
1695 len += strlen(mark) + 2;
1696 }
1697 }
1698 char *buffer = calloc(len + 1, 1);
1699 char *part = malloc(len + 1);
1700
1701 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
1702 free(buffer);
1703 return;
1704 }
1705
1706 for (int i = 0; i < con->marks->length; ++i) {
1707 char *mark = con->marks->items[i];
1708 if (mark[0] != '_') {
1709 snprintf(part, len + 1, "[%s]", mark);
1710 strcat(buffer, part);
1711 }
1712 }
1713 free(part);
1714
1715 render_titlebar_text_texture(output, con, texture, class, false, buffer);
1716
1717 free(buffer);
1718}
1719
1720void container_update_marks_textures(struct sway_container *con) {
1721 if (!config->show_marks) {
1722 return;
1723 }
1724 update_marks_texture(con, &con->marks_focused,
1725 &config->border_colors.focused);
1726 update_marks_texture(con, &con->marks_focused_inactive,
1727 &config->border_colors.focused_inactive);
1728 update_marks_texture(con, &con->marks_unfocused,
1729 &config->border_colors.unfocused);
1730 update_marks_texture(con, &con->marks_urgent,
1731 &config->border_colors.urgent);
1732 update_marks_texture(con, &con->marks_focused_tab_title,
1733 &config->border_colors.focused_tab_title);
1734 container_damage_whole(con);
1735}
1736
1737void container_raise_floating(struct sway_container *con) { 1574void container_raise_floating(struct sway_container *con) {
1738 // Bring container to front by putting it at the end of the floating list. 1575 // Bring container to front by putting it at the end of the floating list.
1739 struct sway_container *floater = container_toplevel_ancestor(con); 1576 struct sway_container *floater = container_toplevel_ancestor(con);
1740 if (container_is_floating(floater) && floater->pending.workspace) { 1577 if (container_is_floating(floater) && floater->pending.workspace) {
1578 // it's okay to just raise the scene directly instead of waiting
1579 // for the transaction to go through. We won't be reconfiguring
1580 // surfaces
1581 wlr_scene_node_raise_to_top(&floater->scene_tree->node);
1582
1741 list_move_to_end(floater->pending.workspace->floating, floater); 1583 list_move_to_end(floater->pending.workspace->floating, floater);
1742 node_set_dirty(&floater->pending.workspace->node); 1584 node_set_dirty(&floater->pending.workspace->node);
1743 } 1585 }
@@ -1821,3 +1663,177 @@ int container_squash(struct sway_container *con) {
1821 } 1663 }
1822 return change; 1664 return change;
1823} 1665}
1666
1667static 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
1715static 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
1749void container_swap(struct sway_container *con1, struct sway_container *con2) {
1750 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
1751 return;
1752 }
1753 if (!sway_assert(!container_has_ancestor(con1, con2)
1754 && !container_has_ancestor(con2, con1),
1755 "Cannot swap ancestor and descendant")) {
1756 return;
1757 }
1758
1759 sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu",
1760 con1->node.id, con2->node.id);
1761
1762 bool scratch1 = con1->scratchpad;
1763 bool hidden1 = container_is_scratchpad_hidden(con1);
1764 bool scratch2 = con2->scratchpad;
1765 bool hidden2 = container_is_scratchpad_hidden(con2);
1766 if (scratch1) {
1767 if (hidden1) {
1768 root_scratchpad_show(con1);
1769 }
1770 root_scratchpad_remove_container(con1);
1771 }
1772 if (scratch2) {
1773 if (hidden2) {
1774 root_scratchpad_show(con2);
1775 }
1776 root_scratchpad_remove_container(con2);
1777 }
1778
1779 enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode;
1780 if (fs1) {
1781 container_fullscreen_disable(con1);
1782 }
1783 enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
1784 if (fs2) {
1785 container_fullscreen_disable(con2);
1786 }
1787
1788 struct sway_seat *seat = input_manager_current_seat();
1789 struct sway_container *focus = seat_get_focused_container(seat);
1790 struct sway_workspace *vis1 =
1791 output_get_active_workspace(con1->pending.workspace->output);
1792 struct sway_workspace *vis2 =
1793 output_get_active_workspace(con2->pending.workspace->output);
1794 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a"
1795 "workspace. This should not happen")) {
1796 return;
1797 }
1798
1799 char *stored_prev_name = NULL;
1800 if (seat->prev_workspace_name) {
1801 stored_prev_name = strdup(seat->prev_workspace_name);
1802 }
1803
1804 swap_places(con1, con2);
1805
1806 if (!workspace_is_visible(vis1)) {
1807 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));
1808 }
1809 if (!workspace_is_visible(vis2)) {
1810 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));
1811 }
1812
1813 swap_focus(con1, con2, seat, focus);
1814
1815 if (stored_prev_name) {
1816 free(seat->prev_workspace_name);
1817 seat->prev_workspace_name = stored_prev_name;
1818 }
1819
1820 if (scratch1) {
1821 root_scratchpad_add_container(con2, NULL);
1822 if (!hidden1) {
1823 root_scratchpad_show(con2);
1824 }
1825 }
1826 if (scratch2) {
1827 root_scratchpad_add_container(con1, NULL);
1828 if (!hidden2) {
1829 root_scratchpad_show(con1);
1830 }
1831 }
1832
1833 if (fs1) {
1834 container_set_fullscreen(con2, fs1);
1835 }
1836 if (fs2) {
1837 container_set_fullscreen(con1, fs2);
1838 }
1839}
diff --git a/sway/tree/node.c b/sway/tree/node.c
index bc7e2aa5..7aaf9762 100644
--- a/sway/tree/node.c
+++ b/sway/tree/node.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/output.h" 1#include "sway/output.h"
3#include "sway/server.h" 2#include "sway/server.h"
4#include "sway/tree/container.h" 3#include "sway/tree/container.h"
@@ -18,13 +17,13 @@ void node_init(struct sway_node *node, enum sway_node_type type, void *thing) {
18const char *node_type_to_str(enum sway_node_type type) { 17const char *node_type_to_str(enum sway_node_type type) {
19 switch (type) { 18 switch (type) {
20 case N_ROOT: 19 case N_ROOT:
21 return "N_ROOT"; 20 return "root";
22 case N_OUTPUT: 21 case N_OUTPUT:
23 return "N_OUTPUT"; 22 return "output";
24 case N_WORKSPACE: 23 case N_WORKSPACE:
25 return "N_WORKSPACE"; 24 return "workspace";
26 case N_CONTAINER: 25 case N_CONTAINER:
27 return "N_CONTAINER"; 26 return "container";
28 } 27 }
29 return ""; 28 return "";
30} 29}
@@ -159,3 +158,32 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) {
159 } 158 }
160 return false; 159 return false;
161} 160}
161
162void 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
175struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent,
176 bool *failed) {
177 // fallthrough
178 if (*failed) {
179 return NULL;
180 }
181
182 struct wlr_scene_tree *tree = wlr_scene_tree_create(parent);
183 if (!tree) {
184 sway_log(SWAY_ERROR, "Failed to allocate a scene node");
185 *failed = true;
186 }
187
188 return tree;
189}
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 52826c91..2d11195e 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -1,14 +1,13 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <ctype.h> 2#include <ctype.h>
4#include <string.h> 3#include <string.h>
5#include <strings.h> 4#include <strings.h>
6#include <wlr/types/wlr_output_damage.h>
7#include "sway/ipc-server.h" 5#include "sway/ipc-server.h"
8#include "sway/layers.h" 6#include "sway/layers.h"
9#include "sway/output.h" 7#include "sway/output.h"
10#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
11#include "sway/tree/workspace.h" 9#include "sway/tree/workspace.h"
10#include "sway/server.h"
12#include "log.h" 11#include "log.h"
13#include "util.h" 12#include "util.h"
14 13
@@ -87,9 +86,51 @@ static void restore_workspaces(struct sway_output *output) {
87 output_sort_workspaces(output); 86 output_sort_workspaces(output);
88} 87}
89 88
89static 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
90struct sway_output *output_create(struct wlr_output *wlr_output) { 104struct sway_output *output_create(struct wlr_output *wlr_output) {
91 struct sway_output *output = calloc(1, sizeof(struct sway_output)); 105 struct sway_output *output = calloc(1, sizeof(struct sway_output));
92 node_init(&output->node, N_OUTPUT, output); 106 node_init(&output->node, N_OUTPUT, output);
107
108 bool failed = false;
109 output->layers.shell_background = alloc_scene_tree(root->staging, &failed);
110 output->layers.shell_bottom = alloc_scene_tree(root->staging, &failed);
111 output->layers.tiling = alloc_scene_tree(root->staging, &failed);
112 output->layers.fullscreen = alloc_scene_tree(root->staging, &failed);
113 output->layers.shell_top = alloc_scene_tree(root->staging, &failed);
114 output->layers.shell_overlay = alloc_scene_tree(root->staging, &failed);
115 output->layers.session_lock = alloc_scene_tree(root->staging, &failed);
116
117 if (!failed) {
118 output->fullscreen_background = wlr_scene_rect_create(
119 output->layers.fullscreen, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f});
120
121 if (!output->fullscreen_background) {
122 sway_log(SWAY_ERROR, "Unable to allocate a background rect");
123 failed = true;
124 }
125 }
126
127 if (failed) {
128 destroy_scene_layers(output);
129 wlr_scene_output_destroy(output->scene_output);
130 free(output);
131 return NULL;
132 }
133
93 output->wlr_output = wlr_output; 134 output->wlr_output = wlr_output;
94 wlr_output->data = output; 135 wlr_output->data = output;
95 output->detected_subpixel = wlr_output->subpixel; 136 output->detected_subpixel = wlr_output->subpixel;
@@ -102,11 +143,6 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
102 output->workspaces = create_list(); 143 output->workspaces = create_list();
103 output->current.workspaces = create_list(); 144 output->current.workspaces = create_list();
104 145
105 size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
106 for (size_t i = 0; i < len; ++i) {
107 wl_list_init(&output->layers[i]);
108 }
109
110 return output; 146 return output;
111} 147}
112 148
@@ -146,7 +182,7 @@ void output_enable(struct sway_output *output) {
146 182
147 input_manager_configure_xcursor(); 183 input_manager_configure_xcursor();
148 184
149 wl_signal_emit(&root->events.new_node, &output->node); 185 wl_signal_emit_mutable(&root->events.new_node, &output->node);
150 186
151 arrange_layers(output); 187 arrange_layers(output);
152 arrange_root(); 188 arrange_root();
@@ -238,20 +274,14 @@ void output_destroy(struct sway_output *output) {
238 "which is still referenced by transactions")) { 274 "which is still referenced by transactions")) {
239 return; 275 return;
240 } 276 }
277
278 destroy_scene_layers(output);
241 list_free(output->workspaces); 279 list_free(output->workspaces);
242 list_free(output->current.workspaces); 280 list_free(output->current.workspaces);
243 wl_event_source_remove(output->repaint_timer); 281 wl_event_source_remove(output->repaint_timer);
244 free(output); 282 free(output);
245} 283}
246 284
247static 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
255void output_disable(struct sway_output *output) { 285void output_disable(struct sway_output *output) {
256 if (!sway_assert(output->enabled, "Expected an enabled output")) { 286 if (!sway_assert(output->enabled, "Expected an enabled output")) {
257 return; 287 return;
@@ -262,23 +292,20 @@ void output_disable(struct sway_output *output) {
262 } 292 }
263 293
264 sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); 294 sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name);
265 wl_signal_emit(&output->events.disable, output); 295 wl_signal_emit_mutable(&output->events.disable, output);
266 296
267 output_evacuate(output); 297 output_evacuate(output);
268 298
269 root_for_each_container(untrack_output, output);
270
271 list_del(root->outputs, index); 299 list_del(root->outputs, index);
272 300
273 output->enabled = false; 301 output->enabled = false;
274 output->current_mode = NULL;
275 302
276 arrange_root(); 303 arrange_root();
277 304
278 // Reconfigure all devices, since devices with map_to_output directives for 305 // Reconfigure all devices, since devices with map_to_output directives for
279 // an output that goes offline should stop sending events as long as the 306 // an output that goes offline should stop sending events as long as the
280 // output remains offline. 307 // output remains offline.
281 input_manager_configure_all_inputs(); 308 input_manager_configure_all_input_mappings();
282} 309}
283 310
284void output_begin_destroy(struct sway_output *output) { 311void output_begin_destroy(struct sway_output *output) {
@@ -286,7 +313,7 @@ void output_begin_destroy(struct sway_output *output) {
286 return; 313 return;
287 } 314 }
288 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); 315 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name);
289 wl_signal_emit(&output->node.events.destroy, &output->node); 316 wl_signal_emit_mutable(&output->node.events.destroy, &output->node);
290 317
291 output->node.destroying = true; 318 output->node.destroying = true;
292 node_set_dirty(&output->node); 319 node_set_dirty(&output->node);
@@ -390,6 +417,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) {
390 box->height = output->height; 417 box->height = output->height;
391} 418}
392 419
420static 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
434struct 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
393enum sway_container_layout output_get_default_layout( 447enum sway_container_layout output_get_default_layout(
394 struct sway_output *output) { 448 struct sway_output *output) {
395 if (config->default_orientation != L_NONE) { 449 if (config->default_orientation != L_NONE) {
diff --git a/sway/tree/root.c b/sway/tree/root.c
index 8508e9eb..ae3c3cb2 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -1,12 +1,14 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
5#include <wlr/types/wlr_output_layout.h> 4#include <wlr/types/wlr_output_layout.h>
5#include <wlr/types/wlr_scene.h>
6#include <wlr/util/transform.h>
6#include "sway/desktop/transaction.h" 7#include "sway/desktop/transaction.h"
7#include "sway/input/seat.h" 8#include "sway/input/seat.h"
8#include "sway/ipc-server.h" 9#include "sway/ipc-server.h"
9#include "sway/output.h" 10#include "sway/output.h"
11#include "sway/scene_descriptor.h"
10#include "sway/tree/arrange.h" 12#include "sway/tree/arrange.h"
11#include "sway/tree/container.h" 13#include "sway/tree/container.h"
12#include "sway/tree/root.h" 14#include "sway/tree/root.h"
@@ -23,21 +25,60 @@ static void output_layout_handle_change(struct wl_listener *listener,
23 transaction_commit_dirty(); 25 transaction_commit_dirty();
24} 26}
25 27
26struct sway_root *root_create(void) { 28struct sway_root *root_create(struct wl_display *wl_display) {
27 struct sway_root *root = calloc(1, sizeof(struct sway_root)); 29 struct sway_root *root = calloc(1, sizeof(struct sway_root));
28 if (!root) { 30 if (!root) {
29 sway_log(SWAY_ERROR, "Unable to allocate sway_root"); 31 sway_log(SWAY_ERROR, "Unable to allocate sway_root");
30 return NULL; 32 return NULL;
31 } 33 }
34
35 struct wlr_scene *root_scene = wlr_scene_create();
36 if (!root_scene) {
37 sway_log(SWAY_ERROR, "Unable to allocate root scene node");
38 free(root);
39 return NULL;
40 }
41
32 node_init(&root->node, N_ROOT, root); 42 node_init(&root->node, N_ROOT, root);
33 root->output_layout = wlr_output_layout_create(); 43 root->root_scene = root_scene;
34 wl_list_init(&root->all_outputs); 44
45 bool failed = false;
46 root->staging = alloc_scene_tree(&root_scene->tree, &failed);
47 root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed);
48
49 root->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed);
50 root->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed);
51 root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed);
52 root->layers.floating = alloc_scene_tree(root->layer_tree, &failed);
53 root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed);
54 root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed);
55 root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed);
35#if HAVE_XWAYLAND 56#if HAVE_XWAYLAND
36 wl_list_init(&root->xwayland_unmanaged); 57 root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed);
37#endif 58#endif
38 wl_list_init(&root->drag_icons); 59 root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed);
60 root->layers.popup = alloc_scene_tree(root->layer_tree, &failed);
61 root->layers.seat = alloc_scene_tree(root->layer_tree, &failed);
62 root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed);
63
64 if (!failed && !scene_descriptor_assign(&root->layers.seat->node,
65 SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) {
66 failed = true;
67 }
68
69 if (failed) {
70 wlr_scene_node_destroy(&root_scene->tree.node);
71 free(root);
72 return NULL;
73 }
74
75 wlr_scene_node_set_enabled(&root->staging->node, false);
76
77 root->output_layout = wlr_output_layout_create(wl_display);
78 wl_list_init(&root->all_outputs);
39 wl_signal_init(&root->events.new_node); 79 wl_signal_init(&root->events.new_node);
40 root->outputs = create_list(); 80 root->outputs = create_list();
81 root->non_desktop_outputs = create_list();
41 root->scratchpad = create_list(); 82 root->scratchpad = create_list();
42 83
43 root->output_layout_change.notify = output_layout_handle_change; 84 root->output_layout_change.notify = output_layout_handle_change;
@@ -49,11 +90,22 @@ struct sway_root *root_create(void) {
49void root_destroy(struct sway_root *root) { 90void 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
99static 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
57void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { 109void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) {
58 if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { 110 if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) {
59 return; 111 return;
@@ -62,6 +114,8 @@ void root_scratchpad_add_container(struct sway_container *con, struct sway_works
62 struct sway_container *parent = con->pending.parent; 114 struct sway_container *parent = con->pending.parent;
63 struct sway_workspace *workspace = con->pending.workspace; 115 struct sway_workspace *workspace = con->pending.workspace;
64 116
117 set_container_transform(workspace, con);
118
65 // Clear the fullscreen mode when sending to the scratchpad 119 // Clear the fullscreen mode when sending to the scratchpad
66 if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { 120 if (con->pending.fullscreen_mode != FULLSCREEN_NONE) {
67 container_fullscreen_disable(con); 121 container_fullscreen_disable(con);
@@ -131,7 +185,10 @@ void root_scratchpad_show(struct sway_container *con) {
131 // Show the container 185 // Show the container
132 if (old_ws) { 186 if (old_ws) {
133 container_detach(con); 187 container_detach(con);
134 workspace_consider_destroy(old_ws); 188 // Make sure the last inactive container on the old workspace is above
189 // the workspace itself in the focus stack.
190 struct sway_node *node = seat_get_focus_inactive(seat, &old_ws->node);
191 seat_set_raw_focus(seat, node);
135 } else { 192 } else {
136 // Act on the ancestor of scratchpad hidden split containers 193 // Act on the ancestor of scratchpad hidden split containers
137 while (con->pending.parent) { 194 while (con->pending.parent) {
@@ -140,18 +197,18 @@ void root_scratchpad_show(struct sway_container *con) {
140 } 197 }
141 workspace_add_floating(new_ws, con); 198 workspace_add_floating(new_ws, con);
142 199
143 // Make sure the container's center point overlaps this workspace 200 if (new_ws->output) {
144 double center_lx = con->pending.x + con->pending.width / 2; 201 struct wlr_box output_box;
145 double center_ly = con->pending.y + con->pending.height / 2; 202 output_get_box(new_ws->output, &output_box);
146 203 floating_fix_coordinates(con, &con->transform, &output_box);
147 struct wlr_box workspace_box;
148 workspace_get_box(new_ws, &workspace_box);
149 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
150 container_floating_resize_and_center(con);
151 } 204 }
205 set_container_transform(new_ws, con);
152 206
153 arrange_workspace(new_ws); 207 arrange_workspace(new_ws);
154 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); 208 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));
209 if (old_ws) {
210 workspace_consider_destroy(old_ws);
211 }
155} 212}
156 213
157static void disable_fullscreen(struct sway_container *con, void *data) { 214static void disable_fullscreen(struct sway_container *con, void *data) {
@@ -171,6 +228,8 @@ void root_scratchpad_hide(struct sway_container *con) {
171 return; 228 return;
172 } 229 }
173 230
231 set_container_transform(con->pending.workspace, con);
232
174 disable_fullscreen(con, NULL); 233 disable_fullscreen(con, NULL);
175 container_for_each_child(con, disable_fullscreen, NULL); 234 container_for_each_child(con, disable_fullscreen, NULL);
176 container_detach(con); 235 container_detach(con);
@@ -183,172 +242,6 @@ void root_scratchpad_hide(struct sway_container *con) {
183 ipc_event_window(con, "move"); 242 ipc_event_window(con, "move");
184} 243}
185 244
186struct 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
197static 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 */
204static pid_t get_parent_pid(pid_t child) {
205 pid_t parent = -1;
206 char file_name[100];
207 char *buffer = NULL;
208 const char *sep = " ";
209 FILE *stat = NULL;
210 size_t buf_size = 0;
211
212 snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child);
213
214 if ((stat = fopen(file_name, "r"))) {
215 if (getline(&buffer, &buf_size, stat) != -1) {
216 strtok(buffer, sep); // pid
217 strtok(NULL, sep); // executable name
218 strtok(NULL, sep); // state
219 char *token = strtok(NULL, sep); // parent pid
220 parent = strtol(token, NULL, 10);
221 }
222 free(buffer);
223 fclose(stat);
224 }
225
226 if (parent) {
227 return (parent == child) ? -1 : parent;
228 }
229
230 return -1;
231}
232
233static 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
240struct 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);
264found:
265
266 if (pw && pw->workspace) {
267 ws = workspace_by_name(pw->workspace);
268
269 if (!ws) {
270 sway_log(SWAY_DEBUG,
271 "Creating workspace %s for pid %d because it disappeared",
272 pw->workspace, pid);
273
274 struct sway_output *output = pw->output;
275 if (pw->output && !pw->output->enabled) {
276 sway_log(SWAY_DEBUG,
277 "Workspace output %s is disabled, trying another one",
278 pw->output->wlr_output->name);
279 output = NULL;
280 }
281
282 ws = workspace_create(output, pw->workspace);
283 }
284
285 pid_workspace_destroy(pw);
286 }
287
288 return ws;
289}
290
291static void pw_handle_output_destroy(struct wl_listener *listener, void *data) {
292 struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy);
293 pw->output = NULL;
294 wl_list_remove(&pw->output_destroy.link);
295 wl_list_init(&pw->output_destroy.link);
296}
297
298void root_record_workspace_pid(pid_t pid) {
299 sway_log(SWAY_DEBUG, "Recording workspace for process %d", pid);
300 if (!pid_workspaces.prev && !pid_workspaces.next) {
301 wl_list_init(&pid_workspaces);
302 }
303
304 struct sway_seat *seat = input_manager_current_seat();
305 struct sway_workspace *ws = seat_get_focused_workspace(seat);
306 if (!ws) {
307 sway_log(SWAY_DEBUG, "Bailing out, no workspace");
308 return;
309 }
310 struct sway_output *output = ws->output;
311 if (!output) {
312 sway_log(SWAY_DEBUG, "Bailing out, no output");
313 return;
314 }
315
316 struct timespec now;
317 clock_gettime(CLOCK_MONOTONIC, &now);
318
319 // Remove expired entries
320 static const int timeout = 60;
321 struct pid_workspace *old, *_old;
322 wl_list_for_each_safe(old, _old, &pid_workspaces, link) {
323 if (now.tv_sec - old->time_added.tv_sec >= timeout) {
324 pid_workspace_destroy(old);
325 }
326 }
327
328 struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace));
329 pw->workspace = strdup(ws->name);
330 pw->output = output;
331 pw->pid = pid;
332 memcpy(&pw->time_added, &now, sizeof(struct timespec));
333 pw->output_destroy.notify = pw_handle_output_destroy;
334 wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy);
335 wl_list_insert(&pid_workspaces, &pw->link);
336}
337
338void root_remove_workspace_pid(pid_t pid) {
339 if (!pid_workspaces.prev || !pid_workspaces.next) {
340 return;
341 }
342
343 struct pid_workspace *pw, *tmp;
344 wl_list_for_each_safe(pw, tmp, &pid_workspaces, link) {
345 if (pid == pw->pid) {
346 pid_workspace_destroy(pw);
347 return;
348 }
349 }
350}
351
352void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), 245void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
353 void *data) { 246 void *data) {
354 for (int i = 0; i < root->outputs->length; ++i) { 247 for (int i = 0; i < root->outputs->length; ++i) {
@@ -443,17 +336,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) {
443 box->width = root->width; 336 box->width = root->width;
444 box->height = root->height; 337 box->height = root->height;
445} 338}
446
447void root_rename_pid_workspaces(const char *old_name, const char *new_name) {
448 if (!pid_workspaces.prev && !pid_workspaces.next) {
449 wl_list_init(&pid_workspaces);
450 }
451
452 struct pid_workspace *pw = NULL;
453 wl_list_for_each(pw, &pid_workspaces, link) {
454 if (strcmp(pw->workspace, old_name) == 0) {
455 free(pw->workspace);
456 pw->workspace = strdup(new_name);
457 }
458 }
459}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 7d9e038d..35b4b73f 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -1,9 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <strings.h> 2#include <strings.h>
4#include <wayland-server-core.h> 3#include <wayland-server-core.h>
5#include <wlr/render/wlr_renderer.h> 4#include <wlr/render/wlr_renderer.h>
6#include <wlr/types/wlr_buffer.h> 5#include <wlr/types/wlr_buffer.h>
6#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
7#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
7#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
8#include <wlr/types/wlr_server_decoration.h> 9#include <wlr/types/wlr_server_decoration.h>
9#include <wlr/types/wlr_subcompositor.h> 10#include <wlr/types/wlr_subcompositor.h>
@@ -16,14 +17,16 @@
16#include "log.h" 17#include "log.h"
17#include "sway/criteria.h" 18#include "sway/criteria.h"
18#include "sway/commands.h" 19#include "sway/commands.h"
19#include "sway/desktop.h"
20#include "sway/desktop/transaction.h" 20#include "sway/desktop/transaction.h"
21#include "sway/desktop/idle_inhibit_v1.h" 21#include "sway/desktop/idle_inhibit_v1.h"
22#include "sway/desktop/launcher.h"
22#include "sway/input/cursor.h" 23#include "sway/input/cursor.h"
23#include "sway/ipc-server.h" 24#include "sway/ipc-server.h"
24#include "sway/output.h" 25#include "sway/output.h"
25#include "sway/input/seat.h" 26#include "sway/input/seat.h"
27#include "sway/scene_descriptor.h"
26#include "sway/server.h" 28#include "sway/server.h"
29#include "sway/sway_text_node.h"
27#include "sway/tree/arrange.h" 30#include "sway/tree/arrange.h"
28#include "sway/tree/container.h" 31#include "sway/tree/container.h"
29#include "sway/tree/view.h" 32#include "sway/tree/view.h"
@@ -33,15 +36,29 @@
33#include "pango.h" 36#include "pango.h"
34#include "stringop.h" 37#include "stringop.h"
35 38
36void view_init(struct sway_view *view, enum sway_view_type type, 39bool view_init(struct sway_view *view, enum sway_view_type type,
37 const struct sway_view_impl *impl) { 40 const struct sway_view_impl *impl) {
41 bool failed = false;
42 view->scene_tree = alloc_scene_tree(root->staging, &failed);
43 view->content_tree = alloc_scene_tree(view->scene_tree, &failed);
44
45 if (!failed && !scene_descriptor_assign(&view->scene_tree->node,
46 SWAY_SCENE_DESC_VIEW, view)) {
47 failed = true;
48 }
49
50 if (failed) {
51 wlr_scene_node_destroy(&view->scene_tree->node);
52 return false;
53 }
54
38 view->type = type; 55 view->type = type;
39 view->impl = impl; 56 view->impl = impl;
40 view->executed_criteria = create_list(); 57 view->executed_criteria = create_list();
41 wl_list_init(&view->saved_buffers);
42 view->allow_request_urgent = true; 58 view->allow_request_urgent = true;
43 view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; 59 view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
44 wl_signal_init(&view->events.unmap); 60 wl_signal_init(&view->events.unmap);
61 return true;
45} 62}
46 63
47void view_destroy(struct sway_view *view) { 64void view_destroy(struct sway_view *view) {
@@ -58,11 +75,10 @@ void view_destroy(struct sway_view *view) {
58 return; 75 return;
59 } 76 }
60 wl_list_remove(&view->events.unmap.listener_list); 77 wl_list_remove(&view->events.unmap.listener_list);
61 if (!wl_list_empty(&view->saved_buffers)) {
62 view_remove_saved_buffer(view);
63 }
64 list_free(view->executed_criteria); 78 list_free(view->executed_criteria);
65 79
80 view_assign_ctx(view, NULL);
81 wlr_scene_node_destroy(&view->scene_tree->node);
66 free(view->title_format); 82 free(view->title_format);
67 83
68 if (view->impl->destroy) { 84 if (view->impl->destroy) {
@@ -363,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) {
363 } 379 }
364} 380}
365 381
366void view_request_activate(struct sway_view *view) { 382void view_request_activate(struct sway_view *view, struct sway_seat *seat) {
367 struct sway_workspace *ws = view->container->pending.workspace; 383 struct sway_workspace *ws = view->container->pending.workspace;
368 if (!ws) { // hidden scratchpad container 384 if (!seat) {
369 return; 385 seat = input_manager_current_seat();
370 } 386 }
371 struct sway_seat *seat = input_manager_current_seat();
372 387
373 switch (config->focus_on_window_activation) { 388 switch (config->focus_on_window_activation) {
374 case FOWA_SMART: 389 case FOWA_SMART:
375 if (workspace_is_visible(ws)) { 390 if (ws && workspace_is_visible(ws)) {
376 seat_set_focus_container(seat, view->container); 391 seat_set_focus_container(seat, view->container);
392 container_raise_floating(view->container);
377 } else { 393 } else {
378 view_set_urgent(view, true); 394 view_set_urgent(view, true);
379 } 395 }
@@ -382,11 +398,23 @@ void view_request_activate(struct sway_view *view) {
382 view_set_urgent(view, true); 398 view_set_urgent(view, true);
383 break; 399 break;
384 case FOWA_FOCUS: 400 case FOWA_FOCUS:
385 seat_set_focus_container(seat, view->container); 401 if (container_is_scratchpad_hidden_or_child(view->container)) {
402 root_scratchpad_show(view->container);
403 } else {
404 seat_set_focus_container(seat, view->container);
405 container_raise_floating(view->container);
406 }
386 break; 407 break;
387 case FOWA_NONE: 408 case FOWA_NONE:
388 break; 409 break;
389 } 410 }
411 transaction_commit_dirty();
412}
413
414void view_request_urgent(struct sway_view *view) {
415 if (config->focus_on_window_activation != FOWA_NONE) {
416 view_set_urgent(view, true);
417 }
390} 418}
391 419
392void view_set_csd_from_server(struct sway_view *view, bool enabled) { 420void view_set_csd_from_server(struct sway_view *view, bool enabled) {
@@ -433,52 +461,6 @@ void view_close_popups(struct sway_view *view) {
433 } 461 }
434} 462}
435 463
436void view_damage_from(struct sway_view *view) {
437 for (int i = 0; i < root->outputs->length; ++i) {
438 struct sway_output *output = root->outputs->items[i];
439 output_damage_from_view(output, view);
440 }
441}
442
443void view_for_each_surface(struct sway_view *view,
444 wlr_surface_iterator_func_t iterator, void *user_data) {
445 if (!view->surface) {
446 return;
447 }
448 if (view->impl->for_each_surface) {
449 view->impl->for_each_surface(view, iterator, user_data);
450 } else {
451 wlr_surface_for_each_surface(view->surface, iterator, user_data);
452 }
453}
454
455void view_for_each_popup_surface(struct sway_view *view,
456 wlr_surface_iterator_func_t iterator, void *user_data) {
457 if (!view->surface) {
458 return;
459 }
460 if (view->impl->for_each_popup_surface) {
461 view->impl->for_each_popup_surface(view, iterator, user_data);
462 }
463}
464
465static void view_subsurface_create(struct sway_view *view,
466 struct wlr_subsurface *subsurface);
467
468static void view_init_subsurfaces(struct sway_view *view,
469 struct wlr_surface *surface);
470
471static void view_child_init_subsurfaces(struct sway_view_child *view_child,
472 struct wlr_surface *surface);
473
474static void view_handle_surface_new_subsurface(struct wl_listener *listener,
475 void *data) {
476 struct sway_view *view =
477 wl_container_of(listener, view, surface_new_subsurface);
478 struct wlr_subsurface *subsurface = data;
479 view_subsurface_create(view, subsurface);
480}
481
482static bool view_has_executed_criteria(struct sway_view *view, 464static bool view_has_executed_criteria(struct sway_view *view,
483 struct criteria *criteria) { 465 struct criteria *criteria) {
484 for (int i = 0; i < view->executed_criteria->length; ++i) { 466 for (int i = 0; i < view->executed_criteria->length; ++i) {
@@ -520,7 +502,7 @@ static void view_populate_pid(struct sway_view *view) {
520#if HAVE_XWAYLAND 502#if HAVE_XWAYLAND
521 case SWAY_VIEW_XWAYLAND:; 503 case SWAY_VIEW_XWAYLAND:;
522 struct wlr_xwayland_surface *surf = 504 struct wlr_xwayland_surface *surf =
523 wlr_xwayland_surface_from_wlr_surface(view->surface); 505 wlr_xwayland_surface_try_from_wlr_surface(view->surface);
524 pid = surf->pid; 506 pid = surf->pid;
525 break; 507 break;
526#endif 508#endif
@@ -533,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) {
533 view->pid = pid; 515 view->pid = pid;
534} 516}
535 517
518void 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
536static struct sway_workspace *select_workspace(struct sway_view *view) { 532static struct sway_workspace *select_workspace(struct sway_view *view) {
537 struct sway_seat *seat = input_manager_current_seat(); 533 struct sway_seat *seat = input_manager_current_seat();
538 534
@@ -568,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
568 } 564 }
569 list_free(criterias); 565 list_free(criterias);
570 if (ws) { 566 if (ws) {
571 root_remove_workspace_pid(view->pid); 567 view_assign_ctx(view, NULL);
572 return ws; 568 return ws;
573 } 569 }
574 570
575 // Check if there's a PID mapping 571 // Check if there's a PID mapping
576 ws = root_workspace_for_pid(view->pid); 572 ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL;
577 if (ws) { 573 if (ws) {
574 view_assign_ctx(view, NULL);
578 return ws; 575 return ws;
579 } 576 }
580 577
@@ -592,6 +589,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
592 return NULL; 589 return NULL;
593} 590}
594 591
592static 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
595static bool should_focus(struct sway_view *view) { 600static bool should_focus(struct sway_view *view) {
596 struct sway_seat *seat = input_manager_current_seat(); 601 struct sway_seat *seat = input_manager_current_seat();
597 struct sway_container *prev_con = seat_get_focused_container(seat); 602 struct sway_container *prev_con = seat_get_focused_container(seat);
@@ -717,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
717 view_populate_pid(view); 722 view_populate_pid(view);
718 view->container = container_create(view); 723 view->container = container_create(view);
719 724
725 if (view->ctx == NULL) {
726 struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid);
727 if (ctx != NULL) {
728 view_assign_ctx(view, ctx);
729 }
730 }
731
720 // If there is a request to be opened fullscreen on a specific output, try 732 // If there is a request to be opened fullscreen on a specific output, try
721 // to honor that request. Otherwise, fallback to assigns, pid mappings, 733 // to honor that request. Otherwise, fallback to assigns, pid mappings,
722 // focused workspace, etc 734 // focused workspace, etc
@@ -754,6 +766,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
754 } 766 }
755 } 767 }
756 768
769 struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
770 .app_id = view_get_app_id(view),
771 .title = view_get_title(view),
772 };
773 view->ext_foreign_toplevel =
774 wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state);
775
757 view->foreign_toplevel = 776 view->foreign_toplevel =
758 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); 777 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);
759 view->foreign_activate_request.notify = handle_foreign_activate_request; 778 view->foreign_activate_request.notify = handle_foreign_activate_request;
@@ -777,11 +796,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
777 } 796 }
778 ipc_event_window(view->container, "new"); 797 ipc_event_window(view->container, "new");
779 798
780 view_init_subsurfaces(view, wlr_surface);
781 wl_signal_add(&wlr_surface->events.new_subsurface,
782 &view->surface_new_subsurface);
783 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
784
785 if (decoration) { 799 if (decoration) {
786 view_update_csd_from_client(view, decoration); 800 view_update_csd_from_client(view, decoration);
787 } 801 }
@@ -825,9 +839,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
825 bool set_focus = should_focus(view); 839 bool set_focus = should_focus(view);
826 840
827#if HAVE_XWAYLAND 841#if HAVE_XWAYLAND
828 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 842 struct wlr_xwayland_surface *xsurface;
829 struct wlr_xwayland_surface *xsurface = 843 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
830 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
831 set_focus &= wlr_xwayland_icccm_input_model(xsurface) != 844 set_focus &= wlr_xwayland_icccm_input_model(xsurface) !=
832 WLR_ICCCM_INPUT_MODEL_NONE; 845 WLR_ICCCM_INPUT_MODEL_NONE;
833 } 846 }
@@ -837,6 +850,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
837 input_manager_set_focus(&view->container->node); 850 input_manager_set_focus(&view->container->node);
838 } 851 }
839 852
853 if (view->ext_foreign_toplevel) {
854 update_ext_foreign_toplevel(view);
855 }
856
840 const char *app_id; 857 const char *app_id;
841 const char *class; 858 const char *class;
842 if ((app_id = view_get_app_id(view)) != NULL) { 859 if ((app_id = view_get_app_id(view)) != NULL) {
@@ -847,15 +864,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
847} 864}
848 865
849void view_unmap(struct sway_view *view) { 866void view_unmap(struct sway_view *view) {
850 wl_signal_emit(&view->events.unmap, view); 867 wl_signal_emit_mutable(&view->events.unmap, view);
851 868
852 wl_list_remove(&view->surface_new_subsurface.link); 869 view->executed_criteria->length = 0;
853 870
854 if (view->urgent_timer) { 871 if (view->urgent_timer) {
855 wl_event_source_remove(view->urgent_timer); 872 wl_event_source_remove(view->urgent_timer);
856 view->urgent_timer = NULL; 873 view->urgent_timer = NULL;
857 } 874 }
858 875
876 if (view->ext_foreign_toplevel) {
877 wlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel);
878 view->ext_foreign_toplevel = NULL;
879 }
880
859 if (view->foreign_toplevel) { 881 if (view->foreign_toplevel) {
860 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); 882 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel);
861 view->foreign_toplevel = NULL; 883 view->foreign_toplevel = NULL;
@@ -902,291 +924,47 @@ void view_update_size(struct sway_view *view) {
902 container_set_geometry_from_content(con); 924 container_set_geometry_from_content(con);
903} 925}
904 926
905void view_center_surface(struct sway_view *view) { 927void view_center_and_clip_surface(struct sway_view *view) {
906 struct sway_container *con = view->container; 928 struct sway_container *con = view->container;
907 // We always center the current coordinates rather than the next, as the
908 // geometry immediately affects the currently active rendering.
909 con->surface_x = fmax(con->current.content_x, con->current.content_x +
910 (con->current.content_width - view->geometry.width) / 2);
911 con->surface_y = fmax(con->current.content_y, con->current.content_y +
912 (con->current.content_height - view->geometry.height) / 2);
913}
914 929
915static const struct sway_view_child_impl subsurface_impl; 930 if (container_is_floating(con)) {
931 // We always center the current coordinates rather than the next, as the
932 // geometry immediately affects the currently active rendering.
933 int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2);
934 int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2);
916 935
917static void subsurface_get_view_coords(struct sway_view_child *child, 936 wlr_scene_node_set_position(&view->content_tree->node, x, y);
918 int *sx, int *sy) {
919 struct wlr_surface *surface = child->surface;
920 if (child->parent && child->parent->impl &&
921 child->parent->impl->get_view_coords) {
922 child->parent->impl->get_view_coords(child->parent, sx, sy);
923 } else { 937 } else {
924 *sx = *sy = 0; 938 wlr_scene_node_set_position(&view->content_tree->node, 0, 0);
925 } 939 }
926 struct wlr_subsurface *subsurface =
927 wlr_subsurface_from_wlr_surface(surface);
928 *sx += subsurface->current.x;
929 *sy += subsurface->current.y;
930}
931 940
932static void subsurface_destroy(struct sway_view_child *child) { 941 // only make sure to clip the content if there is content to clip
933 if (!sway_assert(child->impl == &subsurface_impl, 942 if (!wl_list_empty(&con->view->content_tree->children)) {
934 "Expected a subsurface")) { 943 wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){
935 return; 944 .x = con->view->geometry.x,
936 } 945 .y = con->view->geometry.y,
937 struct sway_subsurface *subsurface = (struct sway_subsurface *)child; 946 .width = con->current.content_width,
938 wl_list_remove(&subsurface->destroy.link); 947 .height = con->current.content_height,
939 free(subsurface); 948 });
940}
941
942static const struct sway_view_child_impl subsurface_impl = {
943 .get_view_coords = subsurface_get_view_coords,
944 .destroy = subsurface_destroy,
945};
946
947static void subsurface_handle_destroy(struct wl_listener *listener,
948 void *data) {
949 struct sway_subsurface *subsurface =
950 wl_container_of(listener, subsurface, destroy);
951 struct sway_view_child *child = &subsurface->child;
952 view_child_destroy(child);
953}
954
955static void view_child_damage(struct sway_view_child *child, bool whole);
956
957static void view_subsurface_create(struct sway_view *view,
958 struct wlr_subsurface *wlr_subsurface) {
959 struct sway_subsurface *subsurface =
960 calloc(1, sizeof(struct sway_subsurface));
961 if (subsurface == NULL) {
962 sway_log(SWAY_ERROR, "Allocation failed");
963 return;
964 }
965 view_child_init(&subsurface->child, &subsurface_impl, view,
966 wlr_subsurface->surface);
967
968 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
969 subsurface->destroy.notify = subsurface_handle_destroy;
970
971 subsurface->child.mapped = true;
972
973 view_child_damage(&subsurface->child, true);
974}
975
976static void view_child_subsurface_create(struct sway_view_child *child,
977 struct wlr_subsurface *wlr_subsurface) {
978 struct sway_subsurface *subsurface =
979 calloc(1, sizeof(struct sway_subsurface));
980 if (subsurface == NULL) {
981 sway_log(SWAY_ERROR, "Allocation failed");
982 return;
983 }
984 subsurface->child.parent = child;
985 wl_list_insert(&child->children, &subsurface->child.link);
986 view_child_init(&subsurface->child, &subsurface_impl, child->view,
987 wlr_subsurface->surface);
988
989 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
990 subsurface->destroy.notify = subsurface_handle_destroy;
991
992 subsurface->child.mapped = true;
993
994 view_child_damage(&subsurface->child, true);
995}
996
997static bool view_child_is_mapped(struct sway_view_child *child) {
998 while (child) {
999 if (!child->mapped) {
1000 return false;
1001 }
1002 child = child->parent;
1003 }
1004 return true;
1005}
1006
1007static void view_child_damage(struct sway_view_child *child, bool whole) {
1008 if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) {
1009 return;
1010 }
1011 int sx, sy;
1012 child->impl->get_view_coords(child, &sx, &sy);
1013 desktop_damage_surface(child->surface,
1014 child->view->container->pending.content_x -
1015 child->view->geometry.x + sx,
1016 child->view->container->pending.content_y -
1017 child->view->geometry.y + sy, whole);
1018}
1019
1020static void view_child_handle_surface_commit(struct wl_listener *listener,
1021 void *data) {
1022 struct sway_view_child *child =
1023 wl_container_of(listener, child, surface_commit);
1024 view_child_damage(child, false);
1025}
1026
1027static void view_child_handle_surface_new_subsurface(
1028 struct wl_listener *listener, void *data) {
1029 struct sway_view_child *child =
1030 wl_container_of(listener, child, surface_new_subsurface);
1031 struct wlr_subsurface *subsurface = data;
1032 view_child_subsurface_create(child, subsurface);
1033}
1034
1035static void view_child_handle_surface_destroy(struct wl_listener *listener,
1036 void *data) {
1037 struct sway_view_child *child =
1038 wl_container_of(listener, child, surface_destroy);
1039 view_child_destroy(child);
1040}
1041
1042static void view_init_subsurfaces(struct sway_view *view,
1043 struct wlr_surface *surface) {
1044 struct wlr_subsurface *subsurface;
1045 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1046 current.link) {
1047 view_subsurface_create(view, subsurface);
1048 }
1049 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1050 current.link) {
1051 view_subsurface_create(view, subsurface);
1052 }
1053}
1054
1055static void view_child_init_subsurfaces(struct sway_view_child *view_child,
1056 struct wlr_surface *surface) {
1057 struct wlr_subsurface *subsurface;
1058 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1059 current.link) {
1060 view_child_subsurface_create(view_child, subsurface);
1061 }
1062 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1063 current.link) {
1064 view_child_subsurface_create(view_child, subsurface);
1065 }
1066}
1067
1068static void view_child_handle_surface_map(struct wl_listener *listener,
1069 void *data) {
1070 struct sway_view_child *child =
1071 wl_container_of(listener, child, surface_map);
1072 child->mapped = true;
1073 view_child_damage(child, true);
1074}
1075
1076static void view_child_handle_surface_unmap(struct wl_listener *listener,
1077 void *data) {
1078 struct sway_view_child *child =
1079 wl_container_of(listener, child, surface_unmap);
1080 view_child_damage(child, true);
1081 child->mapped = false;
1082}
1083
1084static void view_child_handle_view_unmap(struct wl_listener *listener,
1085 void *data) {
1086 struct sway_view_child *child =
1087 wl_container_of(listener, child, view_unmap);
1088 view_child_damage(child, true);
1089 child->mapped = false;
1090}
1091
1092void view_child_init(struct sway_view_child *child,
1093 const struct sway_view_child_impl *impl, struct sway_view *view,
1094 struct wlr_surface *surface) {
1095 child->impl = impl;
1096 child->view = view;
1097 child->surface = surface;
1098 wl_list_init(&child->children);
1099
1100 wl_signal_add(&surface->events.commit, &child->surface_commit);
1101 child->surface_commit.notify = view_child_handle_surface_commit;
1102 wl_signal_add(&surface->events.new_subsurface,
1103 &child->surface_new_subsurface);
1104 child->surface_new_subsurface.notify =
1105 view_child_handle_surface_new_subsurface;
1106 wl_signal_add(&surface->events.destroy, &child->surface_destroy);
1107 child->surface_destroy.notify = view_child_handle_surface_destroy;
1108
1109 // Not all child views have a map/unmap event
1110 child->surface_map.notify = view_child_handle_surface_map;
1111 wl_list_init(&child->surface_map.link);
1112 child->surface_unmap.notify = view_child_handle_surface_unmap;
1113 wl_list_init(&child->surface_unmap.link);
1114
1115 wl_signal_add(&view->events.unmap, &child->view_unmap);
1116 child->view_unmap.notify = view_child_handle_view_unmap;
1117
1118 struct sway_container *container = child->view->container;
1119 if (container != NULL) {
1120 struct sway_workspace *workspace = container->pending.workspace;
1121 if (workspace) {
1122 wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
1123 }
1124 }
1125
1126 view_child_init_subsurfaces(child, surface);
1127}
1128
1129void view_child_destroy(struct sway_view_child *child) {
1130 if (view_child_is_mapped(child) && child->view->container != NULL) {
1131 view_child_damage(child, true);
1132 }
1133
1134 if (child->parent != NULL) {
1135 wl_list_remove(&child->link);
1136 child->parent = NULL;
1137 }
1138
1139 struct sway_view_child *subchild, *tmpchild;
1140 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) {
1141 wl_list_remove(&subchild->link);
1142 subchild->parent = NULL;
1143 // The subchild lost its parent link, so it cannot see that the parent
1144 // is unmapped. Unmap it directly.
1145 subchild->mapped = false;
1146 }
1147
1148 wl_list_remove(&child->surface_commit.link);
1149 wl_list_remove(&child->surface_destroy.link);
1150 wl_list_remove(&child->surface_map.link);
1151 wl_list_remove(&child->surface_unmap.link);
1152 wl_list_remove(&child->view_unmap.link);
1153 wl_list_remove(&child->surface_new_subsurface.link);
1154
1155 if (child->impl && child->impl->destroy) {
1156 child->impl->destroy(child);
1157 } else {
1158 free(child);
1159 } 949 }
1160} 950}
1161 951
1162struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { 952struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
1163 if (wlr_surface_is_xdg_surface(wlr_surface)) { 953 struct wlr_xdg_surface *xdg_surface;
1164 struct wlr_xdg_surface *xdg_surface = 954 if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) {
1165 wlr_xdg_surface_from_wlr_surface(wlr_surface);
1166 if (xdg_surface == NULL) {
1167 return NULL;
1168 }
1169 return view_from_wlr_xdg_surface(xdg_surface); 955 return view_from_wlr_xdg_surface(xdg_surface);
1170 } 956 }
1171#if HAVE_XWAYLAND 957#if HAVE_XWAYLAND
1172 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 958 struct wlr_xwayland_surface *xsurface;
1173 struct wlr_xwayland_surface *xsurface = 959 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
1174 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
1175 if (xsurface == NULL) {
1176 return NULL;
1177 }
1178 return view_from_wlr_xwayland_surface(xsurface); 960 return view_from_wlr_xwayland_surface(xsurface);
1179 } 961 }
1180#endif 962#endif
1181 if (wlr_surface_is_subsurface(wlr_surface)) { 963 struct wlr_subsurface *subsurface;
1182 struct wlr_subsurface *subsurface = 964 if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) {
1183 wlr_subsurface_from_wlr_surface(wlr_surface);
1184 if (subsurface == NULL) {
1185 return NULL;
1186 }
1187 return view_from_wlr_surface(subsurface->parent); 965 return view_from_wlr_surface(subsurface->parent);
1188 } 966 }
1189 if (wlr_surface_is_layer_surface(wlr_surface)) { 967 if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) {
1190 return NULL; 968 return NULL;
1191 } 969 }
1192 970
@@ -1267,6 +1045,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
1267 return len; 1045 return len;
1268} 1046}
1269 1047
1048void 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
1270void view_update_title(struct sway_view *view, bool force) { 1060void view_update_title(struct sway_view *view, bool force) {
1271 const char *title = view_get_title(view); 1061 const char *title = view_get_title(view);
1272 1062
@@ -1282,29 +1072,41 @@ void view_update_title(struct sway_view *view, bool force) {
1282 1072
1283 free(view->container->title); 1073 free(view->container->title);
1284 free(view->container->formatted_title); 1074 free(view->container->formatted_title);
1285 if (title) { 1075
1286 size_t len = parse_title_format(view, NULL); 1076 size_t len = parse_title_format(view, NULL);
1077
1078 if (len) {
1287 char *buffer = calloc(len + 1, sizeof(char)); 1079 char *buffer = calloc(len + 1, sizeof(char));
1288 if (!sway_assert(buffer, "Unable to allocate title string")) { 1080 if (!sway_assert(buffer, "Unable to allocate title string")) {
1289 return; 1081 return;
1290 } 1082 }
1291 parse_title_format(view, buffer);
1292 1083
1293 view->container->title = strdup(title); 1084 parse_title_format(view, buffer);
1294 view->container->formatted_title = buffer; 1085 view->container->formatted_title = buffer;
1295 } else { 1086 } else {
1296 view->container->title = NULL;
1297 view->container->formatted_title = NULL; 1087 view->container->formatted_title = NULL;
1298 } 1088 }
1299 1089
1090 view->container->title = title ? strdup(title) : NULL;
1091
1300 // Update title after the global font height is updated 1092 // Update title after the global font height is updated
1301 container_update_title_textures(view->container); 1093 if (view->container->title_bar.title_text && len) {
1094 sway_text_node_set_text(view->container->title_bar.title_text,
1095 view->container->formatted_title);
1096 container_arrange_title_bar(view->container);
1097 } else {
1098 container_update_title_bar(view->container);
1099 }
1302 1100
1303 ipc_event_window(view->container, "title"); 1101 ipc_event_window(view->container, "title");
1304 1102
1305 if (view->foreign_toplevel && title) { 1103 if (view->foreign_toplevel && title) {
1306 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); 1104 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title);
1307 } 1105 }
1106
1107 if (view->ext_foreign_toplevel) {
1108 update_ext_foreign_toplevel(view);
1109 }
1308} 1110}
1309 1111
1310bool view_is_visible(struct sway_view *view) { 1112bool view_is_visible(struct sway_view *view) {
@@ -1365,6 +1167,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1365 return; 1167 return;
1366 } 1168 }
1367 clock_gettime(CLOCK_MONOTONIC, &view->urgent); 1169 clock_gettime(CLOCK_MONOTONIC, &view->urgent);
1170 container_update_itself_and_parents(view->container);
1368 } else { 1171 } else {
1369 view->urgent = (struct timespec){ 0 }; 1172 view->urgent = (struct timespec){ 0 };
1370 if (view->urgent_timer) { 1173 if (view->urgent_timer) {
@@ -1372,7 +1175,6 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1372 view->urgent_timer = NULL; 1175 view->urgent_timer = NULL;
1373 } 1176 }
1374 } 1177 }
1375 container_damage_whole(view->container);
1376 1178
1377 ipc_event_window(view->container, "urgent"); 1179 ipc_event_window(view->container, "urgent");
1378 1180
@@ -1386,40 +1188,54 @@ bool view_is_urgent(struct sway_view *view) {
1386} 1188}
1387 1189
1388void view_remove_saved_buffer(struct sway_view *view) { 1190void view_remove_saved_buffer(struct sway_view *view) {
1389 if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { 1191 if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) {
1390 return; 1192 return;
1391 } 1193 }
1392 struct sway_saved_buffer *saved_buf, *tmp; 1194
1393 wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { 1195 wlr_scene_node_destroy(&view->saved_surface_tree->node);
1394 wlr_buffer_unlock(&saved_buf->buffer->base); 1196 view->saved_surface_tree = NULL;
1395 wl_list_remove(&saved_buf->link); 1197 wlr_scene_node_set_enabled(&view->content_tree->node, true);
1396 free(saved_buf);
1397 }
1398} 1198}
1399 1199
1400static void view_save_buffer_iterator(struct wlr_surface *surface, 1200static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer,
1401 int sx, int sy, void *data) { 1201 int sx, int sy, void *data) {
1402 struct sway_view *view = data; 1202 struct wlr_scene_tree *tree = data;
1403 1203
1404 if (surface && wlr_surface_has_buffer(surface)) { 1204 struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL);
1405 wlr_buffer_lock(&surface->buffer->base); 1205 if (!sbuf) {
1406 struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); 1206 sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface");
1407 saved_buffer->buffer = surface->buffer; 1207 return;
1408 saved_buffer->width = surface->current.width;
1409 saved_buffer->height = surface->current.height;
1410 saved_buffer->x = view->container->surface_x + sx;
1411 saved_buffer->y = view->container->surface_y + sy;
1412 saved_buffer->transform = surface->current.transform;
1413 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box);
1414 wl_list_insert(view->saved_buffers.prev, &saved_buffer->link);
1415 } 1208 }
1209
1210 wlr_scene_buffer_set_dest_size(sbuf,
1211 buffer->dst_width, buffer->dst_height);
1212 wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region);
1213 wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box);
1214 wlr_scene_node_set_position(&sbuf->node, sx, sy);
1215 wlr_scene_buffer_set_transform(sbuf, buffer->transform);
1216 wlr_scene_buffer_set_buffer(sbuf, buffer->buffer);
1416} 1217}
1417 1218
1418void view_save_buffer(struct sway_view *view) { 1219void view_save_buffer(struct sway_view *view) {
1419 if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { 1220 if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) {
1420 view_remove_saved_buffer(view); 1221 view_remove_saved_buffer(view);
1421 } 1222 }
1422 view_for_each_surface(view, view_save_buffer_iterator, view); 1223
1224 view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree);
1225 if (!view->saved_surface_tree) {
1226 sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface");
1227 return;
1228 }
1229
1230 // Enable and disable the saved surface tree like so to atomitaclly update
1231 // the tree. This will prevent over damaging or other weirdness.
1232 wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false);
1233
1234 wlr_scene_node_for_each_buffer(&view->content_tree->node,
1235 view_save_buffer_iterator, view->saved_surface_tree);
1236
1237 wlr_scene_node_set_enabled(&view->content_tree->node, false);
1238 wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true);
1423} 1239}
1424 1240
1425bool view_is_transient_for(struct sway_view *child, 1241bool view_is_transient_for(struct sway_view *child,
@@ -1427,3 +1243,19 @@ bool view_is_transient_for(struct sway_view *child,
1427 return child->impl->is_transient_for && 1243 return child->impl->is_transient_for &&
1428 child->impl->is_transient_for(child, ancestor); 1244 child->impl->is_transient_for(child, ancestor);
1429} 1245}
1246
1247static 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
1253void view_send_frame_done(struct sway_view *view) {
1254 struct timespec when;
1255 clock_gettime(CLOCK_MONOTONIC, &when);
1256
1257 struct wlr_scene_node *node;
1258 wl_list_for_each(node, &view->content_tree->children, link) {
1259 wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when);
1260 }
1261}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index c84320bd..a68dc927 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <ctype.h> 1#include <ctype.h>
3#include <limits.h> 2#include <limits.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -56,6 +55,8 @@ struct sway_output *workspace_get_initial_output(const char *name) {
56 55
57struct sway_workspace *workspace_create(struct sway_output *output, 56struct 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) {
142void workspace_begin_destroy(struct sway_workspace *workspace) { 160void 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) {
174static bool workspace_valid_on_output(const char *output_name, 192static bool workspace_valid_on_output(const char *output_name,
175 const char *ws_name) { 193 const char *ws_name) {
176 struct workspace_config *wsc = workspace_find_config(ws_name); 194 struct workspace_config *wsc = workspace_find_config(ws_name);
177 char identifier[128];
178 struct sway_output *output = output_by_name_or_id(output_name); 195 struct sway_output *output = output_by_name_or_id(output_name);
179 if (!output) { 196 if (!output) {
180 return false; 197 return false;
181 } 198 }
182 output_name = output->wlr_output->name;
183 output_get_identifier(identifier, sizeof(identifier), output);
184
185 if (!wsc) { 199 if (!wsc) {
186 return true; 200 return true;
187 } 201 }
188 202
189 for (int i = 0; i < wsc->outputs->length; i++) { 203 for (int i = 0; i < wsc->outputs->length; i++) {
190 if (strcmp(wsc->outputs->items[i], "*") == 0 || 204 if (output_match_name_or_id(output, wsc->outputs->items[i])) {
191 strcmp(wsc->outputs->items[i], output_name) == 0 ||
192 strcmp(wsc->outputs->items[i], identifier) == 0) {
193 return true; 205 return true;
194 } 206 }
195 } 207 }
@@ -284,13 +296,10 @@ char *workspace_next_name(const char *output_name) {
284 // assignments primarily, falling back to bindings and numbers. 296 // assignments primarily, falling back to bindings and numbers.
285 struct sway_mode *mode = config->current_mode; 297 struct sway_mode *mode = config->current_mode;
286 298
287 char identifier[128];
288 struct sway_output *output = output_by_name_or_id(output_name); 299 struct sway_output *output = output_by_name_or_id(output_name);
289 if (!output) { 300 if (!output) {
290 return NULL; 301 return NULL;
291 } 302 }
292 output_name = output->wlr_output->name;
293 output_get_identifier(identifier, sizeof(identifier), output);
294 303
295 int order = INT_MAX; 304 int order = INT_MAX;
296 char *target = NULL; 305 char *target = NULL;
@@ -310,9 +319,7 @@ char *workspace_next_name(const char *output_name) {
310 } 319 }
311 bool found = false; 320 bool found = false;
312 for (int j = 0; j < wsc->outputs->length; ++j) { 321 for (int j = 0; j < wsc->outputs->length; ++j) {
313 if (strcmp(wsc->outputs->items[j], "*") == 0 || 322 if (output_match_name_or_id(output, wsc->outputs->items[j])) {
314 strcmp(wsc->outputs->items[j], output_name) == 0 ||
315 strcmp(wsc->outputs->items[j], identifier) == 0) {
316 found = true; 323 found = true;
317 free(target); 324 free(target);
318 target = strdup(wsc->workspace); 325 target = strdup(wsc->workspace);
@@ -652,15 +659,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace,
652 659
653struct sway_output *workspace_output_get_highest_available( 660struct sway_output *workspace_output_get_highest_available(
654 struct sway_workspace *ws, struct sway_output *exclude) { 661 struct sway_workspace *ws, struct sway_output *exclude) {
655 char exclude_id[128] = {'\0'};
656 if (exclude) {
657 output_get_identifier(exclude_id, sizeof(exclude_id), exclude);
658 }
659
660 for (int i = 0; i < ws->output_priority->length; i++) { 662 for (int i = 0; i < ws->output_priority->length; i++) {
661 char *name = ws->output_priority->items[i]; 663 const char *name = ws->output_priority->items[i];
662 if (exclude && (strcmp(name, exclude->wlr_output->name) == 0 664 if (exclude && output_match_name_or_id(exclude, name)) {
663 || strcmp(name, exclude_id) == 0)) {
664 continue; 665 continue;
665 } 666 }
666 667
@@ -684,7 +685,6 @@ void workspace_detect_urgent(struct sway_workspace *workspace) {
684 if (workspace->urgent != new_urgent) { 685 if (workspace->urgent != new_urgent) {
685 workspace->urgent = new_urgent; 686 workspace->urgent = new_urgent;
686 ipc_event_workspace(NULL, workspace, "urgent"); 687 ipc_event_workspace(NULL, workspace, "urgent");
687 output_damage_whole(workspace->output);
688 } 688 }
689} 689}
690 690
diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c
index 6c70c785..b7c80dd4 100644
--- a/sway/xdg_activation_v1.c
+++ b/sway/xdg_activation_v1.c
@@ -1,20 +1,65 @@
1#include <wlr/types/wlr_xdg_activation_v1.h> 1#include <wlr/types/wlr_xdg_activation_v1.h>
2#include <wlr/types/wlr_xdg_shell.h>
3#include "sway/desktop/launcher.h"
2#include "sway/tree/view.h" 4#include "sway/tree/view.h"
5#include "sway/tree/workspace.h"
3 6
4void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, 7void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
5 void *data) { 8 void *data) {
6 const struct wlr_xdg_activation_v1_request_activate_event *event = data; 9 const struct wlr_xdg_activation_v1_request_activate_event *event = data;
7 10
8 if (!wlr_surface_is_xdg_surface(event->surface)) { 11 struct wlr_xdg_surface *xdg_surface =
12 wlr_xdg_surface_try_from_wlr_surface(event->surface);
13 if (xdg_surface == NULL) {
9 return; 14 return;
10 } 15 }
11
12 struct wlr_xdg_surface *xdg_surface =
13 wlr_xdg_surface_from_wlr_surface(event->surface);
14 struct sway_view *view = xdg_surface->data; 16 struct sway_view *view = xdg_surface->data;
15 if (!xdg_surface->mapped || view == NULL) { 17 if (view == NULL) {
18 return;
19 }
20
21 struct launcher_ctx *ctx = event->token->data;
22 if (ctx == NULL) {
23 return;
24 }
25
26 if (!xdg_surface->surface->mapped) {
27 // This is a startup notification. If we are tracking it, the data
28 // field is a launcher_ctx.
29 if (ctx->activated) {
30 // This ctx has already been activated and cannot be used again
31 // for a startup notification. It will be destroyed
32 return;
33 } else {
34 ctx->activated = true;
35 view_assign_ctx(view, ctx);
36 }
16 return; 37 return;
17 } 38 }
18 39
19 view_request_activate(view); 40 // This is an activation request. If this context is internal we have ctx->seat.
41 struct sway_seat *seat = ctx->seat;
42 if (!seat) {
43 // Otherwise, use the seat indicated by the launcher client in set_serial
44 seat = ctx->token->seat ? ctx->token->seat->data : NULL;
45 }
46
47 if (seat && ctx->had_focused_surface) {
48 view_request_activate(view, seat);
49 } else {
50 // The token is valid, but cannot be used to activate a window
51 view_request_urgent(view);
52 }
53}
54
55void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void *data) {
56 struct wlr_xdg_activation_token_v1 *token = data;
57 struct sway_seat *seat = token->seat ? token->seat->data :
58 input_manager_current_seat();
59
60 struct sway_workspace *ws = seat_get_focused_workspace(seat);
61 if (ws) {
62 launcher_ctx_create(token, &ws->node);
63 return;
64 }
20} 65}
diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c
index ec9e8d68..fa8c6279 100644
--- a/sway/xdg_decoration.c
+++ b/sway/xdg_decoration.c
@@ -23,37 +23,12 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener,
23 void *data) { 23 void *data) {
24 struct sway_xdg_decoration *deco = 24 struct sway_xdg_decoration *deco =
25 wl_container_of(listener, deco, request_mode); 25 wl_container_of(listener, deco, request_mode);
26 struct sway_view *view = deco->view; 26 set_xdg_decoration_mode(deco);
27 enum wlr_xdg_toplevel_decoration_v1_mode mode =
28 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
29 enum wlr_xdg_toplevel_decoration_v1_mode client_mode =
30 deco->wlr_xdg_decoration->requested_mode;
31
32 bool floating;
33 if (view->container) {
34 floating = container_is_floating(view->container);
35 bool csd = false;
36 csd = client_mode ==
37 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
38 view_update_csd_from_client(view, csd);
39 arrange_container(view->container);
40 transaction_commit_dirty();
41 } else {
42 floating = view->impl->wants_floating &&
43 view->impl->wants_floating(view);
44 }
45
46 if (floating && client_mode) {
47 mode = client_mode;
48 }
49
50 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
51 mode);
52} 27}
53 28
54void handle_xdg_decoration(struct wl_listener *listener, void *data) { 29void handle_xdg_decoration(struct wl_listener *listener, void *data) {
55 struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; 30 struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data;
56 struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->surface->data; 31 struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->toplevel->base->data;
57 32
58 struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); 33 struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco));
59 if (deco == NULL) { 34 if (deco == NULL) {
@@ -72,16 +47,46 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data) {
72 47
73 wl_list_insert(&server.xdg_decorations, &deco->link); 48 wl_list_insert(&server.xdg_decorations, &deco->link);
74 49
75 xdg_decoration_handle_request_mode(&deco->request_mode, wlr_deco); 50 set_xdg_decoration_mode(deco);
76} 51}
77 52
78struct sway_xdg_decoration *xdg_decoration_from_surface( 53struct sway_xdg_decoration *xdg_decoration_from_surface(
79 struct wlr_surface *surface) { 54 struct wlr_surface *surface) {
80 struct sway_xdg_decoration *deco; 55 struct sway_xdg_decoration *deco;
81 wl_list_for_each(deco, &server.xdg_decorations, link) { 56 wl_list_for_each(deco, &server.xdg_decorations, link) {
82 if (deco->wlr_xdg_decoration->surface->surface == surface) { 57 if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) {
83 return deco; 58 return deco;
84 } 59 }
85 } 60 }
86 return NULL; 61 return NULL;
87} 62}
63
64void set_xdg_decoration_mode(struct sway_xdg_decoration *deco) {
65 struct sway_view *view = deco->view;
66 enum wlr_xdg_toplevel_decoration_v1_mode mode =
67 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
68 enum wlr_xdg_toplevel_decoration_v1_mode client_mode =
69 deco->wlr_xdg_decoration->requested_mode;
70
71 bool floating;
72 if (view->container) {
73 floating = container_is_floating(view->container);
74 bool csd = false;
75 csd = client_mode ==
76 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
77 view_update_csd_from_client(view, csd);
78 arrange_container(view->container);
79 transaction_commit_dirty();
80 } else {
81 floating = view->impl->wants_floating &&
82 view->impl->wants_floating(view);
83 }
84
85 if (floating && client_mode) {
86 mode = client_mode;
87 }
88
89 if (view->wlr_xdg_toplevel->base->initialized) {
90 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, mode);
91 }
92}
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 5e4ebd97..5b1213a8 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <errno.h> 2#include <errno.h>
4#include <fcntl.h> 3#include <fcntl.h>
@@ -362,6 +361,9 @@ static void handle_global(void *data, struct wl_registry *registry,
362 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { 361 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
363 bar->xdg_output_manager = wl_registry_bind(registry, name, 362 bar->xdg_output_manager = wl_registry_bind(registry, name,
364 &zxdg_output_manager_v1_interface, 2); 363 &zxdg_output_manager_v1_interface, 2);
364 } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
365 bar->cursor_shape_manager = wl_registry_bind(registry, name,
366 &wp_cursor_shape_manager_v1_interface, 1);
365 } 367 }
366} 368}
367 369
@@ -425,15 +427,17 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) {
425 // Second roundtrip for xdg-output 427 // Second roundtrip for xdg-output
426 wl_display_roundtrip(bar->display); 428 wl_display_roundtrip(bar->display);
427 429
428 struct swaybar_seat *seat; 430 if (!bar->cursor_shape_manager) {
429 wl_list_for_each(seat, &bar->seats, link) { 431 struct swaybar_seat *seat;
430 struct swaybar_pointer *pointer = &seat->pointer; 432 wl_list_for_each(seat, &bar->seats, link) {
431 if (!pointer) { 433 struct swaybar_pointer *pointer = &seat->pointer;
432 continue; 434 if (!pointer) {
435 continue;
436 }
437 pointer->cursor_surface =
438 wl_compositor_create_surface(bar->compositor);
439 assert(pointer->cursor_surface);
433 } 440 }
434 pointer->cursor_surface =
435 wl_compositor_create_surface(bar->compositor);
436 assert(pointer->cursor_surface);
437 } 441 }
438 442
439 if (bar->config->status_command) { 443 if (bar->config->status_command) {
diff --git a/swaybar/config.c b/swaybar/config.c
index 5e828773..55bfcb72 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <string.h> 2#include <string.h>
4#include "swaybar/config.h" 3#include "swaybar/config.h"
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index 6d00befb..62c22d43 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <json.h> 1#include <json.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <ctype.h> 3#include <ctype.h>
@@ -269,11 +268,16 @@ bool i3bar_handle_readable(struct status_line *status) {
269 268
270enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, 269enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
271 struct i3bar_block *block, double x, double y, double rx, double ry, 270 struct i3bar_block *block, double x, double y, double rx, double ry,
272 double w, double h, int scale, uint32_t button) { 271 double w, double h, int scale, uint32_t button, bool released) {
273 sway_log(SWAY_DEBUG, "block %s clicked", block->name); 272 sway_log(SWAY_DEBUG, "block %s clicked", block->name);
274 if (!block->name || !status->click_events) { 273 if (!block->name || !status->click_events) {
275 return HOTSPOT_PROCESS; 274 return HOTSPOT_PROCESS;
276 } 275 }
276 if (released) {
277 // Since we handle the pressed event, also handle the released event
278 // to block it from falling through to a binding in the bar
279 return HOTSPOT_IGNORE;
280 }
277 281
278 struct json_object *event_json = json_object_new_object(); 282 struct json_object *event_json = json_object_new_object();
279 json_object_object_add(event_json, "name", 283 json_object_object_add(event_json, "name",
diff --git a/common/background-image.c b/swaybar/image.c
index 994a0805..ed24b9f9 100644
--- a/common/background-image.c
+++ b/swaybar/image.c
@@ -1,29 +1,12 @@
1#include <assert.h> 1#include <assert.h>
2#include "background-image.h" 2#include "config.h"
3#include "cairo_util.h"
4#include "log.h" 3#include "log.h"
4#include "swaybar/image.h"
5
5#if HAVE_GDK_PIXBUF 6#if HAVE_GDK_PIXBUF
6#include <gdk-pixbuf/gdk-pixbuf.h> 7#include <gdk-pixbuf/gdk-pixbuf.h>
7#endif 8#endif
8 9
9enum 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
28static cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( 11static 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
124cairo_surface_t *load_background_image(const char *path) { 107cairo_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
155void render_background_image(cairo_t *cairo, cairo_surface_t *image,
156 enum background_mode mode, int buffer_width, int buffer_height) {
157 double width = cairo_image_surface_get_width(image);
158 double height = cairo_image_surface_get_height(image);
159
160 cairo_save(cairo);
161 switch (mode) {
162 case BACKGROUND_MODE_STRETCH:
163 cairo_scale(cairo,
164 (double)buffer_width / width,
165 (double)buffer_height / height);
166 cairo_set_source_surface(cairo, image, 0, 0);
167 break;
168 case BACKGROUND_MODE_FILL: {
169 double window_ratio = (double)buffer_width / buffer_height;
170 double bg_ratio = width / height;
171
172 if (window_ratio > bg_ratio) {
173 double scale = (double)buffer_width / width;
174 cairo_scale(cairo, scale, scale);
175 cairo_set_source_surface(cairo, image,
176 0, (double)buffer_height / 2 / scale - height / 2);
177 } else {
178 double scale = (double)buffer_height / height;
179 cairo_scale(cairo, scale, scale);
180 cairo_set_source_surface(cairo, image,
181 (double)buffer_width / 2 / scale - width / 2, 0);
182 }
183 break;
184 }
185 case BACKGROUND_MODE_FIT: {
186 double window_ratio = (double)buffer_width / buffer_height;
187 double bg_ratio = width / height;
188
189 if (window_ratio > bg_ratio) {
190 double scale = (double)buffer_height / height;
191 cairo_scale(cairo, scale, scale);
192 cairo_set_source_surface(cairo, image,
193 (double)buffer_width / 2 / scale - width / 2, 0);
194 } else {
195 double scale = (double)buffer_width / width;
196 cairo_scale(cairo, scale, scale);
197 cairo_set_source_surface(cairo, image,
198 0, (double)buffer_height / 2 / scale - height / 2);
199 }
200 break;
201 }
202 case BACKGROUND_MODE_CENTER:
203 cairo_set_source_surface(cairo, image,
204 (double)buffer_width / 2 - width / 2,
205 (double)buffer_height / 2 - height / 2);
206 break;
207 case BACKGROUND_MODE_TILE: {
208 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
209 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
210 cairo_set_source(cairo, pattern);
211 break;
212 }
213 case BACKGROUND_MODE_SOLID_COLOR:
214 case BACKGROUND_MODE_INVALID:
215 assert(0);
216 break;
217 }
218 cairo_paint(cairo);
219 cairo_restore(cairo);
220}
diff --git a/swaybar/input.c b/swaybar/input.c
index c8c8f0d4..ada4bc86 100644
--- a/swaybar/input.c
+++ b/swaybar/input.c
@@ -81,8 +81,16 @@ void update_cursor(struct swaybar_seat *seat) {
81 int scale = pointer->current ? pointer->current->scale : 1; 81 int scale = pointer->current ? pointer->current->scale : 1;
82 pointer->cursor_theme = wl_cursor_theme_load( 82 pointer->cursor_theme = wl_cursor_theme_load(
83 cursor_theme, cursor_size * scale, seat->bar->shm); 83 cursor_theme, cursor_size * scale, seat->bar->shm);
84 if (!pointer->cursor_theme) {
85 sway_log(SWAY_ERROR, "Failed to load cursor theme");
86 return;
87 }
84 struct wl_cursor *cursor; 88 struct wl_cursor *cursor;
85 cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); 89 cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default");
90 if (!cursor) {
91 sway_log(SWAY_ERROR, "Failed to get default cursor from theme");
92 return;
93 }
86 pointer->cursor_image = cursor->images[0]; 94 pointer->cursor_image = cursor->images[0];
87 wl_surface_set_buffer_scale(pointer->cursor_surface, scale); 95 wl_surface_set_buffer_scale(pointer->cursor_surface, scale);
88 wl_surface_attach(pointer->cursor_surface, 96 wl_surface_attach(pointer->cursor_surface,
@@ -103,7 +111,7 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
103 struct swaybar_pointer *pointer = &seat->pointer; 111 struct swaybar_pointer *pointer = &seat->pointer;
104 seat->pointer.x = wl_fixed_to_double(surface_x); 112 seat->pointer.x = wl_fixed_to_double(surface_x);
105 seat->pointer.y = wl_fixed_to_double(surface_y); 113 seat->pointer.y = wl_fixed_to_double(surface_y);
106 pointer->serial = serial; 114
107 struct swaybar_output *output; 115 struct swaybar_output *output;
108 wl_list_for_each(output, &seat->bar->outputs, link) { 116 wl_list_for_each(output, &seat->bar->outputs, link) {
109 if (output->surface == surface) { 117 if (output->surface == surface) {
@@ -111,7 +119,18 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
111 break; 119 break;
112 } 120 }
113 } 121 }
114 update_cursor(seat); 122
123 if (seat->bar->cursor_shape_manager) {
124 struct wp_cursor_shape_device_v1 *device =
125 wp_cursor_shape_manager_v1_get_pointer(
126 seat->bar->cursor_shape_manager, wl_pointer);
127 wp_cursor_shape_device_v1_set_shape(device, serial,
128 WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
129 wp_cursor_shape_device_v1_destroy(device);
130 } else {
131 pointer->serial = serial;
132 update_cursor(seat);
133 }
115} 134}
116 135
117static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, 136static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
@@ -141,14 +160,15 @@ static bool check_bindings(struct swaybar *bar, uint32_t button,
141} 160}
142 161
143static bool process_hotspots(struct swaybar_output *output, 162static bool process_hotspots(struct swaybar_output *output,
144 double x, double y, uint32_t button) { 163 double x, double y, uint32_t button, uint32_t state) {
164 bool released = state == WL_POINTER_BUTTON_STATE_RELEASED;
145 struct swaybar_hotspot *hotspot; 165 struct swaybar_hotspot *hotspot;
146 wl_list_for_each(hotspot, &output->hotspots, link) { 166 wl_list_for_each(hotspot, &output->hotspots, link) {
147 if (x >= hotspot->x && y >= hotspot->y 167 if (x >= hotspot->x && y >= hotspot->y
148 && x < hotspot->x + hotspot->width 168 && x < hotspot->x + hotspot->width
149 && y < hotspot->y + hotspot->height) { 169 && y < hotspot->y + hotspot->height) {
150 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, 170 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y,
151 button, hotspot->data)) { 171 button, released, hotspot->data)) {
152 return true; 172 return true;
153 } 173 }
154 } 174 }
@@ -166,14 +186,11 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
166 return; 186 return;
167 } 187 }
168 188
169 if (check_bindings(seat->bar, button, state)) { 189 if (process_hotspots(output, pointer->x, pointer->y, button, state)) {
170 return; 190 return;
171 } 191 }
172 192
173 if (state != WL_POINTER_BUTTON_STATE_PRESSED) { 193 check_bindings(seat->bar, button, state);
174 return;
175 }
176 process_hotspots(output, pointer->x, pointer->y, button);
177} 194}
178 195
179static void workspace_next(struct swaybar *bar, struct swaybar_output *output, 196static 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,
222static void process_discrete_scroll(struct swaybar_seat *seat, 239static void process_discrete_scroll(struct swaybar_seat *seat,
223 struct swaybar_output *output, struct swaybar_pointer *pointer, 240 struct swaybar_output *output, struct swaybar_pointer *pointer,
224 uint32_t axis, wl_fixed_t value) { 241 uint32_t axis, wl_fixed_t value) {
225 // If there is a button press binding, execute it, skip default behavior,
226 // and check button release bindings
227 uint32_t button = wl_axis_to_button(axis, value); 242 uint32_t button = wl_axis_to_button(axis, value);
228 if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) { 243 if (process_hotspots(output, pointer->x, pointer->y, button, WL_POINTER_BUTTON_STATE_PRESSED)) {
229 check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED); 244 // (Currently hotspots don't do anything on release events, so no need to emit one)
230 return; 245 return;
231 } 246 }
232 247
233 if (process_hotspots(output, pointer->x, pointer->y, button)) { 248 // If there is a button press binding, execute it, and check button release bindings
249 if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) {
250 check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED);
234 return; 251 return;
235 } 252 }
236 253
@@ -403,7 +420,8 @@ static void wl_touch_up(void *data, struct wl_touch *wl_touch,
403 } 420 }
404 if (time - slot->time < 500) { 421 if (time - slot->time < 500) {
405 // Tap, treat it like a pointer click 422 // Tap, treat it like a pointer click
406 process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT); 423 process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
424 // (Currently hotspots don't do anything on release events, so no need to emit one)
407 } 425 }
408 slot->output = NULL; 426 slot->output = NULL;
409} 427}
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 9d81a9fb..03500bdf 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <limits.h> 1#include <limits.h>
3#include <poll.h> 2#include <poll.h>
4#include <stdio.h> 3#include <stdio.h>
@@ -426,12 +425,9 @@ bool ipc_initialize(struct swaybar *bar) {
426 } 425 }
427 free(res); 426 free(res);
428 427
429 struct swaybar_config *config = bar->config; 428 char *subscribe =
430 char subscribe[128]; // suitably large buffer 429 "[ \"barconfig_update\", \"bar_state_update\", \"mode\", \"workspace\" ]";
431 len = snprintf(subscribe, 128, 430 len = strlen(subscribe);
432 "[ \"barconfig_update\" , \"bar_state_update\" %s %s ]",
433 config->binding_mode_indicator ? ", \"mode\"" : "",
434 config->workspace_buttons ? ", \"workspace\"" : "");
435 free(ipc_single_command(bar->ipc_event_socketfd, 431 free(ipc_single_command(bar->ipc_event_socketfd,
436 IPC_SUBSCRIBE, subscribe, &len)); 432 IPC_SUBSCRIBE, subscribe, &len));
437 return true; 433 return true;
diff --git a/swaybar/main.c b/swaybar/main.c
index a44c1e63..3dc67233 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
diff --git a/swaybar/meson.build b/swaybar/meson.build
index 9feb3cd2..34bbdeea 100644
--- a/swaybar/meson.build
+++ b/swaybar/meson.build
@@ -8,7 +8,6 @@ tray_files = have_tray ? [
8 8
9swaybar_deps = [ 9swaybar_deps = [
10 cairo, 10 cairo,
11 client_protos,
12 gdk_pixbuf, 11 gdk_pixbuf,
13 jsonc, 12 jsonc,
14 math, 13 math,
@@ -27,12 +26,14 @@ executable(
27 'bar.c', 26 'bar.c',
28 'config.c', 27 'config.c',
29 'i3bar.c', 28 'i3bar.c',
29 'image.c',
30 'input.c', 30 'input.c',
31 'ipc.c', 31 'ipc.c',
32 'main.c', 32 'main.c',
33 'render.c', 33 'render.c',
34 'status_line.c', 34 'status_line.c',
35 tray_files 35 tray_files,
36 wl_protos_src,
36 ], 37 ],
37 include_directories: [sway_inc], 38 include_directories: [sway_inc],
38 dependencies: swaybar_deps, 39 dependencies: swaybar_deps,
diff --git a/swaybar/render.c b/swaybar/render.c
index a878805e..879a4e42 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <limits.h> 3#include <limits.h>
@@ -160,7 +159,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
160 159
161static enum hotspot_event_handling block_hotspot_callback( 160static enum hotspot_event_handling block_hotspot_callback(
162 struct swaybar_output *output, struct swaybar_hotspot *hotspot, 161 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
163 double x, double y, uint32_t button, void *data) { 162 double x, double y, uint32_t button, bool released, void *data) {
164 struct i3bar_block *block = data; 163 struct i3bar_block *block = data;
165 struct status_line *status = output->bar->status; 164 struct status_line *status = output->bar->status;
166 return i3bar_block_send_click(status, block, x, y, 165 return i3bar_block_send_click(status, block, x, y,
@@ -168,7 +167,7 @@ static enum hotspot_event_handling block_hotspot_callback(
168 y - (double)hotspot->y, 167 y - (double)hotspot->y,
169 (double)hotspot->width, 168 (double)hotspot->width,
170 (double)hotspot->height, 169 (double)hotspot->height,
171 output->scale, button); 170 output->scale, button, released);
172} 171}
173 172
174static void i3bar_block_unref_callback(void *data) { 173static void i3bar_block_unref_callback(void *data) {
@@ -292,7 +291,7 @@ static uint32_t render_status_block(struct render_context *ctx,
292 } 291 }
293 292
294 double offset = 0; 293 double offset = 0;
295 if (strncmp(block->align, "left", 5) == 0) { 294 if (strncmp(block->align, "left", 4) == 0) {
296 offset = x_pos; 295 offset = x_pos;
297 } else if (strncmp(block->align, "right", 5) == 0) { 296 } else if (strncmp(block->align, "right", 5) == 0) {
298 offset = x_pos + width - text_width; 297 offset = x_pos + width - text_width;
@@ -599,10 +598,15 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx,
599 598
600static enum hotspot_event_handling workspace_hotspot_callback( 599static enum hotspot_event_handling workspace_hotspot_callback(
601 struct swaybar_output *output, struct swaybar_hotspot *hotspot, 600 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
602 double x, double y, uint32_t button, void *data) { 601 double x, double y, uint32_t button, bool released, void *data) {
603 if (button != BTN_LEFT) { 602 if (button != BTN_LEFT) {
604 return HOTSPOT_PROCESS; 603 return HOTSPOT_PROCESS;
605 } 604 }
605 if (released) {
606 // Since we handle the pressed event, also handle the released event
607 // to block it from falling through to a binding in the bar
608 return HOTSPOT_IGNORE;
609 }
606 ipc_send_workspace_command(output->bar, (const char *)data); 610 ipc_send_workspace_command(output->bar, (const char *)data);
607 return HOTSPOT_IGNORE; 611 return HOTSPOT_IGNORE;
608} 612}
@@ -688,15 +692,6 @@ static uint32_t render_to_cairo(struct render_context *ctx) {
688 struct swaybar_output *output = ctx->output; 692 struct swaybar_output *output = ctx->output;
689 struct swaybar *bar = output->bar; 693 struct swaybar *bar = output->bar;
690 struct swaybar_config *config = bar->config; 694 struct swaybar_config *config = bar->config;
691 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
692 if (output->focused) {
693 ctx->background_color = config->colors.focused_background;
694 } else {
695 ctx->background_color = config->colors.background;
696 }
697
698 cairo_set_source_u32(cairo, ctx->background_color);
699 cairo_paint(cairo);
700 695
701 int th; 696 int th;
702 get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, ""); 697 get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, "");
@@ -758,8 +753,17 @@ void render_frame(struct swaybar_output *output) {
758 753
759 free_hotspots(&output->hotspots); 754 free_hotspots(&output->hotspots);
760 755
756 uint32_t background_color;
757 if (output->focused) {
758 background_color = output->bar->config->colors.focused_background;
759 } else {
760 background_color = output->bar->config->colors.background;
761 }
762
761 struct render_context ctx = { 0 }; 763 struct render_context ctx = { 0 };
762 ctx.output = output; 764 ctx.output = output;
765 // initial background color used for deciding the best way to antialias text
766 ctx.background_color = background_color;
763 767
764 cairo_surface_t *recorder = cairo_recording_surface_create( 768 cairo_surface_t *recorder = cairo_recording_surface_create(
765 CAIRO_CONTENT_COLOR_ALPHA, NULL); 769 CAIRO_CONTENT_COLOR_ALPHA, NULL);
@@ -769,24 +773,23 @@ void render_frame(struct swaybar_output *output) {
769 ctx.cairo = cairo; 773 ctx.cairo = cairo;
770 774
771 cairo_font_options_t *fo = cairo_font_options_create(); 775 cairo_font_options_t *fo = cairo_font_options_create();
772 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
773 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); 776 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
774 ctx.textaa_safe = fo; 777 ctx.textaa_safe = fo;
775 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { 778 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
776 ctx.textaa_sharp = ctx.textaa_safe; 779 ctx.textaa_sharp = ctx.textaa_safe;
777 } else { 780 } else {
778 fo = cairo_font_options_create(); 781 fo = cairo_font_options_create();
779 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
780 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); 782 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
781 cairo_font_options_set_subpixel_order(fo, 783 cairo_font_options_set_subpixel_order(fo,
782 to_cairo_subpixel_order(output->subpixel)); 784 to_cairo_subpixel_order(output->subpixel));
783 ctx.textaa_sharp = fo; 785 ctx.textaa_sharp = fo;
784 } 786 }
785 787
786 cairo_save(cairo); 788
787 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 789 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
790 cairo_set_source_u32(cairo, background_color);
788 cairo_paint(cairo); 791 cairo_paint(cairo);
789 cairo_restore(cairo); 792
790 uint32_t height = render_to_cairo(&ctx); 793 uint32_t height = render_to_cairo(&ctx);
791 int config_height = output->bar->config->height; 794 int config_height = output->bar->config->height;
792 if (config_height > 0) { 795 if (config_height > 0) {
@@ -831,13 +834,15 @@ void render_frame(struct swaybar_output *output) {
831 wl_surface_damage(output->surface, 0, 0, 834 wl_surface_damage(output->surface, 0, 0,
832 output->width, output->height); 835 output->width, output->height);
833 836
834 uint32_t bg_alpha = ctx.background_color & 0xFF; 837 uint32_t bg_alpha = background_color & 0xFF;
835 if (bg_alpha == 0xFF) { 838 if (bg_alpha == 0xFF) {
836 struct wl_region *region = 839 struct wl_region *region =
837 wl_compositor_create_region(output->bar->compositor); 840 wl_compositor_create_region(output->bar->compositor);
838 wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); 841 wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
839 wl_surface_set_opaque_region(output->surface, region); 842 wl_surface_set_opaque_region(output->surface, region);
840 wl_region_destroy(region); 843 wl_region_destroy(region);
844 } else {
845 wl_surface_set_opaque_region(output->surface, NULL);
841 } 846 }
842 847
843 struct wl_callback *frame_callback = wl_surface_frame(output->surface); 848 struct wl_callback *frame_callback = wl_surface_frame(output->surface);
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 2e9bb7f1..e542e606 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <fcntl.h> 2#include <fcntl.h>
4#include <sys/ioctl.h> 3#include <sys/ioctl.h>
diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c
index ddf2416d..79b54606 100644
--- a/swaybar/tray/host.c
+++ b/swaybar/tray/host.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -10,6 +9,7 @@
10#include "swaybar/tray/tray.h" 9#include "swaybar/tray/tray.h"
11#include "list.h" 10#include "list.h"
12#include "log.h" 11#include "log.h"
12#include "stringop.h"
13 13
14static const char *watcher_path = "/StatusNotifierWatcher"; 14static const char *watcher_path = "/StatusNotifierWatcher";
15 15
@@ -138,12 +138,10 @@ static int handle_new_watcher(sd_bus_message *msg,
138 138
139bool init_host(struct swaybar_host *host, char *protocol, 139bool 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, &reg_slot, host->watcher_interface, 147 int ret = sd_bus_match_signal(tray->bus, &reg_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 */
207static struct icon_theme *read_theme_file(char *basedir, char *theme_name) { 204static 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
435static bool theme_exists_in_basedir(char *theme, char *basedir) { 422static bool theme_exists_in_basedir(char *theme, char *basedir) {
436 size_t path_len = snprintf(NULL, 0, "%s/%s", basedir, theme) + 1; 423 char *path = format_str("%s/%s", basedir, theme);
437 char *path = malloc(path_len);
438 snprintf(path, path_len, "%s/%s", basedir, theme);
439 bool ret = dir_exists(path); 424 bool ret = dir_exists(path);
440 free(path); 425 free(path);
441 return ret; 426 return ret;
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c
index 0cb5ee9d..ca6c03ad 100644
--- a/swaybar/tray/item.c
+++ b/swaybar/tray/item.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <arpa/inet.h> 1#include <arpa/inet.h>
3#include <cairo.h> 2#include <cairo.h>
4#include <limits.h> 3#include <limits.h>
@@ -7,12 +6,12 @@
7#include <string.h> 6#include <string.h>
8#include "swaybar/bar.h" 7#include "swaybar/bar.h"
9#include "swaybar/config.h" 8#include "swaybar/config.h"
9#include "swaybar/image.h"
10#include "swaybar/input.h" 10#include "swaybar/input.h"
11#include "swaybar/tray/host.h" 11#include "swaybar/tray/host.h"
12#include "swaybar/tray/icon.h" 12#include "swaybar/tray/icon.h"
13#include "swaybar/tray/item.h" 13#include "swaybar/tray/item.h"
14#include "swaybar/tray/tray.h" 14#include "swaybar/tray/tray.h"
15#include "background-image.h"
16#include "cairo_util.h" 15#include "cairo_util.h"
17#include "list.h" 16#include "list.h"
18#include "log.h" 17#include "log.h"
@@ -385,13 +384,18 @@ static int cmp_sni_id(const void *item, const void *cmp_to) {
385 384
386static enum hotspot_event_handling icon_hotspot_callback( 385static enum hotspot_event_handling icon_hotspot_callback(
387 struct swaybar_output *output, struct swaybar_hotspot *hotspot, 386 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
388 double x, double y, uint32_t button, void *data) { 387 double x, double y, uint32_t button, bool released, void *data) {
389 sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data); 388 sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data);
390 389
391 struct swaybar_tray *tray = output->bar->tray; 390 struct swaybar_tray *tray = output->bar->tray;
392 int idx = list_seq_find(tray->items, cmp_sni_id, data); 391 int idx = list_seq_find(tray->items, cmp_sni_id, data);
393 392
394 if (idx != -1) { 393 if (idx != -1) {
394 if (released) {
395 // Since we handle the pressed event, also handle the released event
396 // to block it from falling through to a binding in the bar
397 return HOTSPOT_IGNORE;
398 }
395 struct swaybar_sni *sni = tray->items->items[idx]; 399 struct swaybar_sni *sni = tray->items->items[idx];
396 // guess global position since wayland doesn't expose it 400 // guess global position since wayland doesn't expose it
397 struct swaybar_config *config = tray->bar->config; 401 struct swaybar_config *config = tray->bar->config;
@@ -426,7 +430,7 @@ static void reload_sni(struct swaybar_sni *sni, char *icon_theme,
426 list_free(icon_search_paths); 430 list_free(icon_search_paths);
427 if (icon_path) { 431 if (icon_path) {
428 cairo_surface_destroy(sni->icon); 432 cairo_surface_destroy(sni->icon);
429 sni->icon = load_background_image(icon_path); 433 sni->icon = load_image(icon_path);
430 free(icon_path); 434 free(icon_path);
431 return; 435 return;
432 } 436 }
diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c
index 16afc27c..3cfea8d8 100644
--- a/swaybar/tray/watcher.c
+++ b/swaybar/tray/watcher.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stddef.h> 2#include <stddef.h>
4#include <stdio.h> 3#include <stdio.h>
@@ -6,6 +5,7 @@
6#include <string.h> 5#include <string.h>
7#include "list.h" 6#include "list.h"
8#include "log.h" 7#include "log.h"
8#include "stringop.h"
9#include "swaybar/tray/watcher.h" 9#include "swaybar/tray/watcher.h"
10 10
11static const char *obj_path = "/StatusNotifierWatcher"; 11static const char *obj_path = "/StatusNotifierWatcher";
@@ -76,9 +76,7 @@ static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) {
76 service = service_or_path; 76 service = service_or_path;
77 path = "/StatusNotifierItem"; 77 path = "/StatusNotifierItem";
78 } 78 }
79 size_t id_len = snprintf(NULL, 0, "%s%s", service, path) + 1; 79 id = format_str("%s%s", service, path);
80 id = malloc(id_len);
81 snprintf(id, id_len, "%s%s", service, path);
82 } 80 }
83 81
84 if (list_seq_find(watcher->items, cmp_id, id) == -1) { 82 if (list_seq_find(watcher->items, cmp_id, id) == -1) {
@@ -107,7 +105,7 @@ static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) {
107 sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); 105 sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service);
108 list_add(watcher->hosts, strdup(service)); 106 list_add(watcher->hosts, strdup(service));
109 sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, 107 sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
110 "StatusNotifierHostRegistered", "s", service); 108 "StatusNotifierHostRegistered", "");
111 } else { 109 } else {
112 sway_log(SWAY_DEBUG, "Status Notifier Host '%s' already registered", service); 110 sway_log(SWAY_DEBUG, "Status Notifier Host '%s' already registered", service);
113 } 111 }
@@ -159,9 +157,7 @@ struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) {
159 return NULL; 157 return NULL;
160 } 158 }
161 159
162 size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; 160 watcher->interface = format_str("org.%s.StatusNotifierWatcher", protocol);
163 watcher->interface = malloc(len);
164 snprintf(watcher->interface, len, "org.%s.StatusNotifierWatcher", protocol);
165 161
166 sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL; 162 sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL;
167 int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path, 163 int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path,
diff --git a/swaymsg/main.c b/swaymsg/main.c
index c08406e2..573a7b16 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2 1
3#include <limits.h> 2#include <limits.h>
4#include <stdio.h> 3#include <stdio.h>
@@ -60,7 +59,7 @@ static void pretty_print_cmd(json_object *r) {
60 if (!success_object(r)) { 59 if (!success_object(r)) {
61 json_object *error; 60 json_object *error;
62 if (!json_object_object_get_ex(r, "error", &error)) { 61 if (!json_object_object_get_ex(r, "error", &error)) {
63 printf("An unknkown error occurred"); 62 printf("An unknown error occurred");
64 } else { 63 } else {
65 printf("Error: %s\n", json_object_get_string(error)); 64 printf("Error: %s\n", json_object_get_string(error));
66 } 65 }
@@ -185,12 +184,14 @@ static void pretty_print_seat(json_object *i) {
185} 184}
186 185
187static void pretty_print_output(json_object *o) { 186static void pretty_print_output(json_object *o) {
188 json_object *name, *rect, *focused, *active, *ws, *current_mode; 187 json_object *name, *rect, *focused, *active, *power, *ws, *current_mode, *non_desktop;
189 json_object_object_get_ex(o, "name", &name); 188 json_object_object_get_ex(o, "name", &name);
190 json_object_object_get_ex(o, "rect", &rect); 189 json_object_object_get_ex(o, "rect", &rect);
191 json_object_object_get_ex(o, "focused", &focused); 190 json_object_object_get_ex(o, "focused", &focused);
192 json_object_object_get_ex(o, "active", &active); 191 json_object_object_get_ex(o, "active", &active);
192 json_object_object_get_ex(o, "power", &power);
193 json_object_object_get_ex(o, "current_workspace", &ws); 193 json_object_object_get_ex(o, "current_workspace", &ws);
194 json_object_object_get_ex(o, "non_desktop", &non_desktop);
194 json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, 195 json_object *make, *model, *serial, *scale, *scale_filter, *subpixel,
195 *transform, *max_render_time, *adaptive_sync_status; 196 *transform, *max_render_time, *adaptive_sync_status;
196 json_object_object_get_ex(o, "make", &make); 197 json_object_object_get_ex(o, "make", &make);
@@ -213,10 +214,19 @@ static void pretty_print_output(json_object *o) {
213 json_object_object_get_ex(current_mode, "height", &height); 214 json_object_object_get_ex(current_mode, "height", &height);
214 json_object_object_get_ex(current_mode, "refresh", &refresh); 215 json_object_object_get_ex(current_mode, "refresh", &refresh);
215 216
216 if (json_object_get_boolean(active)) { 217 if (json_object_get_boolean(non_desktop)) {
218 printf(
219 "Output %s '%s %s %s' (non-desktop)\n",
220 json_object_get_string(name),
221 json_object_get_string(make),
222 json_object_get_string(model),
223 json_object_get_string(serial)
224 );
225 } else if (json_object_get_boolean(active)) {
217 printf( 226 printf(
218 "Output %s '%s %s %s'%s\n" 227 "Output %s '%s %s %s'%s\n"
219 " Current mode: %dx%d @ %.3f Hz\n" 228 " Current mode: %dx%d @ %.3f Hz\n"
229 " Power: %s\n"
220 " Position: %d,%d\n" 230 " Position: %d,%d\n"
221 " Scale factor: %f\n" 231 " Scale factor: %f\n"
222 " Scale filter: %s\n" 232 " Scale filter: %s\n"
@@ -231,6 +241,7 @@ static void pretty_print_output(json_object *o) {
231 json_object_get_int(width), 241 json_object_get_int(width),
232 json_object_get_int(height), 242 json_object_get_int(height),
233 (double)json_object_get_int(refresh) / 1000, 243 (double)json_object_get_int(refresh) / 1000,
244 json_object_get_boolean(power) ? "on" : "off",
234 json_object_get_int(x), json_object_get_int(y), 245 json_object_get_int(x), json_object_get_int(y),
235 json_object_get_double(scale), 246 json_object_get_double(scale),
236 json_object_get_string(scale_filter), 247 json_object_get_string(scale_filter),
@@ -247,7 +258,7 @@ static void pretty_print_output(json_object *o) {
247 json_object_get_string(adaptive_sync_status)); 258 json_object_get_string(adaptive_sync_status));
248 } else { 259 } else {
249 printf( 260 printf(
250 "Output %s '%s %s %s' (inactive)\n", 261 "Output %s '%s %s %s' (disabled)\n",
251 json_object_get_string(name), 262 json_object_get_string(name),
252 json_object_get_string(make), 263 json_object_get_string(make),
253 json_object_get_string(model), 264 json_object_get_string(model),
@@ -262,14 +273,22 @@ static void pretty_print_output(json_object *o) {
262 for (size_t i = 0; i < modes_len; ++i) { 273 for (size_t i = 0; i < modes_len; ++i) {
263 json_object *mode = json_object_array_get_idx(modes, i); 274 json_object *mode = json_object_array_get_idx(modes, i);
264 275
265 json_object *mode_width, *mode_height, *mode_refresh; 276 json_object *mode_width, *mode_height, *mode_refresh,
277 *mode_picture_aspect_ratio;
266 json_object_object_get_ex(mode, "width", &mode_width); 278 json_object_object_get_ex(mode, "width", &mode_width);
267 json_object_object_get_ex(mode, "height", &mode_height); 279 json_object_object_get_ex(mode, "height", &mode_height);
268 json_object_object_get_ex(mode, "refresh", &mode_refresh); 280 json_object_object_get_ex(mode, "refresh", &mode_refresh);
281 json_object_object_get_ex(mode, "picture_aspect_ratio",
282 &mode_picture_aspect_ratio);
269 283
270 printf(" %dx%d @ %.3f Hz\n", json_object_get_int(mode_width), 284 printf(" %dx%d @ %.3f Hz", json_object_get_int(mode_width),
271 json_object_get_int(mode_height), 285 json_object_get_int(mode_height),
272 (double)json_object_get_int(mode_refresh) / 1000); 286 (double)json_object_get_int(mode_refresh) / 1000);
287 if (mode_picture_aspect_ratio &&
288 strcmp("none", json_object_get_string(mode_picture_aspect_ratio)) != 0) {
289 printf(" (%s)", json_object_get_string(mode_picture_aspect_ratio));
290 }
291 printf("\n");
273 } 292 }
274 } 293 }
275 294
diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd
index 24a9d6c9..abee1bb9 100644
--- a/swaymsg/swaymsg.1.scd
+++ b/swaymsg/swaymsg.1.scd
@@ -107,6 +107,8 @@ _swaymsg_ [options...] [message]
107 Subscribe to a list of event types. The argument for this type should be 107 Subscribe to a list of event types. The argument for this type should be
108 provided in the form of a valid JSON array. If any of the types are invalid 108 provided in the form of a valid JSON array. If any of the types are invalid
109 or if a valid JSON array is not provided, this will result in a failure. 109 or if a valid JSON array is not provided, this will result in a failure.
110 For a list of valid event types and the data returned with them refer to
111 *sway-ipc*(7).
110 112
111# RETURN CODES 113# RETURN CODES
112 114
diff --git a/swaynag/config.c b/swaynag/config.c
index a0bf3197..efd71ce7 100644
--- a/swaynag/config.c
+++ b/swaynag/config.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <getopt.h> 1#include <getopt.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -166,7 +165,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
166 fprintf(stderr, "Missing action for button %s\n", optarg); 165 fprintf(stderr, "Missing action for button %s\n", optarg);
167 return EXIT_FAILURE; 166 return EXIT_FAILURE;
168 } 167 }
169 struct swaynag_button *button = calloc(sizeof(struct swaynag_button), 1); 168 struct swaynag_button *button = calloc(1, sizeof(struct swaynag_button));
170 if (!button) { 169 if (!button) {
171 perror("calloc"); 170 perror("calloc");
172 return EXIT_FAILURE; 171 return EXIT_FAILURE;
@@ -226,10 +225,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
226 break; 225 break;
227 case 'f': // Font 226 case 'f': // Font
228 if (type) { 227 if (type) {
229 free(type->font);
230 pango_font_description_free(type->font_description); 228 pango_font_description_free(type->font_description);
231 type->font = strdup(optarg); 229 type->font_description = pango_font_description_from_string(optarg);
232 type->font_description = pango_font_description_from_string(type->font);
233 } 230 }
234 break; 231 break;
235 case 'l': // Detailed Message 232 case 'l': // Detailed Message
@@ -245,8 +242,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
245 break; 242 break;
246 case 'L': // Detailed Button Text 243 case 'L': // Detailed Button Text
247 if (swaynag) { 244 if (swaynag) {
248 free(swaynag->details.button_details.text); 245 free(swaynag->details.details_text);
249 swaynag->details.button_details.text = strdup(optarg); 246 swaynag->details.details_text = strdup(optarg);
250 } 247 }
251 break; 248 break;
252 case 'm': // Message 249 case 'm': // Message
diff --git a/swaynag/main.c b/swaynag/main.c
index 2ce37831..634bddbf 100644
--- a/swaynag/main.c
+++ b/swaynag/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <signal.h> 2#include <signal.h>
4#include "log.h" 3#include "log.h"
@@ -29,10 +28,12 @@ int main(int argc, char **argv) {
29 wl_list_init(&swaynag.outputs); 28 wl_list_init(&swaynag.outputs);
30 wl_list_init(&swaynag.seats); 29 wl_list_init(&swaynag.seats);
31 30
32 struct swaynag_button button_close = { 0 }; 31 struct swaynag_button *button_close = calloc(1, sizeof(struct swaynag_button));
33 button_close.text = strdup("X"); 32 button_close->text = strdup("X");
34 button_close.type = SWAYNAG_ACTION_DISMISS; 33 button_close->type = SWAYNAG_ACTION_DISMISS;
35 list_add(swaynag.buttons, &button_close); 34 list_add(swaynag.buttons, button_close);
35
36 swaynag.details.details_text = strdup("Toggle details");
36 37
37 char *config_path = NULL; 38 char *config_path = NULL;
38 bool debug = false; 39 bool debug = false;
@@ -54,8 +55,6 @@ int main(int argc, char **argv) {
54 } 55 }
55 } 56 }
56 57
57 swaynag.details.button_details.text = strdup("Toggle details");
58 swaynag.details.button_details.type = SWAYNAG_ACTION_EXPAND;
59 58
60 if (argc > 1) { 59 if (argc > 1) {
61 struct swaynag_type *type_args = swaynag_type_new("<args>"); 60 struct swaynag_type *type_args = swaynag_type_new("<args>");
@@ -88,17 +87,20 @@ int main(int argc, char **argv) {
88 swaynag_type_merge(type, swaynag_type_get(types, "<args>")); 87 swaynag_type_merge(type, swaynag_type_get(types, "<args>"));
89 swaynag.type = type; 88 swaynag.type = type;
90 89
91 swaynag_types_free(types);
92
93 if (swaynag.details.message) { 90 if (swaynag.details.message) {
94 list_add(swaynag.buttons, &swaynag.details.button_details); 91 swaynag.details.button_details = calloc(1, sizeof(struct swaynag_button));
92 swaynag.details.button_details->text = strdup(swaynag.details.details_text);
93 swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND;
94 list_add(swaynag.buttons, swaynag.details.button_details);
95 } 95 }
96 96
97 sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output); 97 sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output);
98 sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors); 98 sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors);
99 sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name); 99 sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name);
100 sway_log(SWAY_DEBUG, "Message: %s", swaynag.message); 100 sway_log(SWAY_DEBUG, "Message: %s", swaynag.message);
101 sway_log(SWAY_DEBUG, "Font: %s", swaynag.type->font); 101 char *font = pango_font_description_to_string(swaynag.type->font_description);
102 sway_log(SWAY_DEBUG, "Font: %s", font);
103 free(font);
102 sway_log(SWAY_DEBUG, "Buttons"); 104 sway_log(SWAY_DEBUG, "Buttons");
103 for (int i = 0; i < swaynag.buttons->length; i++) { 105 for (int i = 0; i < swaynag.buttons->length; i++) {
104 struct swaynag_button *button = swaynag.buttons->items[i]; 106 struct swaynag_button *button = swaynag.buttons->items[i];
@@ -109,11 +111,9 @@ int main(int argc, char **argv) {
109 111
110 swaynag_setup(&swaynag); 112 swaynag_setup(&swaynag);
111 swaynag_run(&swaynag); 113 swaynag_run(&swaynag);
112 return status;
113 114
114cleanup: 115cleanup:
115 swaynag_types_free(types); 116 swaynag_types_free(types);
116 free(swaynag.details.button_details.text);
117 swaynag_destroy(&swaynag); 117 swaynag_destroy(&swaynag);
118 return status; 118 return status;
119} 119}
diff --git a/swaynag/meson.build b/swaynag/meson.build
index 71f2fc2d..aef21483 100644
--- a/swaynag/meson.build
+++ b/swaynag/meson.build
@@ -5,13 +5,14 @@ executable(
5 'render.c', 5 'render.c',
6 'swaynag.c', 6 'swaynag.c',
7 'types.c', 7 'types.c',
8 wl_protos_src,
8 ], 9 ],
9 include_directories: [sway_inc], 10 include_directories: [sway_inc],
10 dependencies: [ 11 dependencies: [
11 cairo, 12 cairo,
12 client_protos,
13 pango, 13 pango,
14 pangocairo, 14 pangocairo,
15 rt,
15 wayland_client, 16 wayland_client,
16 wayland_cursor, 17 wayland_cursor,
17 ], 18 ],
diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c
index 5620155d..50eea148 100644
--- a/swaynag/swaynag.c
+++ b/swaynag/swaynag.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <assert.h> 2#include <assert.h>
4#include <sys/stat.h> 3#include <sys/stat.h>
@@ -153,8 +152,15 @@ static void update_cursor(struct swaynag_seat *seat) {
153 } 152 }
154 pointer->cursor_theme = wl_cursor_theme_load( 153 pointer->cursor_theme = wl_cursor_theme_load(
155 cursor_theme, cursor_size * swaynag->scale, swaynag->shm); 154 cursor_theme, cursor_size * swaynag->scale, swaynag->shm);
156 struct wl_cursor *cursor = 155 if (!pointer->cursor_theme) {
157 wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); 156 sway_log(SWAY_ERROR, "Failed to load cursor theme");
157 return;
158 }
159 struct wl_cursor *cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default");
160 if (!cursor) {
161 sway_log(SWAY_ERROR, "Failed to get default cursor from theme");
162 return;
163 }
158 pointer->cursor_image = cursor->images[0]; 164 pointer->cursor_image = cursor->images[0];
159 wl_surface_set_buffer_scale(pointer->cursor_surface, 165 wl_surface_set_buffer_scale(pointer->cursor_surface,
160 swaynag->scale); 166 swaynag->scale);
@@ -182,11 +188,22 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
182 uint32_t serial, struct wl_surface *surface, 188 uint32_t serial, struct wl_surface *surface,
183 wl_fixed_t surface_x, wl_fixed_t surface_y) { 189 wl_fixed_t surface_x, wl_fixed_t surface_y) {
184 struct swaynag_seat *seat = data; 190 struct swaynag_seat *seat = data;
191
185 struct swaynag_pointer *pointer = &seat->pointer; 192 struct swaynag_pointer *pointer = &seat->pointer;
186 pointer->x = wl_fixed_to_int(surface_x); 193 pointer->x = wl_fixed_to_int(surface_x);
187 pointer->y = wl_fixed_to_int(surface_y); 194 pointer->y = wl_fixed_to_int(surface_y);
188 pointer->serial = serial; 195
189 update_cursor(seat); 196 if (seat->swaynag->cursor_shape_manager) {
197 struct wp_cursor_shape_device_v1 *device =
198 wp_cursor_shape_manager_v1_get_pointer(
199 seat->swaynag->cursor_shape_manager, wl_pointer);
200 wp_cursor_shape_device_v1_set_shape(device, serial,
201 WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
202 wp_cursor_shape_device_v1_destroy(device);
203 } else {
204 pointer->serial = serial;
205 update_cursor(seat);
206 }
190} 207}
191 208
192static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, 209static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
@@ -378,6 +395,9 @@ static void handle_global(void *data, struct wl_registry *registry,
378 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { 395 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
379 swaynag->layer_shell = wl_registry_bind( 396 swaynag->layer_shell = wl_registry_bind(
380 registry, name, &zwlr_layer_shell_v1_interface, 1); 397 registry, name, &zwlr_layer_shell_v1_interface, 1);
398 } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
399 swaynag->cursor_shape_manager = wl_registry_bind(
400 registry, name, &wp_cursor_shape_manager_v1_interface, 1);
381 } 401 }
382} 402}
383 403
@@ -456,7 +476,9 @@ void swaynag_setup(struct swaynag *swaynag) {
456 exit(EXIT_FAILURE); 476 exit(EXIT_FAILURE);
457 } 477 }
458 478
459 swaynag_setup_cursors(swaynag); 479 if (!swaynag->cursor_shape_manager) {
480 swaynag_setup_cursors(swaynag);
481 }
460 482
461 swaynag->surface = wl_compositor_create_surface(swaynag->compositor); 483 swaynag->surface = wl_compositor_create_surface(swaynag->compositor);
462 assert(swaynag->surface); 484 assert(swaynag->surface);
@@ -483,10 +505,6 @@ void swaynag_run(struct swaynag *swaynag) {
483 && wl_display_dispatch(swaynag->display) != -1) { 505 && wl_display_dispatch(swaynag->display) != -1) {
484 // This is intentionally left blank 506 // This is intentionally left blank
485 } 507 }
486
487 if (swaynag->display) {
488 wl_display_disconnect(swaynag->display);
489 }
490} 508}
491 509
492void swaynag_destroy(struct swaynag *swaynag) { 510void swaynag_destroy(struct swaynag *swaynag) {
@@ -501,6 +519,7 @@ void swaynag_destroy(struct swaynag *swaynag) {
501 } 519 }
502 list_free(swaynag->buttons); 520 list_free(swaynag->buttons);
503 free(swaynag->details.message); 521 free(swaynag->details.message);
522 free(swaynag->details.details_text);
504 free(swaynag->details.button_up.text); 523 free(swaynag->details.button_up.text);
505 free(swaynag->details.button_down.text); 524 free(swaynag->details.button_down.text);
506 525
@@ -541,4 +560,8 @@ void swaynag_destroy(struct swaynag *swaynag) {
541 if (swaynag->shm) { 560 if (swaynag->shm) {
542 wl_shm_destroy(swaynag->shm); 561 wl_shm_destroy(swaynag->shm);
543 } 562 }
563
564 if (swaynag->display) {
565 wl_display_disconnect(swaynag->display);
566 }
544} 567}
diff --git a/swaynag/types.c b/swaynag/types.c
index 7bef0f87..821e5b21 100644
--- a/swaynag/types.c
+++ b/swaynag/types.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <getopt.h> 1#include <getopt.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -32,7 +31,8 @@ struct swaynag_type *swaynag_type_new(const char *name) {
32 31
33void swaynag_types_add_default(list_t *types) { 32void swaynag_types_add_default(list_t *types) {
34 struct swaynag_type *type_defaults = swaynag_type_new("<defaults>"); 33 struct swaynag_type *type_defaults = swaynag_type_new("<defaults>");
35 type_defaults->font = strdup("pango:Monospace 10"); 34 type_defaults->font_description =
35 pango_font_description_from_string("pango:Monospace 10");
36 type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP 36 type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
37 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT 37 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
38 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; 38 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
@@ -90,8 +90,8 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) {
90 return; 90 return;
91 } 91 }
92 92
93 if (src->font) { 93 if (src->font_description) {
94 dest->font = strdup(src->font); 94 dest->font_description = pango_font_description_copy(src->font_description);
95 } 95 }
96 96
97 if (src->output) { 97 if (src->output) {
@@ -172,7 +172,7 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) {
172 172
173void swaynag_type_free(struct swaynag_type *type) { 173void swaynag_type_free(struct swaynag_type *type) {
174 free(type->name); 174 free(type->name);
175 free(type->font); 175 pango_font_description_free(type->font_description);
176 free(type->output); 176 free(type->output);
177 free(type); 177 free(type);
178} 178}