aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.builds/alpine.yml7
-rw-r--r--.builds/archlinux.yml5
-rw-r--r--.builds/freebsd.yml6
-rw-r--r--.clang-format18
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md1
-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.md24
-rw-r--r--README.dk.md69
-rw-r--r--README.es.md12
-rw-r--r--README.fr.md12
-rw-r--r--README.ge.md61
-rw-r--r--README.gr.md67
-rw-r--r--README.hi.md63
-rw-r--r--README.hu.md13
-rw-r--r--README.ir.md54
-rw-r--r--README.it.md66
-rw-r--r--README.ja.md20
-rw-r--r--README.ko.md12
-rw-r--r--README.md64
-rw-r--r--README.nl.md12
-rw-r--r--README.no.md68
-rw-r--r--README.pl.md12
-rw-r--r--README.pt.md12
-rw-r--r--README.ro.md14
-rw-r--r--README.ru.md12
-rw-r--r--README.sv.md83
-rw-r--r--README.tr.md12
-rw-r--r--README.uk.md12
-rw-r--r--README.zh-CN.md53
-rw-r--r--README.zh-TW.md12
-rw-r--r--client/pool-buffer.c67
-rw-r--r--common/gesture.c332
-rw-r--r--common/ipc-client.c1
-rw-r--r--common/log.c1
-rw-r--r--common/loop.c1
-rw-r--r--common/meson.build3
-rw-r--r--common/pango.c46
-rw-r--r--common/stringop.c34
-rw-r--r--common/util.c7
-rw-r--r--completions/bash/sway2
-rw-r--r--completions/bash/swaybar2
-rw-r--r--completions/bash/swaymsg2
-rw-r--r--completions/meson.build57
-rw-r--r--config.in8
-rwxr-xr-xcontrib/autoname-workspaces.py124
-rwxr-xr-xcontrib/grimshot154
-rw-r--r--contrib/grimshot.1104
-rw-r--r--contrib/grimshot.1.scd77
-rwxr-xr-xcontrib/inactive-windows-transparency.py69
-rw-r--r--include/background-image.h20
-rw-r--r--include/gesture.h104
-rw-r--r--include/ipc-client.h3
-rw-r--r--include/pango.h12
-rw-r--r--include/stringop.h10
-rw-r--r--include/sway/commands.h20
-rw-r--r--include/sway/config.h118
-rw-r--r--include/sway/criteria.h8
-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.h17
-rw-r--r--include/sway/input/input-manager.h8
-rw-r--r--include/sway/input/keyboard.h1
-rw-r--r--include/sway/input/libinput.h5
-rw-r--r--include/sway/input/seat.h121
-rw-r--r--include/sway/input/switch.h1
-rw-r--r--include/sway/input/tablet.h3
-rw-r--r--include/sway/input/text_input.h8
-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.h52
-rw-r--r--include/sway/output.h109
-rw-r--r--include/sway/scene_descriptor.h33
-rw-r--r--include/sway/server.h93
-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.h109
-rw-r--r--include/sway/tree/node.h13
-rw-r--r--include/sway/tree/root.h52
-rw-r--r--include/sway/tree/view.h136
-rw-r--r--include/sway/tree/workspace.h8
-rw-r--r--include/sway/xdg_decoration.h2
-rw-r--r--include/swaybar/bar.h3
-rw-r--r--include/swaybar/config.h3
-rw-r--r--include/swaybar/i3bar.h3
-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.h7
-rw-r--r--include/swaynag/types.h6
-rw-r--r--meson.build154
-rw-r--r--meson_options.txt2
-rw-r--r--protocols/meson.build77
-rw-r--r--protocols/wlr-input-inhibitor-unstable-v1.xml67
-rwxr-xr-xrelease.sh31
-rw-r--r--sway/commands.c53
-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.c3
-rw-r--r--sway/commands/bar/icon_theme.c1
-rw-r--r--sway/commands/bar/id.c1
-rw-r--r--sway/commands/bar/mode.c3
-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.c25
-rw-r--r--sway/commands/client.c28
-rw-r--r--sway/commands/exec_always.c30
-rw-r--r--sway/commands/floating_minmax_size.c6
-rw-r--r--sway/commands/focus.c26
-rw-r--r--sway/commands/font.c30
-rw-r--r--sway/commands/for_window.c2
-rw-r--r--sway/commands/gesture.c165
-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.c37
-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.c4
-rw-r--r--sway/commands/move.c67
-rw-r--r--sway/commands/no_focus.c2
-rw-r--r--sway/commands/opacity.c3
-rw-r--r--sway/commands/output.c19
-rw-r--r--sway/commands/output/background.c16
-rw-r--r--sway/commands/output/dpms.c45
-rw-r--r--sway/commands/output/mode.c58
-rw-r--r--sway/commands/output/power.c43
-rw-r--r--sway/commands/output/render_bit_depth.c29
-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.c9
-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.c29
-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/smart_gaps.c7
-rw-r--r--sway/commands/split.c21
-rw-r--r--sway/commands/swap.c183
-rw-r--r--sway/commands/title_align.c9
-rw-r--r--sway/commands/title_format.c2
-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.c163
-rw-r--r--sway/config/bar.c4
-rw-r--r--sway/config/input.c13
-rw-r--r--sway/config/output.c721
-rw-r--r--sway/config/seat.c2
-rw-r--r--sway/criteria.c99
-rw-r--r--sway/desktop/desktop.c39
-rw-r--r--sway/desktop/idle_inhibit_v1.c54
-rw-r--r--sway/desktop/launcher.c267
-rw-r--r--sway/desktop/layer_shell.c778
-rw-r--r--sway/desktop/output.c1011
-rw-r--r--sway/desktop/render.c1160
-rw-r--r--sway/desktop/surface.c46
-rw-r--r--sway/desktop/transaction.c502
-rw-r--r--sway/desktop/xdg_shell.c388
-rw-r--r--sway/desktop/xwayland.c297
-rw-r--r--sway/input/cursor.c587
-rw-r--r--sway/input/input-manager.c149
-rw-r--r--sway/input/keyboard.c190
-rw-r--r--sway/input/libinput.c105
-rw-r--r--sway/input/seat.c445
-rw-r--r--sway/input/seatop_default.c537
-rw-r--r--sway/input/seatop_down.c181
-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.c21
-rw-r--r--sway/input/seatop_resize_tiling.c3
-rw-r--r--sway/input/switch.c34
-rw-r--r--sway/input/tablet.c50
-rw-r--r--sway/input/text_input.c229
-rw-r--r--sway/ipc-json.c251
-rw-r--r--sway/ipc-server.c36
-rw-r--r--sway/lock.c354
-rw-r--r--sway/main.c186
-rw-r--r--sway/meson.build31
-rw-r--r--sway/realtime.c40
-rw-r--r--sway/scene_descriptor.c66
-rw-r--r--sway/server.c320
-rw-r--r--sway/sway-bar.5.scd2
-rw-r--r--sway/sway-input.5.scd44
-rw-r--r--sway/sway-ipc.7.scd52
-rw-r--r--sway/sway-output.5.scd51
-rw-r--r--sway/sway.5.scd157
-rw-r--r--sway/sway_text_node.c302
-rw-r--r--sway/swaynag.c17
-rw-r--r--sway/tree/arrange.c40
-rw-r--r--sway/tree/container.c1297
-rw-r--r--sway/tree/node.c38
-rw-r--r--sway/tree/output.c117
-rw-r--r--sway/tree/root.c279
-rw-r--r--sway/tree/view.c609
-rw-r--r--sway/tree/workspace.c98
-rw-r--r--sway/xdg_activation_v1.c57
-rw-r--r--sway/xdg_decoration.c41
-rw-r--r--swaybar/bar.c35
-rw-r--r--swaybar/config.c5
-rw-r--r--swaybar/i3bar.c39
-rw-r--r--swaybar/image.c (renamed from common/background-image.c)92
-rw-r--r--swaybar/input.c60
-rw-r--r--swaybar/ipc.c21
-rw-r--r--swaybar/main.c1
-rw-r--r--swaybar/meson.build5
-rw-r--r--swaybar/render.c292
-rw-r--r--swaybar/status_line.c14
-rw-r--r--swaybar/tray/host.c11
-rw-r--r--swaybar/tray/icon.c27
-rw-r--r--swaybar/tray/item.c43
-rw-r--r--swaybar/tray/tray.c4
-rw-r--r--swaybar/tray/watcher.c12
-rw-r--r--swaymsg/main.c155
-rw-r--r--swaymsg/swaymsg.1.scd22
-rw-r--r--swaynag/config.c79
-rw-r--r--swaynag/main.c48
-rw-r--r--swaynag/meson.build3
-rw-r--r--swaynag/render.c70
-rw-r--r--swaynag/swaynag.c133
-rw-r--r--swaynag/types.c10
256 files changed, 10635 insertions, 8217 deletions
diff --git a/.builds/alpine.yml b/.builds/alpine.yml
index 7f0bef02..59df7737 100644
--- a/.builds/alpine.yml
+++ b/.builds/alpine.yml
@@ -4,6 +4,7 @@ packages:
4 - eudev-dev 4 - eudev-dev
5 - gdk-pixbuf-dev 5 - gdk-pixbuf-dev
6 - json-c-dev 6 - json-c-dev
7 - libdisplay-info-dev
7 - libevdev-dev 8 - libevdev-dev
8 - libinput-dev 9 - libinput-dev
9 - libseat-dev 10 - libseat-dev
@@ -12,16 +13,18 @@ packages:
12 - mesa-dev 13 - mesa-dev
13 - meson 14 - meson
14 - pango-dev 15 - pango-dev
16 - pcre2-dev
15 - pixman-dev 17 - pixman-dev
16 - scdoc 18 - scdoc
17 - wayland-dev 19 - wayland-dev
18 - wayland-protocols 20 - wayland-protocols
19 - xcb-util-image-dev 21 - xcb-util-image-dev
20 - xcb-util-wm-dev 22 - xcb-util-wm-dev
21 - xwayland 23 - xwayland-dev
24 - hwdata-dev
22sources: 25sources:
23 - https://github.com/swaywm/sway 26 - https://github.com/swaywm/sway
24 - https://github.com/swaywm/wlroots 27 - https://gitlab.freedesktop.org/wlroots/wlroots.git
25tasks: 28tasks:
26 - wlroots: | 29 - wlroots: |
27 cd wlroots 30 cd wlroots
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml
index a8f1dfed..9972c01a 100644
--- a/.builds/archlinux.yml
+++ b/.builds/archlinux.yml
@@ -3,12 +3,14 @@ packages:
3 - cairo 3 - cairo
4 - gdk-pixbuf2 4 - gdk-pixbuf2
5 - json-c 5 - json-c
6 - libdisplay-info
6 - libegl 7 - libegl
7 - libinput 8 - libinput
8 - libxcb 9 - libxcb
9 - libxkbcommon 10 - libxkbcommon
10 - meson 11 - meson
11 - pango 12 - pango
13 - pcre2
12 - scdoc 14 - scdoc
13 - wayland 15 - wayland
14 - wayland-protocols 16 - wayland-protocols
@@ -16,9 +18,10 @@ packages:
16 - xcb-util-wm 18 - xcb-util-wm
17 - xorg-xwayland 19 - xorg-xwayland
18 - seatd 20 - seatd
21 - hwdata
19sources: 22sources:
20 - https://github.com/swaywm/sway 23 - https://github.com/swaywm/sway
21 - https://github.com/swaywm/wlroots 24 - https://gitlab.freedesktop.org/wlroots/wlroots.git
22tasks: 25tasks:
23 - wlroots: | 26 - wlroots: |
24 cd wlroots 27 cd wlroots
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
index 1a3c8512..29c6312a 100644
--- a/.builds/freebsd.yml
+++ b/.builds/freebsd.yml
@@ -4,6 +4,7 @@ packages:
4- devel/json-c 4- devel/json-c
5- devel/libevdev 5- devel/libevdev
6- devel/meson 6- devel/meson
7- devel/pcre2
7- devel/pkgconf 8- devel/pkgconf
8- graphics/cairo 9- graphics/cairo
9- graphics/gdk-pixbuf2 10- graphics/gdk-pixbuf2
@@ -19,14 +20,17 @@ packages:
19- devel/libudev-devd 20- devel/libudev-devd
20- graphics/libdrm 21- graphics/libdrm
21- graphics/mesa-libs 22- graphics/mesa-libs
23- sysutils/libdisplay-info
22- sysutils/seatd 24- sysutils/seatd
23- x11/libinput 25- x11/libinput
24- x11/libX11 26- x11/libX11
25- x11/pixman 27- x11/pixman
26- x11/xcb-util-wm 28- x11/xcb-util-wm
29- x11-servers/xwayland-devel
30- misc/hwdata
27sources: 31sources:
28- https://github.com/swaywm/sway 32- https://github.com/swaywm/sway
29- https://github.com/swaywm/wlroots 33- https://gitlab.freedesktop.org/wlroots/wlroots.git
30tasks: 34tasks:
31- setup: | 35- setup: |
32 cd sway 36 cd sway
diff --git a/.clang-format b/.clang-format
deleted file mode 100644
index 24ea869d..00000000
--- a/.clang-format
+++ /dev/null
@@ -1,18 +0,0 @@
1BasedOnStyle: LLVM
2IndentWidth: 4
3TabWidth: 4
4UseTab: Always
5BreakBeforeBraces: Attach
6AllowShortIfStatementsOnASingleLine: false
7IndentCaseLabels: false
8SortIncludes: false
9ColumnLimit: 80
10AlignAfterOpenBracket: DontAlign
11BinPackParameters: true
12BinPackArguments: true
13ContinuationIndentWidth: 8
14AllowAllParametersOfDeclarationOnNextLine: false
15AllowShortLoopsOnASingleLine: true
16ReflowComments: false
17AllowAllArgumentsOnNextLine: false
18AlignOperands: DontAlign
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 8542b7b9..eba606e6 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -18,6 +18,7 @@ labels: 'bug'
18- **Debug Log:** 18- **Debug Log:**
19 - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com. 19 - Run `sway -d 2> ~/sway.log` from a TTY and upload it to a pastebin, such as gist.github.com.
20 - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway. 20 - This will record information about sway's activity. Please try to keep the reproduction as brief as possible and exit sway.
21 - Attach the **full** file, do not truncate it.
21 22
22- **Configuration File:** 23- **Configuration File:**
23 - Please try to produce with the default configuration. 24 - Please try to produce with the default configuration.
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 142c738c..68b411d9 100644
--- a/README.de.md
+++ b/README.de.md
@@ -1,30 +1,30 @@
1# Sway 1# Sway
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/?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
15sway benötigt die folgenden Pakete: 15sway benötigt die folgenden Pakete:
16 16
17* meson\* 17* meson\*
18* [wlroots](https://github.com/swaywm/wlroots) 18* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
19* wayland 19* wayland
20* wayland-protocols\* 20* wayland-protocols\*
21* pcre 21* pcre2
22* json-c 22* json-c
23* pango 23* pango
24* cairo 24* cairo
25* gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt) 25* gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt)
26* [scdoc](https://git.sr.ht/~sircmpwn/scdoc)\* (Optional, wird für die Dokumentation (Man Pages) benötigt) 26* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (Optional, wird für die Dokumentation (Man Pages) benötigt)\*
27* git\* 27* git (Optional: Versionsinfo)\*
28 28
29_\*Werden nur während des Kompilierens benötigt_ 29_\*Werden nur während des Kompilierens benötigt_
30 30
@@ -34,12 +34,6 @@ Führe die folgenden Befehle aus:
34 ninja -C build 34 ninja -C build
35 sudo ninja -C build install 35 sudo ninja -C build install
36 36
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 79a0df93..5ce94cde 100644
--- a/README.dk.md
+++ b/README.dk.md
@@ -1,41 +1,43 @@
1# Sway 1# Sway
2 2
3Sway er en [i3](https://i3wm.org/)-kompatibel [Wayland](http://wayland.freedesktop.org/) compositor. 3Sway er en [i3]-kompatibel [Wayland] compositor. Læs [Ofte stillede spørgsmål].
4Læs [Ofte stillede spørgsmål](https://github.com/swaywm/sway/wiki). 4Deltag på [IRC kanalen][IRC kanal] \(#sway på irc.libera.chat).
5Deltag på [IRC kanalen](https://web.libera.chat/?channels=#sway) (#sway på irc.libera.chat).
6 5
7## Udgivelses Signaturer 6## Udgivelses Signaturer
8 7
9Udgivelser er signeret med [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 8Udgivelser er signeret med [E88F5E48] og publiceret [på GitHub][GitHub
10og publiseret på [GitHub](https://github.com/swaywm/sway/releases). 9releases].
11 10
12## Installation 11## Installation
13 12
14### Fra Pakker 13### Fra pakker
15 14
16Sway er tilgængelig i mange distributioner. Prøv at installere pakken "sway". Hvis den ikke er tilgængelig, så tjek [denne wiki-side](https://github.com/swaywm/sway/wiki/Unsupported-packages) 15Sway er tilgængelig i mange distributioner. Prøv at installere "sway" pakken
17for information om installation til din(e) distribution(er). 16fra din.
18 17
19Hvis du er interesseret i at lave en Sway pakke til din distribution, burde du besøge IRC 18Hvis du er interesseret i at pakke Sway til din distribution, kan du tage forbi
20kanalen eller sende en e-mail til sir@cmpwn.com for rådgivning. 19IRC kanalen eller sende en email til sir@cmpwn.com for rådgivning.
21 20
22### Kompilering fra kildekode 21### Kompilering fra kildekode
23 22
24Installation afhænger af følgende programmer: 23Se [denne wiki-side][Opsætning til udvikling] hvis du vil bygge HEAD af sway og
24wlroots til test eller udvikling.
25
26Installationsafhængigheder:
25 27
26* meson \* 28* meson \*
27* [wlroots](https://github.com/swaywm/wlroots) 29* [wlroots]
28* wayland 30* wayland
29* wayland-protocols \* 31* wayland-protocols \*
30* pcre 32* pcre2
31* json-c 33* json-c
32* pango 34* pango
33* cairo 35* cairo
34* gdk-pixbuf2 (valgfrit tillæg: system tray) 36* gdk-pixbuf2 (valgfrit: system tray)
35* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (valgfrit tillæg: man pages) \* 37* [scdoc] (valgfrit: man pages) \*
36* git \* 38* git \*
37 39
38_\*Kompiler krav_ 40_\*Kompileringsafhængighed_
39 41
40Kør følgende kommandoer: 42Kør følgende kommandoer:
41 43
@@ -43,21 +45,24 @@ Kør følgende kommandoer:
43 ninja -C build 45 ninja -C build
44 sudo ninja -C build install 46 sudo ninja -C build install
45 47
46På systemer uden 'logind', behøver du at sætte ejerens bruger-id for Sways eksekverbare filer - såkaldt SUID (Set owner User ID):
47
48 sudo chmod a+s /usr/local/bin/sway
49
50Sway vil frasige sig 'root' tilladelser kort efter opstart
51
52## Konfiguration 48## Konfiguration
53 49
54Hvis du allerede bruger i3, bør du kopiere din i3-konfiguration til `~/.config/sway/config` og 50Hvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til
55det vil bare fungerer. Ellers skal du kopiere eksempel konfigurations filen til 51`~/.config/sway/config`. Ellers skal du kopiere eksempelkonfigurationsfilen til
56`~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`. 52`~/.config/sway/config`. Den er normalt placeret i `/etc/sway/config`. Kør
57Kør `man 5 sway` for at få oplysninger om konfigurationen. 53`man 5 sway` for at få oplysninger om konfigurationen.
58 54
59## Kører 55## Eksekvering
60 56
61Kør `sway` fra en TTY. Nogle display managers fungerer muligvis, men understøttes ikke af 57Kør `sway` fra en TTY. Nogle display managers kan fungere, men Sway yder ikke
62Sway (gdm er kendt for at fungere temmelig godt). 58support til dem (gdm er kendt for at fungere temmelig godt).
63 59
60[i3]: https://i3wm.org/
61[Wayland]: http://wayland.freedesktop.org/
62[Ofte stillede spørgsmål]: https://github.com/swaywm/sway/wiki
63[IRC kanal]: https://web.libera.chat/gamja/?channels=#sway
64[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
65[GitHub releases]: https://github.com/swaywm/sway/releases
66[Opsætning til udvikling]: https://github.com/swaywm/sway/wiki/Development-Setup
67[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
68[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.es.md b/README.es.md
index 838778d4..1f1657df 100644
--- a/README.es.md
+++ b/README.es.md
@@ -1,7 +1,7 @@
1# sway 1# sway
2 2
3sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con [i3](https://i3wm.org/). 3sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con [i3](https://i3wm.org/).
4Lea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](https://web.libera.chat/?channels=#sway) (#sway on 4Lea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway on
5irc.libera.chat). 5irc.libera.chat).
6 6
7## Firmas de las versiones 7## Firmas de las versiones
@@ -25,10 +25,10 @@ escriba un email a sir@cmpwn.com
25Instale las dependencias: 25Instale las dependencias:
26 26
27* meson \* 27* meson \*
28* [wlroots](https://github.com/swaywm/wlroots) 28* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
29* wayland 29* wayland
30* wayland-protocols \* 30* wayland-protocols \*
31* pcre 31* pcre2
32* json-c 32* json-c
33* pango 33* pango
34* cairo 34* cairo
@@ -44,12 +44,6 @@ Desde su consola, ejecute las órdenes:
44 ninja -C build 44 ninja -C build
45 sudo ninja -C build install 45 sudo ninja -C build install
46 46
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 86d434e5..7752fc70 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -35,7 +35,7 @@ Installez les dépendances :
35* [wlroots] 35* [wlroots]
36* wayland 36* wayland
37* wayland-protocols \* 37* wayland-protocols \*
38* pcre 38* pcre2
39* json-c 39* json-c
40* pango 40* pango
41* cairo 41* cairo
@@ -51,12 +51,6 @@ Exécutez ces commandes :
51 ninja -C build 51 ninja -C build
52 sudo ninja -C build install 52 sudo ninja -C build install
53 53
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
@@ -74,10 +68,10 @@ bien fonctionner).
74[Wayland]: http://wayland.freedesktop.org/ 68[Wayland]: http://wayland.freedesktop.org/
75[i3]: https://i3wm.org/ 69[i3]: https://i3wm.org/
76[FAQ]: https://github.com/swaywm/sway/wiki 70[FAQ]: https://github.com/swaywm/sway/wiki
77[canal IRC]: https://web.libera.chat/?channels=#sway 71[canal IRC]: https://web.libera.chat/gamja/?channels=#sway
78[abdelq]: https://github.com/abdelq 72[abdelq]: https://github.com/abdelq
79[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 73[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
80[versions GitHub]: https://github.com/swaywm/sway/releases 74[versions GitHub]: https://github.com/swaywm/sway/releases
81[Configuration de développement]: https://github.com/swaywm/sway/wiki/Development-Setup 75[Configuration de développement]: https://github.com/swaywm/sway/wiki/Development-Setup
82[wlroots]: https://github.com/swaywm/wlroots 76[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
83[scdoc]: https://git.sr.ht/~sircmpwn/scdoc 77[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.ge.md b/README.ge.md
new file mode 100644
index 00000000..bb8b9a34
--- /dev/null
+++ b/README.ge.md
@@ -0,0 +1,61 @@
1# sway
2
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
new file mode 100644
index 00000000..d697f78e
--- /dev/null
+++ b/README.gr.md
@@ -0,0 +1,67 @@
1# Sway
2
3Το sway ένα [i3]-συμβατό [Wayland] compositor. Διαβάστε το [FAQ]. Μπείτε στο
4[IRC channel] \(#sway on irc.libera.chat).
5
6## ΥπογÏαφές δημοσιεÏσεων
7
8Οι εκδόσεις είναι υπογεÏαμμένες με [E88F5E48] και δημοσιευμένες [στο GitHub][GitHub releases].
9
10## Εγκατάσταση
11
12### Από πακέτα
13
14Το Sway είναι διαθέσιμο σε πολλά distributions. Δοκιμάστε εγκαταστώντας το "sway" package για
15το δικό σας.
16
17Εάν ενδιαφέÏεστε για packaging του sway για το distribution σας, να πάτε στο IRC
18channel ή στείλτε ένα email στο sir@cmpwn.com για συμβουλές.
19
20### Compiling από πηγή
21
22ΤσεκάÏετε [αυτό το wiki page][Development setup] εάμα θέλετε να κάνετε build το HEAD του
23sway και wlroots γιά τεστάÏισμα ή development.
24
25Εγκατάσταση των dependencies:
26
27* meson \*
28* [wlroots]
29* wayland
30* wayland-protocols \*
31* pcre2
32* json-c
33* pango
34* cairo
35* gdk-pixbuf2 (Ï€ÏοαιÏετικό: system tray)
36* [scdoc] (Ï€ÏοαιÏετικό: man pages) \*
37* git (Ï€ÏοαιÏετικό: πληÏοφοÏίες εκδώσεων) \*
38
39_\*Compile-time dep_
40
41ΤÏέξτε αυτά τα commands:
42
43 meson build/
44 ninja -C build/
45 sudo ninja -C build/ install
46
47## Configuration
48
49Εάν ήδη χÏησιμοποιήτε το i3, αντιγÏάψτε το i3 config σας στο `~/.config/sway/config` και
50θα δουλέψει out of the box. Αλλιώς, αντιγÏάψτε το sample configuration αÏχείο στο
51`~/.config/sway/config`. Το οποίο συνήθως βÏίσκεται στο `/etc/sway/config`.
52Κάντε run `man 5 sway` για πληÏοφοÏίες Ï„Î¿Ï configuration.
53
54## ΤÏέχοντας
55
56ΤÏέξτε `sway` από ένα TTY. ΜεÏίκα display managers μποÏεί να δουλέψουν αλλά δέν είναι συμβατά με
57το sway (το gdm γνωÏίζεται να δουλέβει σχετικά καλά).
58
59[i3]: https://i3wm.org/
60[Wayland]: http://wayland.freedesktop.org/
61[FAQ]: https://github.com/swaywm/sway/wiki
62[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
63[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
64[GitHub releases]: https://github.com/swaywm/sway/releases
65[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
66[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
67[scdoc]: https://git.sr.ht/~sircmpwn/scdoc \ No newline at end of file
diff --git a/README.hi.md b/README.hi.md
new file mode 100644
index 00000000..eae5e90a
--- /dev/null
+++ b/README.hi.md
@@ -0,0 +1,63 @@
1# sway
2
3sway à¤à¤• [i3](https://i3wm.org/)-अनà¥à¤•à¥‚ल
4[Wayland](https://wayland.freedesktop.org/) Compositor है।
5[FAQ](https://github.com/swaywm/sway/wiki) पढिये। [IRC
6Channel](https://web.libera.chat/gamja/?channels=#sway)
7(irc.libera.chat पर #sway) में भी जà¥à¤¡à¤¿à¤¯à¥‡à¥¤
8
9## रिलीज हसà¥à¤¤à¤¾à¤•à¥à¤·à¤°
10
11रिलीजें
12[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
13से साइन होतें हैं और [Github पर](https://github.com/swaywm/sway/releases) पà¥à¤°à¤•à¤¾à¤¶à¤¿à¤¤ होते हैं।
14
15## इंसà¥à¤Ÿà¥Œà¤²à¥‡à¤¶à¤¨
16
17### पैकेजों के दà¥à¤µà¤¾à¤°à¤¾
18
19Sway कई distributions में उपà¥à¤²à¤¬à¥à¤§ है। आप अपने में "sway" नामक पैकेज इंसà¥à¤Ÿà¥Œà¤² करके देख
20सकते हैं।
21
22### Source से compile करके
23
24यदि आप परीकà¥à¤·à¤£ और विकास के लिठsway और wlroots के नवीनतम संसà¥à¤•à¤°à¤£ बनाना
25चाहते हैं, तो [यह विकी
26पृषà¥à¤ ](https://github.com/swaywm/sway/wiki/Development-Setup) देखें।
27
28निरà¥à¤­à¤°à¤¤à¤¾à¤à¤‚:
29
30* meson \*
31* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
32* wayland
33* wayland-protocols \*
34* pcre2
35* json-c
36* pango
37* cairo
38* gdk-pixbuf (वैकलà¥à¤ªà¤¿à¤•: system tray के लिये)
39* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (वैकलà¥à¤ªà¤¿à¤•: man पृषà¥à¤ à¥‹à¤‚ के लिये)
40 \*
41* git (वैकलà¥à¤ªà¤¿à¤•: संसà¥à¤•à¤°à¤£ जानने के लिये)
42
43_\* Compilation के समय आवशà¥à¤¯à¤•_
44
45ये commands चलाà¤à¤‚:
46
47 meson build/
48 ninja -C build/
49 sudo ninja -C build/ install
50
51## Configuration
52
53अगर आप पहले से ही i3 का उपयोग करते हैं तो अपने i3 config को
54`~/.config/sway/config` में copy कर लीजिये और वह बिना किसी परिवरà¥à¤¤à¤¨ के काम
55करेगा। अनà¥à¤¯à¤¥à¤¾, नमूने configuration file को `~/.config/sway/config` में copy
56कर लीजिये। यह सामानà¥à¤¯à¤¤à¤ƒ `/etc/sway/config` में पाया जाता है। `man 5
57sway` से आप configuration के बारे में जानकारी पà¥à¤°à¤¾à¤ªà¥à¤¤ कर सकते हैं।
58
59## चलाना
60
61आप à¤à¤• tty से `sway` को चला सकते हैं। कà¥à¤› display managers काम करते हैं परनà¥à¤¤à¥ ये
62sway के दà¥à¤µà¤¾à¤°à¤¾ समरà¥à¤¥à¤¿à¤¤ नहीं है (gdm के बारे में जाना गया है कि वह सही काम करता
63है)।
diff --git a/README.hu.md b/README.hu.md
index f673627a..82ca6785 100644
--- a/README.hu.md
+++ b/README.hu.md
@@ -28,7 +28,7 @@ Telepítsd a függőségeket:
28* [wlroots] 28* [wlroots]
29* wayland 29* wayland
30* wayland-protocols \* 30* wayland-protocols \*
31* pcre 31* pcre2
32* json-c 32* json-c
33* pango 33* pango
34* cairo 34* cairo
@@ -44,13 +44,6 @@ Futtasd ezeket a parancsokat:
44 ninja -C build 44 ninja -C build
45 sudo ninja -C build install 45 sudo ninja -C build install
46 46
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
@@ -69,9 +62,9 @@ gdm-ről ismeretes, hogy egész jól működik.)
69[i3]: https://i3wm.org/ 62[i3]: https://i3wm.org/
70[Wayland]: http://wayland.freedesktop.org/ 63[Wayland]: http://wayland.freedesktop.org/
71[FAQ]: https://github.com/swaywm/sway/wiki 64[FAQ]: https://github.com/swaywm/sway/wiki
72[IRC channel]: https://web.libera.chat/?channels=#sway 65[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
73[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 66[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
74[GitHub releases]: https://github.com/swaywm/sway/releases 67[GitHub releases]: https://github.com/swaywm/sway/releases
75[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup 68[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
76[wlroots]: https://github.com/swaywm/wlroots 69[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
77[scdoc]: https://git.sr.ht/~sircmpwn/scdoc 70[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.ir.md b/README.ir.md
new file mode 100644
index 00000000..a485a405
--- /dev/null
+++ b/README.ir.md
@@ -0,0 +1,54 @@
1# sway
2
3&rlm;sway یک کامپوزیتور الهام گرÙته از [i3](https://i3wm.org/) بر روی [Wayland](http://wayland.freedesktop.org/) است. [سوال‌های متداول](https://github.com/swaywm/sway/wiki) را بخوانید. در [کانال
4IRC](http://web.libera.chat/gamja/?channels=sway&uio=d4) عضو شوید (&lrm;#sway&rlm; در
5irc.libera.chat).
6
7برای حمایت از تیم توسعه sway به [صÙحه
8Patreon با نام کاربری SirCmpwn](https://patreon.com/sircmpwn) مراجعه کنید.
9
10## امضای نسخه‌ها
11
12امضای نسخه‌ها با [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) در [GitHub](https://github.com/swaywm/sway/releases) منتشر می‌شود.
13
14## شیوه نصب
15
16### از بسته‌های رسمی
17
18&rlm;sway در بسته‌های رسمی توزیع‌های مختل٠وجود دارد. بسته «sway» را نصب کنید. در صورتی Ú©Ù‡ بسته رسمی وجود نداشت، برای آگاهی بیشتر درباره نصب روی توزیعتان به این [صÙحه راهنما](https://github.com/swaywm/sway/wiki/Unsupported-packages) مراجعه کنید.
19
20اگر به ایجاد بسته sway برای توزیعتان علاقه‌مند هستید، از کانال IRC استÙاده کنید یا به sir@cmpwn.com ایمیل بزنید.
21
22### کامپایل کردن کد
23
24چنانچه می‌خواهید آخرین نسخه کد sway Ùˆ wlroots را برای آزمایش یا توسعه بسازید به این [صÙحه راهنما](https://github.com/swaywm/sway/wiki/Development-Setup) مراجعه کنید.
25
26بسته‌های مورد نیاز:
27
28* meson \*
29* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
30* wayland
31* wayland-protocols \*
32* pcre2
33* json-c
34* pango
35* cairo
36* gdk-pixbuf2 (انتخابی: برای system tray)
37* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (انتخابی: برای صÙحه‌های راهنما) \*
38* git (انتخابی: برای اطلاع در خصوص نسخه‌ها) \*
39
40_\*نیازمندی‌های زمان کامپایل برنامه_
41
42این Ùرمان‌ها را اجرا کنید:
43
44 meson build
45 ninja -C build
46 sudo ninja -C build install
47
48### شخصی سازی و تنظیمات
49
50اگر در حال حاضر از i3 استÙاده می‌کنید، تنظیمات i3 خودتان را در Ùایل ‪`~/.config/sway/config`‬ Ú©Ù¾ÛŒ کنید Ùˆ بدون نیاز به تغییر کار خواهد کرد. در غیر این‌صورت، Ùایل نمونه تنظیمات را استÙاده کنید. این Ùایل عموما در ‪`/etc/sway/config`‬ قرار دارد. برای آگاهی بیشتر `man 5 sway` را اجرا کنید.
51
52## اجرا
53
54در محیط TTY کاÙیست `sway` را اجرا کنید. ممکن است ابزارهای مدیریت نمایشگری نیز برای این کار وجود داشته باشند اما از طر٠sway پشتیبانی نمی‌شوند (gdm عملکرد خوبی در این زمینه دارد).
diff --git a/README.it.md b/README.it.md
new file mode 100644
index 00000000..82bb5783
--- /dev/null
+++ b/README.it.md
@@ -0,0 +1,66 @@
1# sway
2
3sway è un compositore di [Wayland] compatibile con [i3]. Leggi le [FAQ].
4Unisciti al [canale di IRC] \(#sway su irc.libera.chat).
5
6## Firma delle versioni
7
8Le versioni sono firmate con la chiave [E88F5E48] e sono pubblicate
9[su GitHub][GitHub releases].
10
11## Installazione
12
13### Da un pacchetto
14
15Sway è disponibile in molte distribuzioni, prova a installare il pacchetto
16"sway" per la tua.
17
18### Compilazione dei sorgenti
19
20Consulta [questa pagina del wiki][Development setup] se vuoi compilare l'HEAD
21di sway e wlroots per testarli o contribuire allo sviluppo.
22
23Installa le dipendenze:
24
25* meson \*
26* [wlroots]
27* wayland
28* wayland-protocols \*
29* pcre
30* json-c
31* pango
32* cairo
33* gdk-pixbuf2 (opzionale: area di notifica)
34* [scdoc] (opzionale: pagine del manuale) \*
35* git (opzionale: informazioni sulla versione) \*
36
37_\* Dipendenza necessaria per la compilazione_
38
39Esegui questi comandi:
40
41 meson build/
42 ninja -C build/
43 sudo ninja -C build/ install
44
45## Configurazione
46
47Se hai già usato i3, copia il tuo file di configurazione in
48`~/.config/sway/config` e sway funzionerà immediatamente. Altrimenti, copia il
49file d'esempio in `~/.config/sway/config`, generalmente è situato in
50`/etc/sway/config`. Consulta `man 5 sway` per ulteriori informazioni sulla
51configurazione.
52
53## Esecuzione
54
55Lancia `sway` da un TTY. Alcuni gestori d'accesso potrebbero funzionare ma non
56sono supportati da sway (gdm funziona abbastanza bene).
57
58[i3]: https://i3wm.org/
59[Wayland]: http://wayland.freedesktop.org/
60[FAQ]: https://github.com/swaywm/sway/wiki
61[canale di IRC]: https://web.libera.chat/gamja/?channels=#sway
62[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
63[GitHub releases]: https://github.com/swaywm/sway/releases
64[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
65[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
66[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.ja.md b/README.ja.md
index 9e7daee1..4e9a9971 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -2,7 +2,7 @@
2 2
3Swayã¯[i3](https://i3wm.org/)互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚ 3Swayã¯[i3](https://i3wm.org/)互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚
4[FAQ](https://github.com/swaywm/sway/wiki)ã‚‚åˆã‚ã›ã¦ã”覧ãã ã•ã„。 4[FAQ](https://github.com/swaywm/sway/wiki)ã‚‚åˆã‚ã›ã¦ã”覧ãã ã•ã„。
5[IRC ãƒãƒ£ãƒ³ãƒãƒ«](https://web.libera.chat/?channels=#sway) (irc.libera.chatã®#sway)ã‚‚ã‚ã‚Šã¾ã™ã€‚ 5[IRC ãƒãƒ£ãƒ³ãƒãƒ«](https://web.libera.chat/gamja/?channels=#sway) (irc.libera.chatã®#sway)ã‚‚ã‚ã‚Šã¾ã™ã€‚
6 6
7[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 7[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
8 8
@@ -27,18 +27,18 @@ Swayã¯æ²¢å±±ã®ãƒ‡ã‚£ã‚¹ãƒˆãƒªãƒ“ューションã§æä¾›ã•ã‚Œã¦ã„ã¾ã™ã€‚"
27次ã®ä¾å­˜ãƒ‘ッケージをインストールã—ã¦ãã ã•ã„: 27次ã®ä¾å­˜ãƒ‘ッケージをインストールã—ã¦ãã ã•ã„:
28 28
29* meson \* 29* meson \*
30* [wlroots](https://github.com/swaywm/wlroots) 30* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
31* wayland 31* wayland
32* wayland-protocols \* 32* wayland-protocols \*
33* pcre 33* pcre2
34* json-c 34* json-c
35* pango 35* pango
36* cairo 36* cairo
37* gdk-pixbuf2 (システムイコンã§å¿…è¦ã§ã™) 37* gdk-pixbuf2 (ä»»æ„: システムイコンã§å¿…è¦ã§ã™)
38* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (manã§å¿…è¦ã§ã™) \* 38* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (ä»»æ„: manã§å¿…è¦ã§ã™) \*
39* git \* 39* git (ä»»æ„: ãƒãƒ¼ã‚¸ãƒ§ãƒ³æƒ…å ±ã§å¿…è¦ã§ã™) \*
40 40
41_\*コンパイルã®æ™‚_ 41_\*コンパイル時ã®ä¾å­˜_
42 42
43次ã®ã‚³ãƒžãƒ³ãƒ‰ã‚’実行ã—ã¦ãã ã•ã„: 43次ã®ã‚³ãƒžãƒ³ãƒ‰ã‚’実行ã—ã¦ãã ã•ã„:
44 44
@@ -46,12 +46,6 @@ _\*コンパイルã®æ™‚_
46 ninja -C build 46 ninja -C build
47 sudo ninja -C build install 47 sudo ninja -C build install
48 48
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 4d5619de..e086c174 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -1,7 +1,7 @@
1# sway 1# sway
2 2
3sway는 [i3](https://i3wm.org/)-호환 [Wayland](http://wayland.freedesktop.org/) ì»´í¬ì§€í„°ìž…니다. 3sway는 [i3](https://i3wm.org/)-호환 [Wayland](http://wayland.freedesktop.org/) ì»´í¬ì§€í„°ìž…니다.
4[FAQ](https://github.com/swaywm/sway/wiki)를 ì½ì–´ë³´ì„¸ìš”. [IRC 채ë„](https://web.libera.chat/?channels=#sway) (#sway on irc.libera.chat)ë„ ìžˆìŠµë‹ˆë‹¤. 4[FAQ](https://github.com/swaywm/sway/wiki)를 ì½ì–´ë³´ì„¸ìš”. [IRC 채ë„](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat)ë„ ìžˆìŠµë‹ˆë‹¤.
5 5
6## 릴리즈 서명 6## 릴리즈 서명
7 7
@@ -24,10 +24,10 @@ IRC 채ë„ì„ ë°©ë¬¸í•˜ê±°ë‚˜ sir@cmpwn.com으로 ì´ë©”ì¼ì„ ë³´ë‚´ ìƒë‹´ ë°›
24ë‹¤ìŒ ì˜ì¡´ íŒ¨í‚¤ì§€ë“¤ì„ ì„¤ì¹˜í•´ 주세요: 24ë‹¤ìŒ ì˜ì¡´ íŒ¨í‚¤ì§€ë“¤ì„ ì„¤ì¹˜í•´ 주세요:
25 25
26* meson \* 26* meson \*
27* [wlroots](https://github.com/swaywm/wlroots) 27* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
28* wayland 28* wayland
29* wayland-protocols \* 29* wayland-protocols \*
30* pcre 30* pcre2
31* json-c 31* json-c
32* pango 32* pango
33* cairo 33* cairo
@@ -43,12 +43,6 @@ _\*ì»´íŒŒì¼ ë–„ í•„ìš”_
43 ninja -C build 43 ninja -C build
44 sudo ninja -C build install 44 sudo ninja -C build install
45 45
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 3946040f..15c7c099 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
1# sway 1# sway
2 2
3**[English][en]** - [日本語][ja] - [Français][fr] - [УкраїнÑька][uk] - [Español][es] - [Polski][pl] - [中文-简体][zh-CN] - [Deutsch][de] - [Nederlands][nl] - [РуÑÑкий][ru] - [中文-ç¹é«”][zh-TW] - [Português][pt] - [Dansk][dk] - [한국어][ko] - [Română][ro] - [Magyar][hu] - [Türkçe][tr] 3**[English][en]** - [عربي][ar] - [ÄŒesky][cs] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქáƒáƒ áƒ—ული][ge] - [Ελληνικά][gr] - [हिनà¥à¤¦à¥€][hi] - [Magyar][hu] - [Ùارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [РуÑÑкий][ru] - [Svenska][sv] - [Türkçe][tr] - [УкраїнÑька][uk] - [中文-简体][zh-CN] - [中文-ç¹é«”][zh-TW]
4 4
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).
@@ -16,9 +16,6 @@ Releases are signed with [E88F5E48] and published [on GitHub][GitHub releases].
16Sway is available in many distributions. Try installing the "sway" package for 16Sway is available in many distributions. Try installing the "sway" package for
17yours. 17yours.
18 18
19If you're interested in packaging sway for your distribution, stop by the IRC
20channel or shoot an email to sir@cmpwn.com for advice.
21
22### Compiling from Source 19### Compiling from Source
23 20
24Check out [this wiki page][Development setup] if you want to build the HEAD of 21Check out [this wiki page][Development setup] if you want to build the HEAD of
@@ -30,15 +27,16 @@ Install dependencies:
30* [wlroots] 27* [wlroots]
31* wayland 28* wayland
32* wayland-protocols \* 29* wayland-protocols \*
33* pcre 30* pcre2
34* json-c 31* json-c
35* pango 32* pango
36* cairo 33* cairo
37* gdk-pixbuf2 (optional: system tray) 34* gdk-pixbuf2 (optional: additional image formats for system tray)
35* [swaybg] (optional: wallpaper)
38* [scdoc] (optional: man pages) \* 36* [scdoc] (optional: man pages) \*
39* git (optional: version info) \* 37* git (optional: version info) \*
40 38
41_\*Compile-time dep_ 39_\* Compile-time dep_
42 40
43Run these commands: 41Run these commands:
44 42
@@ -46,12 +44,6 @@ Run these commands:
46 ninja -C build/ 44 ninja -C build/
47 sudo ninja -C build/ install 45 sudo ninja -C build/ install
48 46
49On systems without logind, you need to suid the sway binary:
50
51 sudo chmod a+s /usr/local/bin/sway
52
53Sway will drop root permissions shortly after startup.
54
55## Configuration 47## Configuration
56 48
57If 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
@@ -65,28 +57,38 @@ Run `sway` from a TTY. Some display managers may work but are not supported by
65sway (gdm is known to work fairly well). 57sway (gdm is known to work fairly well).
66 58
67[en]: https://github.com/swaywm/sway#readme 59[en]: https://github.com/swaywm/sway#readme
68[ja]: https://github.com/swaywm/sway/blob/master/README.ja.md 60[ar]: README.ar.md
69[fr]: https://github.com/swaywm/sway/blob/master/README.fr.md 61[cs]: README.cs.md
70[uk]: https://github.com/swaywm/sway/blob/master/README.uk.md 62[de]: README.de.md
71[es]: https://github.com/swaywm/sway/blob/master/README.es.md 63[dk]: README.dk.md
72[pl]: https://github.com/swaywm/sway/blob/master/README.pl.md 64[es]: README.es.md
73[zh-CN]: https://github.com/swaywm/sway/blob/master/README.zh-CN.md 65[fr]: README.fr.md
74[de]: https://github.com/swaywm/sway/blob/master/README.de.md 66[ge]: README.ge.md
75[nl]: https://github.com/swaywm/sway/blob/master/README.nl.md 67[gr]: README.gr.md
76[ru]: https://github.com/swaywm/sway/blob/master/README.ru.md 68[hi]: README.hi.md
77[zh-TW]: https://github.com/swaywm/sway/blob/master/README.zh-TW.md 69[hu]: README.hu.md
78[pt]: https://github.com/swaywm/sway/blob/master/README.pt.md 70[ir]: README.ir.md
79[dk]: https://github.com/swaywm/sway/blob/master/README.dk.md 71[it]: README.it.md
80[ko]: https://github.com/swaywm/sway/blob/master/README.ko.md 72[ja]: README.ja.md
81[ro]: https://github.com/swaywm/sway/blob/master/README.ro.md 73[ko]: README.ko.md
82[hu]: https://github.com/swaywm/sway/blob/master/README.hu.md 74[nl]: README.nl.md
83[tr]: https://github.com/swaywm/sway/blob/master/README.tr.md 75[no]: README.no.md
76[pl]: README.pl.md
77[pt]: README.pt.md
78[ro]: README.ro.md
79[ru]: README.ru.md
80[sv]: README.sv.md
81[tr]: README.tr.md
82[uk]: README.uk.md
83[zh-CN]: README.zh-CN.md
84[zh-TW]: README.zh-TW.md
84[i3]: https://i3wm.org/ 85[i3]: https://i3wm.org/
85[Wayland]: http://wayland.freedesktop.org/ 86[Wayland]: http://wayland.freedesktop.org/
86[FAQ]: https://github.com/swaywm/sway/wiki 87[FAQ]: https://github.com/swaywm/sway/wiki
87[IRC channel]: https://web.libera.chat/?channels=#sway 88[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
88[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 89[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
89[GitHub releases]: https://github.com/swaywm/sway/releases 90[GitHub releases]: https://github.com/swaywm/sway/releases
90[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup 91[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
91[wlroots]: https://github.com/swaywm/wlroots 92[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
93[swaybg]: https://github.com/swaywm/swaybg/
92[scdoc]: https://git.sr.ht/~sircmpwn/scdoc 94[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.nl.md b/README.nl.md
index a82a3d4c..bf1ea975 100644
--- a/README.nl.md
+++ b/README.nl.md
@@ -2,7 +2,7 @@
2 2
3Sway is een [i3](https://i3wm.org/)-compatibele [Wayland](http://wayland.freedesktop.org/) compositor. 3Sway is een [i3](https://i3wm.org/)-compatibele [Wayland](http://wayland.freedesktop.org/) compositor.
4Lees de [FAQ](https://github.com/swaywm/sway/wiki). Word lid van het [IRC 4Lees de [FAQ](https://github.com/swaywm/sway/wiki). Word lid van het [IRC
5kanaal](https://web.libera.chat/?channels=#sway) (#sway op 5kanaal](https://web.libera.chat/gamja/?channels=#sway) (#sway op
6irc.libera.chat). 6irc.libera.chat).
7 7
8## Releasehandtekeningen 8## Releasehandtekeningen
@@ -25,10 +25,10 @@ kanaal of stuur een e-mail naar sir@cmpwn.com voor advies.
25Afhankelijkheden installeren: 25Afhankelijkheden installeren:
26 26
27* meson \* 27* meson \*
28* [wlroots](https://github.com/swaywm/wlroots) 28* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
29* wayland 29* wayland
30* wayland-protocols \* 30* wayland-protocols \*
31* pcre 31* pcre2
32* json-c 32* json-c
33* pango 33* pango
34* cairo 34* cairo
@@ -44,12 +44,6 @@ Voer deze opdrachten uit:
44 ninja -C build 44 ninja -C build
45 sudo ninja -C build install 45 sudo ninja -C build install
46 46
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 f981c741..65b3c3a1 100644
--- a/README.pl.md
+++ b/README.pl.md
@@ -1,7 +1,7 @@
1# sway 1# sway
2 2
3sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z [i3](https://i3wm.org/). 3sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z [i3](https://i3wm.org/).
4Przeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](https://web.libera.chat/?channels=#sway) 4Przeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](https://web.libera.chat/gamja/?channels=#sway)
5(#sway na irc.libera.chat). 5(#sway na irc.libera.chat).
6 6
7## Podpisy cyfrowe wydań 7## Podpisy cyfrowe wydań
@@ -25,10 +25,10 @@ adres sir@cmpwn.com w celu uzyskania wskazówek.
25Zainstaluj zależności: 25Zainstaluj zależności:
26 26
27* meson \* 27* meson \*
28* [wlroots](https://github.com/swaywm/wlroots) 28* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
29* wayland 29* wayland
30* wayland-protocols \* 30* wayland-protocols \*
31* pcre 31* pcre2
32* json-c 32* json-c
33* pango 33* pango
34* cairo 34* cairo
@@ -44,12 +44,6 @@ Wykonaj następujące polecenia:
44 ninja -C build 44 ninja -C build
45 sudo ninja -C build install 45 sudo ninja -C build install
46 46
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 c33cdf04..c1611a31 100644
--- a/README.pt.md
+++ b/README.pt.md
@@ -2,7 +2,7 @@
2 2
3O sway é um compositor do [Wayland](http://wayland.freedesktop.org/) compatível com o [i3](https://i3wm.org/). 3O sway é um compositor do [Wayland](http://wayland.freedesktop.org/) compatível com o [i3](https://i3wm.org/).
4Leia o [FAQ](https://github.com/swaywm/sway/wiki). Junte-se ao [canal do 4Leia o [FAQ](https://github.com/swaywm/sway/wiki). Junte-se ao [canal do
5IRC](https://web.libera.chat/?channels=#sway) (#sway em 5IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway em
6irc.libera.chat). 6irc.libera.chat).
7 7
8## Assinatura das versões 8## Assinatura das versões
@@ -27,10 +27,10 @@ Verifique [essa página da wiki](https://github.com/swaywm/sway/wiki/Development
27Instale as dependências: 27Instale as dependências:
28 28
29* meson \* 29* meson \*
30* [wlroots](https://github.com/swaywm/wlroots) 30* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
31* wayland 31* wayland
32* wayland-protocols \* 32* wayland-protocols \*
33* pcre 33* pcre2
34* json-c 34* json-c
35* pango 35* pango
36* cairo 36* cairo
@@ -46,12 +46,6 @@ Execute esses comandos:
46 ninja -C build 46 ninja -C build
47 sudo ninja -C build install 47 sudo ninja -C build install
48 48
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 f3f4a32b..a3559a8b 100644
--- a/README.ro.md
+++ b/README.ro.md
@@ -1,7 +1,7 @@
1# sway 1# sway
2 2
3sway este un compositor pentru [Wayland](http://wayland.freedesktop.org/) compatibil cu [i3](https://i3wm.org/). 3sway este un compositor pentru [Wayland](http://wayland.freedesktop.org/) compatibil cu [i3](https://i3wm.org/).
4Citiți [FAQ](https://github.com/swaywm/sway/wiki)-ul. Connectați-vă la canalul nostru [IRC](https://web.libera.chat/?channels=#sway) (#sway pe irc.libera.chat). 4Citiți [FAQ](https://github.com/swaywm/sway/wiki)-ul. Connectați-vă la canalul nostru [IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway pe irc.libera.chat).
5 5
6## Semnarea digitală 6## Semnarea digitală
7 7
@@ -22,10 +22,10 @@ Dacă sunteți interesați in a crea pachete pentru distribuția voastră, infor
22Dependențe pentru instalare: 22Dependențe pentru instalare:
23 23
24* meson \* 24* meson \*
25* [wlroots](https://github.com/swaywm/wlroots) 25* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
26* wayland 26* wayland
27* wayland-protocols \* 27* wayland-protocols \*
28* pcre 28* pcre2
29* json-c 29* json-c
30* pango 30* pango
31* cairo 31* cairo
@@ -43,14 +43,6 @@ Rulați aceste comenzi:
43 sudo ninja -C build install 43 sudo ninja -C build install
44``` 44```
45 45
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 257578a8..edc0eda7 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -29,7 +29,7 @@ sway и wlroots Ð´Ð»Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ разработки.
29* [wlroots] 29* [wlroots]
30* wayland 30* wayland
31* wayland-protocols \* 31* wayland-protocols \*
32* pcre 32* pcre2
33* json-c 33* json-c
34* pango 34* pango
35* cairo 35* cairo
@@ -45,12 +45,6 @@ _\*ЗавиÑимоÑти Ð´Ð»Ñ Ñборки_
45 ninja -C build 45 ninja -C build
46 sudo ninja -C build install 46 sudo ninja -C build install
47 47
48Ðа ÑиÑтемах без logind вам понадобитÑÑ Ð´Ð¾Ð±Ð°Ð²Ð¸Ñ‚ÑŒ suid к файлу программы sway:
49
50 sudo chmod a+s /usr/local/bin/sway
51
52sway ÑброÑит root-права при запуÑке.
53
54## ÐаÑтройка 48## ÐаÑтройка
55 49
56ЕÑли вы уже иÑпользуете i3, Ñкопируйте ваш конфигурационный файл i3 в `~/.config/sway/config`, и 50ЕÑли вы уже иÑпользуете i3, Ñкопируйте ваш конфигурационный файл i3 в `~/.config/sway/config`, и
@@ -66,9 +60,9 @@ sway (gdm работает довольно неплохо).
66[i3]: https://i3wm.org/ 60[i3]: https://i3wm.org/
67[Wayland]: http://wayland.freedesktop.org/ 61[Wayland]: http://wayland.freedesktop.org/
68[FAQ]: https://github.com/swaywm/sway/wiki 62[FAQ]: https://github.com/swaywm/sway/wiki
69[IRC channel]: https://web.libera.chat/?channels=#sway 63[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
70[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 64[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
71[GitHub releases]: https://github.com/swaywm/sway/releases 65[GitHub releases]: https://github.com/swaywm/sway/releases
72[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup 66[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
73[wlroots]: https://github.com/swaywm/wlroots 67[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
74[scdoc]: https://git.sr.ht/~sircmpwn/scdoc 68[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.sv.md b/README.sv.md
new file mode 100644
index 00000000..c50ca068
--- /dev/null
+++ b/README.sv.md
@@ -0,0 +1,83 @@
1# sway
2
3[English][en] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - **[Svenska][sv]** - [Ελληνικά][gr] - [Magyar][hu] - [Ùارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Polski][pl] - [Português][pt] - [Română][ro] - [РуÑÑкий][ru] - [Türkçe][tr] - [УкраїнÑька][uk] - [中文-简体][zh-CN] - [中文-ç¹é«”][zh-TW]
4
5sway är en [i3]-kompatibel [Wayland] compositor. Läs våran [FAQ]-sida. Gå med i vår
6[IRC-kanal] \(#sway på irc.libera.chat).
7
8## Utgåvosignaturer
9
10Utgåvor är signerade med [E88F5E48] och publicerade på [GitHub][GitHub releases].
11
12## Installering
13
14### Med pakethanterare
15
16Sway är tillgänglig i många distributioner. Prova att installera "sway" med din distributions pakethanterare.
17
18### Genom att kompilera från källkod
19
20Kolla in [denna wiki-sida][Development setup] om du vill bygga sway och wlroots HEAD för testning eller utveckling.
21
22Installera paket som sway behöver:
23
24* meson \*
25* [wlroots]
26* wayland
27* wayland-protocols \*
28* pcre2
29* json-c
30* pango
31* cairo
32* gdk-pixbuf2 (valbar: systembricka)
33* [scdoc] (valbar: manualer) \*
34* git (valbar: versioninfo) \*
35
36_\* Krav för kompilering_
37
38Kör dessa kommandon:
39
40 meson build/
41 ninja -C build/
42 sudo ninja -C build/ install
43
44## Konfiguration
45
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.
47Kopiera annars exemplarkonfigurationsfilen till `~/.config/sway/config`. Den ligger oftast i `/etc/sway/config`.
48Kör `man 5 sway` för mer information kring konfigurationen.
49
50## Att köra sway
51
52Kör `sway` från en TTY. Vissa inloggningahanterare kan fungera men inte vara stödda av sway (gdm ska fungera hyfsat bra).
53
54[en]: https://github.com/swaywm/sway#readme
55[de]: README.de.md
56[dk]: README.dk.md
57[es]: README.es.md
58[fr]: README.fr.md
59[sv]: README.sv.md
60[gr]: README.gr.md
61[hu]: README.hu.md
62[ir]: README.ir.md
63[it]: README.it.md
64[ja]: README.ja.md
65[ko]: README.ko.md
66[nl]: README.nl.md
67[pl]: README.pl.md
68[pt]: README.pt.md
69[ro]: README.ro.md
70[ru]: README.ru.md
71[tr]: README.tr.md
72[uk]: README.uk.md
73[zh-CN]: README.zh-CN.md
74[zh-TW]: README.zh-TW.md
75[i3]: https://i3wm.org/
76[Wayland]: http://wayland.freedesktop.org/
77[FAQ]: https://github.com/swaywm/sway/wiki
78[IRC-kanal]: https://web.libera.chat/gamja/?channels=#sway
79[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
80[GitHub releases]: https://github.com/swaywm/sway/releases
81[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
82[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
83[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.tr.md b/README.tr.md
index ff603838..40de1474 100644
--- a/README.tr.md
+++ b/README.tr.md
@@ -26,7 +26,7 @@ Aşağıdaki bağımlılıkları yükleyin:
26* [wlroots] 26* [wlroots]
27* wayland 27* wayland
28* wayland-protocols \* 28* wayland-protocols \*
29* pcre 29* pcre2
30* json-c 30* json-c
31* pango 31* pango
32* cairo 32* cairo
@@ -42,12 +42,6 @@ _\*Derleme-anı bağımlılıkları_
42 ninja -C build 42 ninja -C build
43 sudo ninja -C build install 43 sudo ninja -C build install
44 44
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.
@@ -60,9 +54,9 @@ TTY'den `sway` çalıştırın. Bazı görüntü yöneticileriyle(display manag
60[i3]: https://i3wm.org/ 54[i3]: https://i3wm.org/
61[Wayland]: http://wayland.freedesktop.org/ 55[Wayland]: http://wayland.freedesktop.org/
62[FAQ]: https://github.com/swaywm/sway/wiki 56[FAQ]: https://github.com/swaywm/sway/wiki
63[IRC channel]: https://web.libera.chat/?channels=#sway 57[IRC channel]: https://web.libera.chat/gamja/?channels=#sway
64[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 58[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
65[GitHub releases]: https://github.com/swaywm/sway/releases 59[GitHub releases]: https://github.com/swaywm/sway/releases
66[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup 60[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
67[wlroots]: https://github.com/swaywm/wlroots 61[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
68[scdoc]: https://git.sr.ht/~sircmpwn/scdoc 62[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
diff --git a/README.uk.md b/README.uk.md
index 204fc40b..33359cff 100644
--- a/README.uk.md
+++ b/README.uk.md
@@ -2,7 +2,7 @@
2 2
3Sway це ÑуміÑний з [i3](https://i3wm.org/) композитор [Wayland](http://wayland.freedesktop.org/). 3Sway це ÑуміÑний з [i3](https://i3wm.org/) композитор [Wayland](http://wayland.freedesktop.org/).
4ОзнайомтеÑÑŒ з [ЧаПами](https://github.com/swaywm/sway/wiki). ПриєднуйтеÑÑŒ до [Ñпільноти в 4ОзнайомтеÑÑŒ з [ЧаПами](https://github.com/swaywm/sway/wiki). ПриєднуйтеÑÑŒ до [Ñпільноти в
5IRC](https://web.libera.chat/?channels=#sway) (#sway на 5IRC](https://web.libera.chat/gamja/?channels=#sway) (#sway на
6irc.libera.chat). 6irc.libera.chat).
7 7
8## Підтримка українÑькою мовою 8## Підтримка українÑькою мовою
@@ -36,10 +36,10 @@ Sway доÑтупний у багатьох диÑтрибутивах Linux (а
36Ð’Ñтановіть залежноÑÑ‚Ñ–: 36Ð’Ñтановіть залежноÑÑ‚Ñ–:
37 37
38* meson \* 38* meson \*
39* [wlroots](https://github.com/swaywm/wlroots) 39* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
40* wayland 40* wayland
41* wayland-protocols \* 41* wayland-protocols \*
42* pcre 42* pcre2
43* json-c 43* json-c
44* pango 44* pango
45* cairo 45* cairo
@@ -55,12 +55,6 @@ _\*Лише Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¿Ñ–Ð»Ñції_
55 ninja -C build 55 ninja -C build
56 sudo ninja -C build install 56 sudo ninja -C build install
57 57
58Ðа ÑиÑтемах без logind, необхідно вÑтановити біт SUID на виконуваний файл sway:
59
60 sudo chmod a+s /usr/local/bin/sway
61
62Sway втратить права доÑтупу root незабаром піÑÐ»Ñ Ð·Ð°Ð¿ÑƒÑку.
63
64## ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ 58## ÐалаштуваннÑ
65 59
66Якщо ви вже викориÑтовуєте i3, Ñкопіюйте Ñвій файл налаштувань 60Якщо ви вже викориÑтовуєте i3, Ñкопіюйте Ñвій файл налаштувань
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 3f25b6d9..a6f4518a 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,63 +1,50 @@
1# sway 1# sway
2 2
3sway 是和 [i3](https://i3wm.org/) 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor. 3sway 是和 [i3](https://i3wm.org/) 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor。
4阅读 [FAQ](https://github.com/swaywm/sway/wiki). 加入 [IRC 4 [查看FAQ](https://github.com/swaywm/sway/wiki)/ [加入IRC频é“](https://web.libera.chat/gamja/?channels=#sway) (#sway on irc.libera.chat)
5频é“](https://web.libera.chat/?channels=#sway) (#sway on
6irc.libera.chat).
7 5
8## å‘布签å 6## å‘行签å
9 7
10å‘布是以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) ç­¾å 8æ¯ä¸ªå‘行版都以 [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) 的密钥签å并å‘布在 [GitHub](https://github.com/swaywm/sway/releases)上。
11并å‘布在 [GitHub](https://github.com/swaywm/sway/releases).
12 9
13## 安装 10## 安装
14 11
15### 从软件包中 12### 从包管ç†å™¨å®‰è£…
16 13
17Sway 在很多å‘行版中å¯ç”¨. å°è¯•åœ¨ä½ çš„å‘行版中安装 "sway" 包. 14Sway 在很多å‘行版中å¯ç”¨ã€‚请å°è¯•åœ¨ä½ çš„å‘行版中安装 `sway` 。
18如果这ä¸å¯ç”¨, 请到 [æ­¤ wiki 页](https://github.com/swaywm/sway/wiki/Unsupported-packages)
19检查针对你的å‘行版关于安装的信æ¯.
20 15
21如果你有兴趣给你的å‘行版打包 sway, åœä¸‹æ¥åˆ° IRC 频é“或者å邮件至 sir@cmpwn.com 获å–建议. 16### 从æºç ç¼–è¯
22 17
23### 从æºä»£ç ç¼–译 18如果想è¦æž„建最新版swayå’Œwlroots用以测试和开å‘,请查看 [æ­¤wiki页é¢](https://github.com/swaywm/sway/wiki/Development-Setup)
24 19
25安装ä¾èµ–: 20安装如下ä¾èµ–:
26 21
27* meson \* 22* meson \*
28* [wlroots](https://github.com/swaywm/wlroots) 23* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
29* wayland 24* wayland
30* wayland-protocols \* 25* wayland-protocols \*
31* pcre 26* pcre2
32* json-c 27* json-c
33* pango 28* pango
34* cairo 29* cairo
35* gdk-pixbuf2 (å¯é€‰çš„: system tray) 30* gdk-pixbuf2 (å¯é€‰çš„: system tray)
36* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (å¯é€‰çš„: man pages) \* 31* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (å¯é€‰: man pages) \*
37* git \* 32* git \*
38 33
39_\*编译时ä¾èµ–_ 34_\*编译时ä¾èµ–_
40 35
41è¿è¡Œè¿™äº›å‘½ä»¤: 36è¿è¡Œå¦‚下命令:
42 37
43 meson build 38 meson build/
44 ninja -C build 39 ninja -C build/
45 sudo ninja -C build install 40 sudo ninja -C build/ install
46
47在没有 logind 的系统上, 你需è¦ç»™ sway 二进制设置 suid:
48
49 sudo chmod a+s /usr/local/bin/sway
50
51Sway 将会在å¯åŠ¨åŽå°½å¿«ä¸¢æŽ‰ root æƒé™.
52 41
53## é…ç½® 42## é…ç½®
54 43
55如果你已ç»åœ¨ä½¿ç”¨ i3, 接下æ¥å¤åˆ¶ä½ çš„ i3 é…置到 `~/.config/sway/config` 44如果你已ç»åœ¨ä½¿ç”¨i3,直接å¤åˆ¶i3é…置文件到 `~/.config/sway/config`,这是开箱å³ç”¨çš„。或者,你å¯ä»¥å¤åˆ¶é…置样例到`~/.config/sway/config`。它通常ä½äºŽ `/etc/sway/config`。
56它å¯ä»¥ç›´æŽ¥å·¥ä½œ. 或者, å¤åˆ¶æ ·æœ¬é…置文件到 45è¿è¡Œ `man 5 sway` 获å–关于é…置的更多信æ¯ã€‚
57`~/.config/sway/config`. 它通常ä½äºŽ `/etc/sway/config`.
58è¿è¡Œ `man 5 sway` 获å–关于é…置的信æ¯.
59 46
60## è¿è¡Œ 47## è¿è¡Œ
61 48
62从 TTY 中è¿è¡Œ `sway` . æŸäº›æ˜¾ç¤ºç®¡ç†å™¨å¯èƒ½ä¼šå·¥ä½œä½†å¹¶ä¸è¢« sway æ”¯æŒ 49从 TTY 中è¿è¡Œ `sway`。 æŸäº›æ˜¾ç¤ºç®¡ç†å™¨ï¼ˆDisplay Manager)也许å¯ä»¥å·¥ä½œä½†ä¸è¢« sway 支æŒã€‚
63(已知的 gdm 工作得éžå¸¸å¥½). 50(已知 gdm 工作得éžå¸¸å¥½)。
diff --git a/README.zh-TW.md b/README.zh-TW.md
index 4834ded2..2de2f63f 100644
--- a/README.zh-TW.md
+++ b/README.zh-TW.md
@@ -2,7 +2,7 @@
2 2
3sway 是一個與 [i3](https://i3wm.org/) 相容的 [Wayland](http://wayland.freedesktop.org/) compositor。 3sway 是一個與 [i3](https://i3wm.org/) 相容的 [Wayland](http://wayland.freedesktop.org/) compositor。
4閱讀 [FAQ](https://github.com/swaywm/sway/wiki)。 加入 [IRC 4閱讀 [FAQ](https://github.com/swaywm/sway/wiki)。 加入 [IRC
5é »é“](https://web.libera.chat/?channels=#sway) (#sway on 5é »é“](https://web.libera.chat/gamja/?channels=#sway) (#sway on
6irc.libera.chat) 6irc.libera.chat)
7 7
8## 發行簽章 8## 發行簽章
@@ -25,10 +25,10 @@ Sway 在許多發行版都有æä¾›ã€‚è«‹è‡ªå·±å˜—è©¦æ–¼ä½ çš„ç™¼è¡Œç‰ˆå®‰è£ ã€
25相ä¾å¥—件: 25相ä¾å¥—件:
26 26
27* meson \* 27* meson \*
28* [wlroots](https://github.com/swaywm/wlroots) 28* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)
29* wayland 29* wayland
30* wayland-protocols \* 30* wayland-protocols \*
31* pcre 31* pcre2
32* json-c 32* json-c
33* pango 33* pango
34* cairo 34* cairo
@@ -44,12 +44,6 @@ _\*編譯時相ä¾_
44 ninja -C build 44 ninja -C build
45 sudo ninja -C build install 45 sudo ninja -C build install
46 46
47在沒有 logind 的系統上,你需è¦ç‚º sway 的執行檔加上 suid。
48
49 sudo chmod a+s /usr/local/bin/sway
50
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
new file mode 100644
index 00000000..272aa837
--- /dev/null
+++ b/common/gesture.c
@@ -0,0 +1,332 @@
1#include "gesture.h"
2
3#include <math.h>
4#include <stdarg.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include "list.h"
9#include "log.h"
10#include "stringop.h"
11
12const uint8_t GESTURE_FINGERS_ANY = 0;
13
14char *gesture_parse(const char *input, struct gesture *output) {
15 // Clear output in case of failure
16 output->type = GESTURE_TYPE_NONE;
17 output->fingers = GESTURE_FINGERS_ANY;
18 output->directions = GESTURE_DIRECTION_NONE;
19
20 // Split input type, fingers and directions
21 list_t *split = split_string(input, ":");
22 if (split->length < 1 || split->length > 3) {
23 return format_str(
24 "expected <gesture>[:<fingers>][:direction], got %s",
25 input);
26 }
27
28 // Parse gesture type
29 if (strcmp(split->items[0], "hold") == 0) {
30 output->type = GESTURE_TYPE_HOLD;
31 } else if (strcmp(split->items[0], "pinch") == 0) {
32 output->type = GESTURE_TYPE_PINCH;
33 } else if (strcmp(split->items[0], "swipe") == 0) {
34 output->type = GESTURE_TYPE_SWIPE;
35 } else {
36 return format_str("expected hold|pinch|swipe, got %s",
37 (const char *)split->items[0]);
38 }
39
40 // Parse optional arguments
41 if (split->length > 1) {
42 char *next = split->items[1];
43
44 // Try to parse as finger count (1-9)
45 if (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') {
46 output->fingers = atoi(next);
47
48 // Move to next if available
49 next = split->length == 3 ? split->items[2] : NULL;
50 } else if (split->length == 3) {
51 // Fail here if argument can only be finger count
52 return format_str("expected 1-9, got %s", next);
53 }
54
55 // If there is an argument left, try to parse as direction
56 if (next) {
57 list_t *directions = split_string(next, "+");
58
59 for (int i = 0; i < directions->length; ++i) {
60 const char *item = directions->items[i];
61 if (strcmp(item, "any") == 0) {
62 continue;
63 } else if (strcmp(item, "up") == 0) {
64 output->directions |= GESTURE_DIRECTION_UP;
65 } else if (strcmp(item, "down") == 0) {
66 output->directions |= GESTURE_DIRECTION_DOWN;
67 } else if (strcmp(item, "left") == 0) {
68 output->directions |= GESTURE_DIRECTION_LEFT;
69 } else if (strcmp(item, "right") == 0) {
70 output->directions |= GESTURE_DIRECTION_RIGHT;
71 } else if (strcmp(item, "inward") == 0) {
72 output->directions |= GESTURE_DIRECTION_INWARD;
73 } else if (strcmp(item, "outward") == 0) {
74 output->directions |= GESTURE_DIRECTION_OUTWARD;
75 } else if (strcmp(item, "clockwise") == 0) {
76 output->directions |= GESTURE_DIRECTION_CLOCKWISE;
77 } else if (strcmp(item, "counterclockwise") == 0) {
78 output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
79 } else {
80 return format_str("expected direction, got %s", item);
81 }
82 }
83 list_free_items_and_destroy(directions);
84 }
85 } // if optional args
86
87 list_free_items_and_destroy(split);
88
89 return NULL;
90}
91
92const char *gesture_type_string(enum gesture_type type) {
93 switch (type) {
94 case GESTURE_TYPE_NONE:
95 return "none";
96 case GESTURE_TYPE_HOLD:
97 return "hold";
98 case GESTURE_TYPE_PINCH:
99 return "pinch";
100 case GESTURE_TYPE_SWIPE:
101 return "swipe";
102 }
103
104 return NULL;
105}
106
107const char *gesture_direction_string(enum gesture_direction direction) {
108 switch (direction) {
109 case GESTURE_DIRECTION_NONE:
110 return "none";
111 case GESTURE_DIRECTION_UP:
112 return "up";
113 case GESTURE_DIRECTION_DOWN:
114 return "down";
115 case GESTURE_DIRECTION_LEFT:
116 return "left";
117 case GESTURE_DIRECTION_RIGHT:
118 return "right";
119 case GESTURE_DIRECTION_INWARD:
120 return "inward";
121 case GESTURE_DIRECTION_OUTWARD:
122 return "outward";
123 case GESTURE_DIRECTION_CLOCKWISE:
124 return "clockwise";
125 case GESTURE_DIRECTION_COUNTERCLOCKWISE:
126 return "counterclockwise";
127 }
128
129 return NULL;
130}
131
132// Helper to turn combination of directions flags into string representation.
133static char *gesture_directions_to_string(uint32_t directions) {
134 char *result = NULL;
135
136 for (uint8_t bit = 0; bit < 32; bit++) {
137 uint32_t masked = directions & (1 << bit);
138 if (masked) {
139 const char *name = gesture_direction_string(masked);
140
141 if (!name) {
142 name = "unknown";
143 }
144
145 if (!result) {
146 result = strdup(name);
147 } else {
148 char *new = format_str("%s+%s", result, name);
149 free(result);
150 result = new;
151 }
152 }
153 }
154
155 if(!result) {
156 return strdup("any");
157 }
158
159 return result;
160}
161
162char *gesture_to_string(struct gesture *gesture) {
163 char *directions = gesture_directions_to_string(gesture->directions);
164 char *result = format_str("%s:%u:%s",
165 gesture_type_string(gesture->type),
166 gesture->fingers, directions);
167 free(directions);
168 return result;
169}
170
171bool gesture_check(struct gesture *target, enum gesture_type type, uint8_t fingers) {
172 // Check that gesture type matches
173 if (target->type != type) {
174 return false;
175 }
176
177 // Check that finger count matches
178 if (target->fingers != GESTURE_FINGERS_ANY && target->fingers != fingers) {
179 return false;
180 }
181
182 return true;
183}
184
185bool gesture_match(struct gesture *target, struct gesture *to_match, bool exact) {
186 // Check type and fingers
187 if (!gesture_check(target, to_match->type, to_match->fingers)) {
188 return false;
189 }
190
191 // Enforce exact matches ...
192 if (exact && target->directions != to_match->directions) {
193 return false;
194 }
195
196 // ... or ensure all target directions are matched
197 return (target->directions & to_match->directions) == target->directions;
198}
199
200bool gesture_equal(struct gesture *a, struct gesture *b) {
201 return a->type == b->type
202 && a->fingers == b->fingers
203 && a->directions == b->directions;
204}
205
206// Return count of set bits in directions bit field.
207static uint8_t gesture_directions_count(uint32_t directions) {
208 uint8_t count = 0;
209 for (; directions; directions >>= 1) {
210 count += directions & 1;
211 }
212 return count;
213}
214
215// Compare direction bit count of two direction.
216static int8_t gesture_directions_compare(uint32_t a, uint32_t b) {
217 return gesture_directions_count(a) - gesture_directions_count(b);
218}
219
220// Compare two direction based on their direction bit count
221int8_t gesture_compare(struct gesture *a, struct gesture *b) {
222 return gesture_directions_compare(a->directions, b->directions);
223}
224
225void gesture_tracker_begin(struct gesture_tracker *tracker,
226 enum gesture_type type, uint8_t fingers) {
227 tracker->type = type;
228 tracker->fingers = fingers;
229
230 tracker->dx = 0.0;
231 tracker->dy = 0.0;
232 tracker->scale = 1.0;
233 tracker->rotation = 0.0;
234
235 sway_log(SWAY_DEBUG, "begin tracking %s:%u:? gesture",
236 gesture_type_string(type), fingers);
237}
238
239bool gesture_tracker_check(struct gesture_tracker *tracker, enum gesture_type type) {
240 return tracker->type == type;
241}
242
243void gesture_tracker_update(struct gesture_tracker *tracker,
244 double dx, double dy, double scale, double rotation) {
245 if (tracker->type == GESTURE_TYPE_HOLD) {
246 sway_assert(false, "hold does not update.");
247 return;
248 }
249
250 tracker->dx += dx;
251 tracker->dy += dy;
252
253 if (tracker->type == GESTURE_TYPE_PINCH) {
254 tracker->scale = scale;
255 tracker->rotation += rotation;
256 }
257
258 sway_log(SWAY_DEBUG, "update tracking %s:%u:? gesture: %f %f %f %f",
259 gesture_type_string(tracker->type),
260 tracker->fingers,
261 tracker->dx, tracker->dy,
262 tracker->scale, tracker->rotation);
263}
264
265void gesture_tracker_cancel(struct gesture_tracker *tracker) {
266 sway_log(SWAY_DEBUG, "cancel tracking %s:%u:? gesture",
267 gesture_type_string(tracker->type), tracker->fingers);
268
269 tracker->type = GESTURE_TYPE_NONE;
270}
271
272struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
273 struct gesture *result = calloc(1, sizeof(struct gesture));
274
275 // Ignore gesture under some threshold
276 // TODO: Make configurable
277 const double min_rotation = 5;
278 const double min_scale_delta = 0.1;
279
280 // Determine direction
281 switch(tracker->type) {
282 // Gestures with scale and rotation
283 case GESTURE_TYPE_PINCH:
284 if (tracker->rotation > min_rotation) {
285 result->directions |= GESTURE_DIRECTION_CLOCKWISE;
286 }
287 if (tracker->rotation < -min_rotation) {
288 result->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
289 }
290
291 if (tracker->scale > (1.0 + min_scale_delta)) {
292 result->directions |= GESTURE_DIRECTION_OUTWARD;
293 }
294 if (tracker->scale < (1.0 - min_scale_delta)) {
295 result->directions |= GESTURE_DIRECTION_INWARD;
296 }
297 __attribute__ ((fallthrough));
298 // Gestures with dx and dy
299 case GESTURE_TYPE_SWIPE:
300 if (fabs(tracker->dx) > fabs(tracker->dy)) {
301 if (tracker->dx > 0) {
302 result->directions |= GESTURE_DIRECTION_RIGHT;
303 } else {
304 result->directions |= GESTURE_DIRECTION_LEFT;
305 }
306 } else {
307 if (tracker->dy > 0) {
308 result->directions |= GESTURE_DIRECTION_DOWN;
309 } else {
310 result->directions |= GESTURE_DIRECTION_UP;
311 }
312 }
313 // Gesture without any direction
314 case GESTURE_TYPE_HOLD:
315 break;
316 // Not tracking any gesture
317 case GESTURE_TYPE_NONE:
318 sway_assert(false, "Not tracking any gesture.");
319 return result;
320 }
321
322 result->type = tracker->type;
323 result->fingers = tracker->fingers;
324
325 char *description = gesture_to_string(result);
326 sway_log(SWAY_DEBUG, "end tracking gesture: %s", description);
327 free(description);
328
329 tracker->type = GESTURE_TYPE_NONE;
330
331 return result;
332}
diff --git a/common/ipc-client.c b/common/ipc-client.c
index d30212d2..a0be2b2d 100644
--- a/common/ipc-client.c
+++ b/common/ipc-client.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <stdint.h> 2#include <stdint.h>
4#include <stdlib.h> 3#include <stdlib.h>
diff --git a/common/log.c b/common/log.c
index 483420e7..3eacdb34 100644
--- a/common/log.c
+++ b/common/log.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200112L
2#include <signal.h> 1#include <signal.h>
3#include <stdarg.h> 2#include <stdarg.h>
4#include <stdio.h> 3#include <stdio.h>
diff --git a/common/loop.c b/common/loop.c
index 80fe18ea..b99c6d55 100644
--- a/common/loop.c
+++ b/common/loop.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200112L
2#include <limits.h> 1#include <limits.h>
3#include <string.h> 2#include <string.h>
4#include <stdbool.h> 3#include <stdbool.h>
diff --git a/common/meson.build b/common/meson.build
index c653dd72..c0ce1f68 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -1,8 +1,8 @@
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',
5 'gesture.c',
6 'ipc-client.c', 6 'ipc-client.c',
7 'log.c', 7 'log.c',
8 'loop.c', 8 'loop.c',
@@ -13,7 +13,6 @@ lib_sway_common = static_library(
13 ), 13 ),
14 dependencies: [ 14 dependencies: [
15 cairo, 15 cairo,
16 gdk_pixbuf,
17 pango, 16 pango,
18 pangocairo, 17 pangocairo,
19 wayland_client.partial_dependency(compile_args: true) 18 wayland_client.partial_dependency(compile_args: true)
diff --git a/common/pango.c b/common/pango.c
index dbc369dc..288569b3 100644
--- a/common/pango.c
+++ b/common/pango.c
@@ -50,7 +50,7 @@ size_t escape_markup_text(const char *src, char *dest) {
50 return length; 50 return length;
51} 51}
52 52
53PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, 53PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc,
54 const char *text, double scale, bool markup) { 54 const char *text, double scale, bool markup) {
55 PangoLayout *layout = pango_cairo_create_layout(cairo); 55 PangoLayout *layout = pango_cairo_create_layout(cairo);
56 PangoAttrList *attrs; 56 PangoAttrList *attrs;
@@ -73,60 +73,59 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
73 } 73 }
74 74
75 pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); 75 pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
76 PangoFontDescription *desc = pango_font_description_from_string(font);
77 pango_layout_set_font_description(layout, desc); 76 pango_layout_set_font_description(layout, desc);
78 pango_layout_set_single_paragraph_mode(layout, 1); 77 pango_layout_set_single_paragraph_mode(layout, 1);
79 pango_layout_set_attributes(layout, attrs); 78 pango_layout_set_attributes(layout, attrs);
80 pango_attr_list_unref(attrs); 79 pango_attr_list_unref(attrs);
81 pango_font_description_free(desc);
82 return layout; 80 return layout;
83} 81}
84 82
85void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, 83void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height,
86 int *baseline, double scale, bool markup, const char *fmt, ...) { 84 int *baseline, double scale, bool markup, const char *fmt, ...) {
87 va_list args; 85 va_list args;
88 va_start(args, fmt); 86 va_start(args, fmt);
89 // Add one since vsnprintf excludes null terminator. 87 char *buf = vformat_str(fmt, args);
90 int length = vsnprintf(NULL, 0, fmt, args) + 1;
91 va_end(args); 88 va_end(args);
92
93 char *buf = malloc(length);
94 if (buf == NULL) { 89 if (buf == NULL) {
95 sway_log(SWAY_ERROR, "Failed to allocate memory");
96 return; 90 return;
97 } 91 }
98 va_start(args, fmt);
99 vsnprintf(buf, length, fmt, args);
100 va_end(args);
101 92
102 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); 93 PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
103 pango_cairo_update_layout(cairo, layout); 94 pango_cairo_update_layout(cairo, layout);
104 pango_layout_get_pixel_size(layout, width, height); 95 pango_layout_get_pixel_size(layout, width, height);
105 if (baseline) { 96 if (baseline) {
106 *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; 97 *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
107 } 98 }
108 g_object_unref(layout); 99 g_object_unref(layout);
100
109 free(buf); 101 free(buf);
110} 102}
111 103
112void pango_printf(cairo_t *cairo, const char *font, 104void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) {
105 cairo_t *cairo = cairo_create(NULL);
106 PangoContext *pango = pango_cairo_create_context(cairo);
107 // When passing NULL as a language, pango uses the current locale.
108 PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL);
109
110 *baseline = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
111 *height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
112
113 pango_font_metrics_unref(metrics);
114 g_object_unref(pango);
115 cairo_destroy(cairo);
116}
117
118void render_text(cairo_t *cairo, const PangoFontDescription *desc,
113 double scale, bool markup, const char *fmt, ...) { 119 double scale, bool markup, const char *fmt, ...) {
114 va_list args; 120 va_list args;
115 va_start(args, fmt); 121 va_start(args, fmt);
116 // Add one since vsnprintf excludes null terminator. 122 char *buf = vformat_str(fmt, args);
117 int length = vsnprintf(NULL, 0, fmt, args) + 1;
118 va_end(args); 123 va_end(args);
119
120 char *buf = malloc(length);
121 if (buf == NULL) { 124 if (buf == NULL) {
122 sway_log(SWAY_ERROR, "Failed to allocate memory");
123 return; 125 return;
124 } 126 }
125 va_start(args, fmt);
126 vsnprintf(buf, length, fmt, args);
127 va_end(args);
128 127
129 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); 128 PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
130 cairo_font_options_t *fo = cairo_font_options_create(); 129 cairo_font_options_t *fo = cairo_font_options_create();
131 cairo_get_font_options(cairo, fo); 130 cairo_get_font_options(cairo, fo);
132 pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); 131 pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo);
@@ -134,5 +133,6 @@ void pango_printf(cairo_t *cairo, const char *font,
134 pango_cairo_update_layout(cairo, layout); 133 pango_cairo_update_layout(cairo, layout);
135 pango_cairo_show_layout(cairo, layout); 134 pango_cairo_show_layout(cairo, layout);
136 g_object_unref(layout); 135 g_object_unref(layout);
136
137 free(buf); 137 free(buf);
138} 138}
diff --git a/common/stringop.c b/common/stringop.c
index 7fb3fe12..16d04917 100644
--- a/common/stringop.c
+++ b/common/stringop.c
@@ -1,5 +1,5 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
2#include <stdarg.h>
3#include <stdbool.h> 3#include <stdbool.h>
4#include <stdio.h> 4#include <stdio.h>
5#include <stdlib.h> 5#include <stdlib.h>
@@ -328,3 +328,35 @@ bool expand_path(char **path) {
328 wordfree(&p); 328 wordfree(&p);
329 return true; 329 return true;
330} 330}
331
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 199f3ee1..7c492bcb 100644
--- a/common/util.c
+++ b/common/util.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <fcntl.h> 2#include <fcntl.h>
4#include <math.h> 3#include <math.h>
@@ -80,6 +79,12 @@ enum movement_unit parse_movement_unit(const char *unit) {
80 79
81int parse_movement_amount(int argc, char **argv, 80int parse_movement_amount(int argc, char **argv,
82 struct movement_amount *amount) { 81 struct movement_amount *amount) {
82 if (!sway_assert(argc > 0, "Expected args in parse_movement_amount")) {
83 amount->amount = 0;
84 amount->unit = MOVEMENT_UNIT_INVALID;
85 return 0;
86 }
87
83 char *err; 88 char *err;
84 amount->amount = (int)strtol(argv[0], &err, 10); 89 amount->amount = (int)strtol(argv[0], &err, 10);
85 if (*err) { 90 if (*err) {
diff --git a/completions/bash/sway b/completions/bash/sway
index edd752cd..01b20073 100644
--- a/completions/bash/sway
+++ b/completions/bash/sway
@@ -2,7 +2,7 @@
2 2
3_sway() 3_sway()
4{ 4{
5 local cur prev 5 local cur prev short long
6 _get_comp_words_by_ref cur prev 6 _get_comp_words_by_ref cur prev
7 7
8 short=( 8 short=(
diff --git a/completions/bash/swaybar b/completions/bash/swaybar
index 1e085c65..3709d4f9 100644
--- a/completions/bash/swaybar
+++ b/completions/bash/swaybar
@@ -2,7 +2,7 @@
2 2
3_swaybar() 3_swaybar()
4{ 4{
5 local cur prev 5 local cur prev short long
6 _get_comp_words_by_ref cur prev 6 _get_comp_words_by_ref cur prev
7 7
8 short=( 8 short=(
diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg
index f865e4e1..30457751 100644
--- a/completions/bash/swaymsg
+++ b/completions/bash/swaymsg
@@ -2,7 +2,7 @@
2 2
3_swaymsg() 3_swaymsg()
4{ 4{
5 local cur prev 5 local cur prev types short long
6 _get_comp_words_by_ref cur prev 6 _get_comp_words_by_ref cur prev
7 7
8 types=( 8 types=(
diff --git a/completions/meson.build b/completions/meson.build
new file mode 100644
index 00000000..6bca9391
--- /dev/null
+++ b/completions/meson.build
@@ -0,0 +1,57 @@
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 8031523e..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#
@@ -37,7 +37,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
37# 37#
38# exec swayidle -w \ 38# exec swayidle -w \
39# timeout 300 'swaylock -f -c 000000' \ 39# timeout 300 'swaylock -f -c 000000' \
40# timeout 600 'swaymsg "output * dpms off"' resume 'swaymsg "output * dpms on"' \ 40# timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \
41# before-sleep 'swaylock -f -c 000000' 41# before-sleep 'swaylock -f -c 000000'
42# 42#
43# This will lock your screen after 300 seconds of inactivity, then turn off 43# This will lock your screen after 300 seconds of inactivity, then turn off
@@ -82,7 +82,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
82 bindsym $mod+Shift+c reload 82 bindsym $mod+Shift+c reload
83 83
84 # Exit sway (logs you out of your Wayland session) 84 # Exit sway (logs you out of your Wayland session)
85 bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit' 85 bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit'
86# 86#
87# Moving around: 87# Moving around:
88# 88#
@@ -205,7 +205,7 @@ bar {
205 205
206 # When the status_command prints a new line to stdout, swaybar updates. 206 # When the status_command prints a new line to stdout, swaybar updates.
207 # The default just shows the current date and time. 207 # The default just shows the current date and time.
208 status_command while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done 208 status_command while date +'%Y-%m-%d %X'; do sleep 1; done
209 209
210 colors { 210 colors {
211 statusline #ffffff 211 statusline #ffffff
diff --git a/contrib/autoname-workspaces.py b/contrib/autoname-workspaces.py
deleted file mode 100755
index 3ec39928..00000000
--- a/contrib/autoname-workspaces.py
+++ /dev/null
@@ -1,124 +0,0 @@
1#!/usr/bin/python
2
3# This script requires i3ipc-python package (install it from a system package manager
4# or pip).
5# It adds icons to the workspace name for each open window.
6# Set your keybindings like this: set $workspace1 workspace number 1
7# Add your icons to WINDOW_ICONS.
8# Based on https://github.com/maximbaz/dotfiles/blob/master/bin/i3-autoname-workspaces
9
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 4ce31f29..00000000
--- a/contrib/grimshot
+++ /dev/null
@@ -1,154 +0,0 @@
1#!/bin/sh
2
3## Grimshot: a helper for screenshots within sway
4## Requirements:
5## - `grim`: screenshot utility for wayland
6## - `slurp`: to select an area
7## - `swaymsg`: to read properties of current window
8## - `wl-copy`: clipboard utility
9## - `jq`: json utility to parse swaymsg output
10## - `notify-send`: to show notifications
11## Those are needed to be installed, if unsure, run `grimshot check`
12##
13## See `man 1 grimshot` or `grimshot usage` for further details.
14
15getTargetDirectory() {
16 test -f ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs && \
17 . ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs
18
19 echo ${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}}
20}
21
22if [ "$1" = "--notify" ]; then
23 NOTIFY=yes
24 shift 1
25else
26 NOTIFY=no
27fi
28
29ACTION=${1:-usage}
30SUBJECT=${2:-screen}
31FILE=${3:-$(getTargetDirectory)/$(date -Ins).png}
32
33if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then
34 echo "Usage:"
35 echo " grimshot [--notify] (copy|save) [active|screen|output|area|window] [FILE|-]"
36 echo " grimshot check"
37 echo " grimshot usage"
38 echo ""
39 echo "Commands:"
40 echo " copy: Copy the screenshot data into the clipboard."
41 echo " save: Save the screenshot to a regular file or '-' to pipe to STDOUT."
42 echo " check: Verify if required tools are installed and exit."
43 echo " usage: Show this message and exit."
44 echo ""
45 echo "Targets:"
46 echo " active: Currently active window."
47 echo " screen: All visible outputs."
48 echo " output: Currently active output."
49 echo " area: Manually select a region."
50 echo " window: Manually select a window."
51 exit
52fi
53
54notify() {
55 notify-send -t 3000 -a grimshot "$@"
56}
57notifyOk() {
58 [ "$NOTIFY" = "no" ] && return
59
60 TITLE=${2:-"Screenshot"}
61 MESSAGE=${1:-"OK"}
62 notify "$TITLE" "$MESSAGE"
63}
64notifyError() {
65 if [ $NOTIFY = "yes" ]; then
66 TITLE=${2:-"Screenshot"}
67 MESSAGE=${1:-"Error taking screenshot with grim"}
68 notify -u critical "$TITLE" "$MESSAGE"
69 else
70 echo $1
71 fi
72}
73
74die() {
75 MSG=${1:-Bye}
76 notifyError "Error: $MSG"
77 exit 2
78}
79
80check() {
81 COMMAND=$1
82 if command -v "$COMMAND" > /dev/null 2>&1; then
83 RESULT="OK"
84 else
85 RESULT="NOT FOUND"
86 fi
87 echo " $COMMAND: $RESULT"
88}
89
90takeScreenshot() {
91 FILE=$1
92 GEOM=$2
93 OUTPUT=$3
94 if [ ! -z "$OUTPUT" ]; then
95 grim -o "$OUTPUT" "$FILE" || die "Unable to invoke grim"
96 elif [ -z "$GEOM" ]; then
97 grim "$FILE" || die "Unable to invoke grim"
98 else
99 grim -g "$GEOM" "$FILE" || die "Unable to invoke grim"
100 fi
101}
102
103if [ "$ACTION" = "check" ] ; then
104 echo "Checking if required tools are installed. If something is missing, install it to your system and make it available in PATH..."
105 check grim
106 check slurp
107 check swaymsg
108 check wl-copy
109 check jq
110 check notify-send
111 exit
112elif [ "$SUBJECT" = "area" ] ; then
113 GEOM=$(slurp -d)
114 # Check if user exited slurp without selecting the area
115 if [ -z "$GEOM" ]; then
116 exit 1
117 fi
118 WHAT="Area"
119elif [ "$SUBJECT" = "active" ] ; then
120 FOCUSED=$(swaymsg -t get_tree | jq -r 'recurse(.nodes[]?, .floating_nodes[]?) | select(.focused)')
121 GEOM=$(echo "$FOCUSED" | jq -r '.rect | "\(.x),\(.y) \(.width)x\(.height)"')
122 APP_ID=$(echo "$FOCUSED" | jq -r '.app_id')
123 WHAT="$APP_ID window"
124elif [ "$SUBJECT" = "screen" ] ; then
125 GEOM=""
126 WHAT="Screen"
127elif [ "$SUBJECT" = "output" ] ; then
128 GEOM=""
129 OUTPUT=$(swaymsg -t get_outputs | jq -r '.[] | select(.focused)' | jq -r '.name')
130 WHAT="$OUTPUT"
131elif [ "$SUBJECT" = "window" ] ; then
132 GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp)
133 # Check if user exited slurp without selecting the area
134 if [ -z "$GEOM" ]; then
135 exit 1
136 fi
137 WHAT="Window"
138else
139 die "Unknown subject to take a screen shot from" "$SUBJECT"
140fi
141
142if [ "$ACTION" = "copy" ] ; then
143 takeScreenshot - "$GEOM" "$OUTPUT" | wl-copy --type image/png || die "Clipboard error"
144 notifyOk "$WHAT copied to buffer"
145else
146 if takeScreenshot "$FILE" "$GEOM" "$OUTPUT"; then
147 TITLE="Screenshot of $SUBJECT"
148 MESSAGE=$(basename "$FILE")
149 notifyOk "$MESSAGE" "$TITLE"
150 echo $FILE
151 else
152 notifyError "Error taking screenshot with grim"
153 fi
154fi
diff --git a/contrib/grimshot.1 b/contrib/grimshot.1
deleted file mode 100644
index e4baccfd..00000000
--- a/contrib/grimshot.1
+++ /dev/null
@@ -1,104 +0,0 @@
1.\" Generated by scdoc 1.11.1
2.\" Complete documentation for this program is not available as a GNU info page
3.ie \n(.g .ds Aq \(aq
4.el .ds Aq '
5.nh
6.ad l
7.\" Begin generated content:
8.TH "grimshot" "1" "2021-02-23"
9.P
10.SH NAME
11.P
12grimshot - a helper for screenshots within sway
13.P
14.SH SYNOPSIS
15.P
16\fBgrimshot\fR [--notify] (copy|save) [TARGET] [FILE]
17.br
18\fBgrimshot\fR check
19.br
20\fBgrimshot\fR usage
21.P
22.SH OPTIONS
23.P
24\fB--notify\fR
25.RS 4
26Show notifications to the user that a screenshot has been taken.\&
27.P
28.RE
29\fBsave\fR
30.RS 4
31Save the screenshot into a regular file.\& Grimshot will write images
32files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined
33in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\&
34Set FILE to '-' to pipe the output to STDOUT.\&
35.P
36.RE
37\fBcopy\fR
38.RS 4
39Copy the screenshot data (as image/png) into the clipboard.\&
40.P
41.RE
42.SH DESCRIPTION
43.P
44Grimshot is an easy-to-use screenshot utility for sway.\& It provides a
45convenient interface over grim, slurp and jq, and supports storing the
46screenshot either directly to the clipboard using wl-copy or to a file.\&
47.P
48.SH EXAMPLES
49.P
50An example usage pattern is to add these bindings to your sway config:
51.P
52.nf
53.RS 4
54# Screenshots:
55# Super+P: Current window
56# Super+Shift+p: Select area
57# Super+Alt+p Current output
58# Super+Ctrl+p Select a window
59
60bindsym Mod4+p exec grimshot save active
61bindsym Mod4+Shift+p exec grimshot save area
62bindsym Mod4+Mod1+p exec grimshot save output
63bindsym Mod4+Ctrl+p exec grimshot save window
64.fi
65.RE
66.P
67.SH TARGETS
68.P
69grimshot can capture the following named targets:
70.P
71\fIactive\fR
72.RS 4
73Captures the currently active window.\&
74.P
75.RE
76\fIscreen\fR
77.RS 4
78Captures the entire screen.\& This includes all visible outputs.\&
79.P
80.RE
81\fIarea\fR
82.RS 4
83Allows manually selecting a rectangular region, and captures that.\&
84.P
85.RE
86\fIwindow\fR
87.RS 4
88Allows manually selecting a single window (by clicking on it), and
89captures it.\&
90.P
91.RE
92\fIoutput\fR
93.RS 4
94Captures the currently active output.\&
95.P
96.RE
97.SH OUTPUT
98.P
99Grimshot will print the filename of the captured screenshot to stdout if called
100with the \fIsave\fR subcommand.\&
101.P
102.SH SEE ALSO
103.P
104\fBgrim\fR(1)
diff --git a/contrib/grimshot.1.scd b/contrib/grimshot.1.scd
deleted file mode 100644
index d2a57759..00000000
--- a/contrib/grimshot.1.scd
+++ /dev/null
@@ -1,77 +0,0 @@
1grimshot(1)
2
3# NAME
4
5grimshot - a helper for screenshots within sway
6
7# SYNOPSIS
8
9*grimshot* [--notify] (copy|save) [TARGET] [FILE]++
10*grimshot* check++
11*grimshot* usage
12
13# OPTIONS
14
15*--notify*
16 Show notifications to the user that a screenshot has been taken.
17
18*save*
19 Save the screenshot into a regular file. Grimshot will write images
20 files to *XDG_SCREENSHOTS_DIR* if this is set (or defined
21 in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*.
22 Set FILE to '-' to pipe the output to STDOUT.
23
24*copy*
25 Copy the screenshot data (as image/png) into the clipboard.
26
27# DESCRIPTION
28
29Grimshot is an easy-to-use screenshot utility for sway. It provides a
30convenient interface over grim, slurp and jq, and supports storing the
31screenshot either directly to the clipboard using wl-copy or to a file.
32
33# EXAMPLES
34
35An example usage pattern is to add these bindings to your sway config:
36
37```
38# Screenshots:
39# Super+P: Current window
40# Super+Shift+p: Select area
41# Super+Alt+p Current output
42# Super+Ctrl+p Select a window
43
44bindsym Mod4+p exec grimshot save active
45bindsym Mod4+Shift+p exec grimshot save area
46bindsym Mod4+Mod1+p exec grimshot save output
47bindsym Mod4+Ctrl+p exec grimshot save window
48```
49
50# TARGETS
51
52grimshot can capture the following named targets:
53
54_active_
55 Captures the currently active window.
56
57_screen_
58 Captures the entire screen. This includes all visible outputs.
59
60_area_
61 Allows manually selecting a rectangular region, and captures that.
62
63_window_
64 Allows manually selecting a single window (by clicking on it), and
65 captures it.
66
67_output_
68 Captures the currently active output.
69
70# OUTPUT
71
72Grimshot will print the filename of the captured screenshot to stdout if called
73with the _save_ subcommand.
74
75# SEE ALSO
76
77*grim*(1)
diff --git a/contrib/inactive-windows-transparency.py b/contrib/inactive-windows-transparency.py
deleted file mode 100755
index b81134dd..00000000
--- a/contrib/inactive-windows-transparency.py
+++ /dev/null
@@ -1,69 +0,0 @@
1#!/usr/bin/python
2
3# This script requires i3ipc-python package (install it from a system package manager
4# or pip).
5# It makes inactive windows transparent. Use `transparency_val` variable to control
6# transparency strength in range of 0…1 or use the command line argument -o.
7
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/gesture.h b/include/gesture.h
new file mode 100644
index 00000000..9c6b0f91
--- /dev/null
+++ b/include/gesture.h
@@ -0,0 +1,104 @@
1#ifndef _SWAY_GESTURE_H
2#define _SWAY_GESTURE_H
3
4#include <stdbool.h>
5#include <stdint.h>
6
7/**
8 * A gesture type used in binding.
9 */
10enum gesture_type {
11 GESTURE_TYPE_NONE = 0,
12 GESTURE_TYPE_HOLD,
13 GESTURE_TYPE_PINCH,
14 GESTURE_TYPE_SWIPE,
15};
16
17// Turns single type enum value to constant string representation.
18const char *gesture_type_string(enum gesture_type direction);
19
20// Value to use to accept any finger count
21extern const uint8_t GESTURE_FINGERS_ANY;
22
23/**
24 * A gesture direction used in binding.
25 */
26enum gesture_direction {
27 GESTURE_DIRECTION_NONE = 0,
28 // Directions based on delta x and y
29 GESTURE_DIRECTION_UP = 1 << 0,
30 GESTURE_DIRECTION_DOWN = 1 << 1,
31 GESTURE_DIRECTION_LEFT = 1 << 2,
32 GESTURE_DIRECTION_RIGHT = 1 << 3,
33 // Directions based on scale
34 GESTURE_DIRECTION_INWARD = 1 << 4,
35 GESTURE_DIRECTION_OUTWARD = 1 << 5,
36 // Directions based on rotation
37 GESTURE_DIRECTION_CLOCKWISE = 1 << 6,
38 GESTURE_DIRECTION_COUNTERCLOCKWISE = 1 << 7,
39};
40
41// Turns single direction enum value to constant string representation.
42const char *gesture_direction_string(enum gesture_direction direction);
43
44/**
45 * Struct representing a pointer gesture
46 */
47struct gesture {
48 enum gesture_type type;
49 uint8_t fingers;
50 uint32_t directions;
51};
52
53/**
54 * Parses gesture from <gesture>[:<fingers>][:<directions>] string.
55 *
56 * Return NULL on success, otherwise error message string
57 */
58char *gesture_parse(const char *input, struct gesture *output);
59
60// Turns gesture into string representation
61char *gesture_to_string(struct gesture *gesture);
62
63// Check if gesture is of certain type and finger count.
64bool gesture_check(struct gesture *target,
65 enum gesture_type type, uint8_t fingers);
66
67// Check if a gesture target/binding is match by other gesture/input
68bool gesture_match(struct gesture *target,
69 struct gesture *to_match, bool exact);
70
71// Returns true if gesture are exactly the same
72bool gesture_equal(struct gesture *a, struct gesture *b);
73
74// Compare distance between two matched target gestures.
75int8_t gesture_compare(struct gesture *a, struct gesture *b);
76
77// Small helper struct to track gestures over time
78struct gesture_tracker {
79 enum gesture_type type;
80 uint8_t fingers;
81 double dx, dy;
82 double scale;
83 double rotation;
84};
85
86// Begin gesture tracking
87void gesture_tracker_begin(struct gesture_tracker *tracker,
88 enum gesture_type type, uint8_t fingers);
89
90// Check if the provides type is currently being tracked
91bool gesture_tracker_check(struct gesture_tracker *tracker,
92 enum gesture_type type);
93
94// Update gesture track with new data point
95void gesture_tracker_update(struct gesture_tracker *tracker, double dx,
96 double dy, double scale, double rotation);
97
98// Reset tracker
99void gesture_tracker_cancel(struct gesture_tracker *tracker);
100
101// Reset tracker and return gesture tracked
102struct gesture *gesture_tracker_end(struct gesture_tracker *tracker);
103
104#endif
diff --git a/include/ipc-client.h b/include/ipc-client.h
index d3895023..9c5712d7 100644
--- a/include/ipc-client.h
+++ b/include/ipc-client.h
@@ -1,6 +1,9 @@
1#ifndef _SWAY_IPC_CLIENT_H 1#ifndef _SWAY_IPC_CLIENT_H
2#define _SWAY_IPC_CLIENT_H 2#define _SWAY_IPC_CLIENT_H
3 3
4// arbitrary number, it's probably sufficient, higher number = more memory usage
5#define JSON_MAX_DEPTH 512
6
4#include <stdbool.h> 7#include <stdbool.h>
5#include <stdint.h> 8#include <stdint.h>
6#include <sys/time.h> 9#include <sys/time.h>
diff --git a/include/pango.h b/include/pango.h
index 75dbba27..228e39cf 100644
--- a/include/pango.h
+++ b/include/pango.h
@@ -5,6 +5,7 @@
5#include <stdint.h> 5#include <stdint.h>
6#include <cairo.h> 6#include <cairo.h>
7#include <pango/pangocairo.h> 7#include <pango/pangocairo.h>
8#include "stringop.h"
8 9
9/** 10/**
10 * Utility function which escape characters a & < > ' ". 11 * Utility function which escape characters a & < > ' ".
@@ -13,11 +14,12 @@
13 * escaped string to dest if provided. 14 * escaped string to dest if provided.
14 */ 15 */
15size_t escape_markup_text(const char *src, char *dest); 16size_t escape_markup_text(const char *src, char *dest);
16PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, 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 char *font, 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 pango_printf(cairo_t *cairo, const char *font, 21void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline);
21 double scale, bool markup, const char *fmt, ...); 22void render_text(cairo_t *cairo, PangoFontDescription *desc,
23 double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(5, 6);
22 24
23#endif 25#endif
diff --git a/include/stringop.h b/include/stringop.h
index 8d7089e9..19a50f23 100644
--- a/include/stringop.h
+++ b/include/stringop.h
@@ -2,8 +2,15 @@
2#define _SWAY_STRINGOP_H 2#define _SWAY_STRINGOP_H
3 3
4#include <stdbool.h> 4#include <stdbool.h>
5#include <stddef.h>
5#include "list.h" 6#include "list.h"
6 7
8#ifdef __GNUC__
9#define _SWAY_ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end)))
10#else
11#define _SWAY_ATTRIB_PRINTF(start, end)
12#endif
13
7void strip_whitespace(char *str); 14void strip_whitespace(char *str);
8void strip_quotes(char *str); 15void strip_quotes(char *str);
9 16
@@ -30,4 +37,7 @@ char *argsep(char **stringp, const char *delim, char *matched_delim);
30// Expand a path using shell replacements such as $HOME and ~ 37// Expand a path using shell replacements such as $HOME and ~
31bool expand_path(char **path); 38bool expand_path(char **path);
32 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
33#endif 43#endif
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 29a6bec3..27058587 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -3,13 +3,14 @@
3 3
4#include <wlr/util/edges.h> 4#include <wlr/util/edges.h>
5#include "config.h" 5#include "config.h"
6#include "stringop.h"
6 7
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 */
@@ -106,12 +107,14 @@ sway_cmd cmd_exec_process;
106sway_cmd cmd_assign; 107sway_cmd cmd_assign;
107sway_cmd cmd_bar; 108sway_cmd cmd_bar;
108sway_cmd cmd_bindcode; 109sway_cmd cmd_bindcode;
110sway_cmd cmd_bindgesture;
109sway_cmd cmd_bindswitch; 111sway_cmd cmd_bindswitch;
110sway_cmd cmd_bindsym; 112sway_cmd cmd_bindsym;
111sway_cmd cmd_border; 113sway_cmd cmd_border;
112sway_cmd cmd_client_noop; 114sway_cmd cmd_client_noop;
113sway_cmd cmd_client_focused; 115sway_cmd cmd_client_focused;
114sway_cmd cmd_client_focused_inactive; 116sway_cmd cmd_client_focused_inactive;
117sway_cmd cmd_client_focused_tab_title;
115sway_cmd cmd_client_unfocused; 118sway_cmd cmd_client_unfocused;
116sway_cmd cmd_client_urgent; 119sway_cmd cmd_client_urgent;
117sway_cmd cmd_client_placeholder; 120sway_cmd cmd_client_placeholder;
@@ -157,12 +160,11 @@ sway_cmd cmd_new_float;
157sway_cmd cmd_new_window; 160sway_cmd cmd_new_window;
158sway_cmd cmd_nop; 161sway_cmd cmd_nop;
159sway_cmd cmd_opacity; 162sway_cmd cmd_opacity;
160sway_cmd cmd_new_float;
161sway_cmd cmd_new_window;
162sway_cmd cmd_no_focus; 163sway_cmd cmd_no_focus;
163sway_cmd cmd_output; 164sway_cmd cmd_output;
164sway_cmd cmd_permit; 165sway_cmd cmd_permit;
165sway_cmd cmd_popup_during_fullscreen; 166sway_cmd cmd_popup_during_fullscreen;
167sway_cmd cmd_primary_selection;
166sway_cmd cmd_reject; 168sway_cmd cmd_reject;
167sway_cmd cmd_reload; 169sway_cmd cmd_reload;
168sway_cmd cmd_rename; 170sway_cmd cmd_rename;
@@ -190,6 +192,7 @@ sway_cmd cmd_titlebar_border_thickness;
190sway_cmd cmd_titlebar_padding; 192sway_cmd cmd_titlebar_padding;
191sway_cmd cmd_unbindcode; 193sway_cmd cmd_unbindcode;
192sway_cmd cmd_unbindswitch; 194sway_cmd cmd_unbindswitch;
195sway_cmd cmd_unbindgesture;
193sway_cmd cmd_unbindsym; 196sway_cmd cmd_unbindsym;
194sway_cmd cmd_unmark; 197sway_cmd cmd_unmark;
195sway_cmd cmd_urgent; 198sway_cmd cmd_urgent;
@@ -249,6 +252,7 @@ sway_cmd input_cmd_click_method;
249sway_cmd input_cmd_drag; 252sway_cmd input_cmd_drag;
250sway_cmd input_cmd_drag_lock; 253sway_cmd input_cmd_drag_lock;
251sway_cmd input_cmd_dwt; 254sway_cmd input_cmd_dwt;
255sway_cmd input_cmd_dwtp;
252sway_cmd input_cmd_events; 256sway_cmd input_cmd_events;
253sway_cmd input_cmd_left_handed; 257sway_cmd input_cmd_left_handed;
254sway_cmd input_cmd_map_from_region; 258sway_cmd input_cmd_map_from_region;
@@ -257,10 +261,12 @@ sway_cmd input_cmd_map_to_region;
257sway_cmd input_cmd_middle_emulation; 261sway_cmd input_cmd_middle_emulation;
258sway_cmd input_cmd_natural_scroll; 262sway_cmd input_cmd_natural_scroll;
259sway_cmd input_cmd_pointer_accel; 263sway_cmd input_cmd_pointer_accel;
264sway_cmd input_cmd_rotation_angle;
260sway_cmd input_cmd_scroll_factor; 265sway_cmd input_cmd_scroll_factor;
261sway_cmd input_cmd_repeat_delay; 266sway_cmd input_cmd_repeat_delay;
262sway_cmd input_cmd_repeat_rate; 267sway_cmd input_cmd_repeat_rate;
263sway_cmd input_cmd_scroll_button; 268sway_cmd input_cmd_scroll_button;
269sway_cmd input_cmd_scroll_button_lock;
264sway_cmd input_cmd_scroll_method; 270sway_cmd input_cmd_scroll_method;
265sway_cmd input_cmd_tap; 271sway_cmd input_cmd_tap;
266sway_cmd input_cmd_tap_button_map; 272sway_cmd input_cmd_tap_button_map;
@@ -282,12 +288,16 @@ sway_cmd output_cmd_dpms;
282sway_cmd output_cmd_enable; 288sway_cmd output_cmd_enable;
283sway_cmd output_cmd_max_render_time; 289sway_cmd output_cmd_max_render_time;
284sway_cmd output_cmd_mode; 290sway_cmd output_cmd_mode;
291sway_cmd output_cmd_modeline;
285sway_cmd output_cmd_position; 292sway_cmd output_cmd_position;
293sway_cmd output_cmd_power;
294sway_cmd output_cmd_render_bit_depth;
286sway_cmd output_cmd_scale; 295sway_cmd output_cmd_scale;
287sway_cmd output_cmd_scale_filter; 296sway_cmd output_cmd_scale_filter;
288sway_cmd output_cmd_subpixel; 297sway_cmd output_cmd_subpixel;
289sway_cmd output_cmd_toggle; 298sway_cmd output_cmd_toggle;
290sway_cmd output_cmd_transform; 299sway_cmd output_cmd_transform;
300sway_cmd output_cmd_unplug;
291 301
292sway_cmd seat_cmd_attach; 302sway_cmd seat_cmd_attach;
293sway_cmd seat_cmd_cursor; 303sway_cmd seat_cmd_cursor;
diff --git a/include/sway/config.h b/include/sway/config.h
index b8327e9c..0be1cd22 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -8,13 +8,17 @@
8#include <wlr/types/wlr_tablet_tool.h> 8#include <wlr/types/wlr_tablet_tool.h>
9#include <wlr/util/box.h> 9#include <wlr/util/box.h>
10#include <xkbcommon/xkbcommon.h> 10#include <xkbcommon/xkbcommon.h>
11#include <xf86drmMode.h>
11#include "../include/config.h" 12#include "../include/config.h"
13#include "gesture.h"
12#include "list.h" 14#include "list.h"
15#include "stringop.h"
13#include "swaynag.h" 16#include "swaynag.h"
14#include "tree/container.h" 17#include "tree/container.h"
15#include "sway/input/tablet.h" 18#include "sway/input/tablet.h"
16#include "sway/tree/root.h" 19#include "sway/tree/root.h"
17#include "wlr-layer-shell-unstable-v1-protocol.h" 20#include "wlr-layer-shell-unstable-v1-protocol.h"
21#include <pango/pangocairo.h>
18 22
19// TODO: Refactor this shit 23// TODO: Refactor this shit
20 24
@@ -31,7 +35,8 @@ enum binding_input_type {
31 BINDING_KEYSYM, 35 BINDING_KEYSYM,
32 BINDING_MOUSECODE, 36 BINDING_MOUSECODE,
33 BINDING_MOUSESYM, 37 BINDING_MOUSESYM,
34 BINDING_SWITCH 38 BINDING_SWITCH, // dummy, only used to call seat_execute_command
39 BINDING_GESTURE // dummy, only used to call seat_execute_command
35}; 40};
36 41
37enum binding_flags { 42enum binding_flags {
@@ -44,10 +49,11 @@ enum binding_flags {
44 BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload 49 BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload
45 BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor 50 BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor
46 BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key 51 BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key
52 BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match
47}; 53};
48 54
49/** 55/**
50 * A key binding and an associated command. 56 * A key (or mouse) binding and an associated command.
51 */ 57 */
52struct sway_binding { 58struct sway_binding {
53 enum binding_input_type type; 59 enum binding_input_type type;
@@ -61,12 +67,10 @@ struct sway_binding {
61 char *command; 67 char *command;
62}; 68};
63 69
64/** 70enum sway_switch_trigger {
65 * A mouse binding and an associated command. 71 SWAY_SWITCH_TRIGGER_OFF,
66 */ 72 SWAY_SWITCH_TRIGGER_ON,
67struct sway_mouse_binding { 73 SWAY_SWITCH_TRIGGER_TOGGLE,
68 uint32_t button;
69 char *command;
70}; 74};
71 75
72/** 76/**
@@ -74,8 +78,18 @@ struct sway_mouse_binding {
74 */ 78 */
75struct sway_switch_binding { 79struct sway_switch_binding {
76 enum wlr_switch_type type; 80 enum wlr_switch_type type;
77 enum wlr_switch_state state; 81 enum sway_switch_trigger trigger;
82 uint32_t flags;
83 char *command;
84};
85
86/**
87 * A gesture binding and an associated command.
88 */
89struct sway_gesture_binding {
90 char *input;
78 uint32_t flags; 91 uint32_t flags;
92 struct gesture gesture;
79 char *command; 93 char *command;
80}; 94};
81 95
@@ -98,6 +112,7 @@ struct sway_mode {
98 list_t *keycode_bindings; 112 list_t *keycode_bindings;
99 list_t *mouse_bindings; 113 list_t *mouse_bindings;
100 list_t *switch_bindings; 114 list_t *switch_bindings;
115 list_t *gesture_bindings;
101 bool pango; 116 bool pango;
102}; 117};
103 118
@@ -136,14 +151,17 @@ struct input_config {
136 int drag; 151 int drag;
137 int drag_lock; 152 int drag_lock;
138 int dwt; 153 int dwt;
154 int dwtp;
139 int left_handed; 155 int left_handed;
140 int middle_emulation; 156 int middle_emulation;
141 int natural_scroll; 157 int natural_scroll;
142 float pointer_accel; 158 float pointer_accel;
159 float rotation_angle;
143 float scroll_factor; 160 float scroll_factor;
144 int repeat_delay; 161 int repeat_delay;
145 int repeat_rate; 162 int repeat_rate;
146 int scroll_button; 163 int scroll_button;
164 int scroll_button_lock;
147 int scroll_method; 165 int scroll_method;
148 int send_events; 166 int send_events;
149 int tap; 167 int tap;
@@ -233,12 +251,6 @@ struct seat_config {
233 } xcursor_theme; 251 } xcursor_theme;
234}; 252};
235 253
236enum config_dpms {
237 DPMS_IGNORE,
238 DPMS_ON,
239 DPMS_OFF,
240};
241
242enum scale_filter_mode { 254enum scale_filter_mode {
243 SCALE_FILTER_DEFAULT, // the default is currently smart 255 SCALE_FILTER_DEFAULT, // the default is currently smart
244 SCALE_FILTER_LINEAR, 256 SCALE_FILTER_LINEAR,
@@ -246,6 +258,12 @@ enum scale_filter_mode {
246 SCALE_FILTER_SMART, 258 SCALE_FILTER_SMART,
247}; 259};
248 260
261enum render_bit_depth {
262 RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8
263 RENDER_BIT_DEPTH_8,
264 RENDER_BIT_DEPTH_10,
265};
266
249/** 267/**
250 * Size and position configuration for a particular output. 268 * Size and position configuration for a particular output.
251 * 269 *
@@ -254,9 +272,11 @@ enum scale_filter_mode {
254struct output_config { 272struct output_config {
255 char *name; 273 char *name;
256 int enabled; 274 int enabled;
275 int power;
257 int width, height; 276 int width, height;
258 float refresh_rate; 277 float refresh_rate;
259 int custom_mode; 278 int custom_mode;
279 drmModeModeInfo drm_mode;
260 int x, y; 280 int x, y;
261 float scale; 281 float scale;
262 enum scale_filter_mode scale_filter; 282 enum scale_filter_mode scale_filter;
@@ -264,11 +284,19 @@ struct output_config {
264 enum wl_output_subpixel subpixel; 284 enum wl_output_subpixel subpixel;
265 int max_render_time; // In milliseconds 285 int max_render_time; // In milliseconds
266 int adaptive_sync; 286 int adaptive_sync;
287 enum render_bit_depth render_bit_depth;
267 288
268 char *background; 289 char *background;
269 char *background_option; 290 char *background_option;
270 char *background_fallback; 291 char *background_fallback;
271 enum config_dpms dpms_state; 292};
293
294/**
295 * An output config pre-matched to an output
296 */
297struct matched_output_config {
298 struct sway_output *output;
299 struct output_config *config;
272}; 300};
273 301
274/** 302/**
@@ -281,6 +309,12 @@ struct side_gaps {
281 int left; 309 int left;
282}; 310};
283 311
312enum smart_gaps_mode {
313 SMART_GAPS_OFF,
314 SMART_GAPS_ON,
315 SMART_GAPS_INVERSE_OUTER,
316};
317
284/** 318/**
285 * Stores configuration for a workspace, regardless of whether the workspace 319 * Stores configuration for a workspace, regardless of whether the workspace
286 * exists. 320 * exists.
@@ -416,14 +450,6 @@ enum sway_popup_during_fullscreen {
416 POPUP_LEAVE, 450 POPUP_LEAVE,
417}; 451};
418 452
419enum command_context {
420 CONTEXT_CONFIG = 1 << 0,
421 CONTEXT_BINDING = 1 << 1,
422 CONTEXT_IPC = 1 << 2,
423 CONTEXT_CRITERIA = 1 << 3,
424 CONTEXT_ALL = 0xFFFFFFFF,
425};
426
427enum focus_follows_mouse_mode { 453enum focus_follows_mouse_mode {
428 FOLLOWS_NO, 454 FOLLOWS_NO,
429 FOLLOWS_YES, 455 FOLLOWS_YES,
@@ -485,9 +511,10 @@ struct sway_config {
485 char *floating_scroll_right_cmd; 511 char *floating_scroll_right_cmd;
486 enum sway_container_layout default_orientation; 512 enum sway_container_layout default_orientation;
487 enum sway_container_layout default_layout; 513 enum sway_container_layout default_layout;
488 char *font; 514 char *font; // Used for IPC.
489 size_t font_height; 515 PangoFontDescription *font_description; // Used internally for rendering and validating.
490 size_t font_baseline; 516 int font_height;
517 int font_baseline;
491 bool pango_markup; 518 bool pango_markup;
492 int titlebar_border_thickness; 519 int titlebar_border_thickness;
493 int titlebar_h_padding; 520 int titlebar_h_padding;
@@ -514,11 +541,12 @@ struct sway_config {
514 bool auto_back_and_forth; 541 bool auto_back_and_forth;
515 bool show_marks; 542 bool show_marks;
516 enum alignment title_align; 543 enum alignment title_align;
544 bool primary_selection;
517 545
518 bool tiling_drag; 546 bool tiling_drag;
519 int tiling_drag_threshold; 547 int tiling_drag_threshold;
520 548
521 bool smart_gaps; 549 enum smart_gaps_mode smart_gaps;
522 int gaps_inner; 550 int gaps_inner;
523 struct side_gaps gaps_outer; 551 struct side_gaps gaps_outer;
524 552
@@ -541,12 +569,15 @@ struct sway_config {
541 struct { 569 struct {
542 struct border_colors focused; 570 struct border_colors focused;
543 struct border_colors focused_inactive; 571 struct border_colors focused_inactive;
572 struct border_colors focused_tab_title;
544 struct border_colors unfocused; 573 struct border_colors unfocused;
545 struct border_colors urgent; 574 struct border_colors urgent;
546 struct border_colors placeholder; 575 struct border_colors placeholder;
547 float background[4]; 576 float background[4];
548 } border_colors; 577 } border_colors;
549 578
579 bool has_focused_tab_title;
580
550 // floating view 581 // floating view
551 int32_t floating_maximum_width; 582 int32_t floating_maximum_width;
552 int32_t floating_maximum_height; 583 int32_t floating_maximum_height;
@@ -604,7 +635,7 @@ void run_deferred_bindings(void);
604/** 635/**
605 * Adds a warning entry to the swaynag instance used for errors. 636 * Adds a warning entry to the swaynag instance used for errors.
606 */ 637 */
607void config_add_swaynag_warning(char *fmt, ...); 638void config_add_swaynag_warning(char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2);
608 639
609/** 640/**
610 * Free config struct 641 * Free config struct
@@ -657,20 +688,22 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt
657 688
658struct output_config *new_output_config(const char *name); 689struct output_config *new_output_config(const char *name);
659 690
660void merge_output_config(struct output_config *dst, struct output_config *src); 691bool apply_output_configs(struct matched_output_config *configs,
661 692 size_t configs_len, bool test_only);
662bool apply_output_config(struct output_config *oc, struct sway_output *output);
663 693
664bool test_output_config(struct output_config *oc, struct sway_output *output); 694void apply_all_output_configs(void);
665 695
666struct 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);
667 704
668struct output_config *find_output_config(struct sway_output *output); 705struct output_config *find_output_config(struct sway_output *output);
669 706
670void apply_output_config_to_outputs(struct output_config *oc);
671
672void reset_outputs(void);
673
674void free_output_config(struct output_config *oc); 707void free_output_config(struct output_config *oc);
675 708
676bool spawn_swaybg(void); 709bool spawn_swaybg(void);
@@ -681,6 +714,8 @@ void free_sway_binding(struct sway_binding *sb);
681 714
682void free_switch_binding(struct sway_switch_binding *binding); 715void free_switch_binding(struct sway_switch_binding *binding);
683 716
717void free_gesture_binding(struct sway_gesture_binding *binding);
718
684void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); 719void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);
685 720
686void load_swaybar(struct bar_config *bar); 721void load_swaybar(struct bar_config *bar);
@@ -696,14 +731,13 @@ void free_bar_binding(struct bar_binding *binding);
696void free_workspace_config(struct workspace_config *wsc); 731void free_workspace_config(struct workspace_config *wsc);
697 732
698/** 733/**
699 * Updates the value of config->font_height based on the max title height 734 * Updates the value of config->font_height based on the metrics for title's
700 * reported by each container. If recalculate is true, the containers will 735 * font as reported by pango.
701 * recalculate their heights before reporting.
702 * 736 *
703 * If the height has changed, all containers will be rearranged to take on the 737 * If the height has changed, all containers will be rearranged to take on the
704 * new size. 738 * new size.
705 */ 739 */
706void config_update_font_height(bool recalculate); 740void config_update_font_height(void);
707 741
708/** 742/**
709 * Convert bindsym into bindcode using the first configured layout. 743 * Convert bindsym into bindcode using the first configured layout.
diff --git a/include/sway/criteria.h b/include/sway/criteria.h
index ad8610cd..8da345ea 100644
--- a/include/sway/criteria.h
+++ b/include/sway/criteria.h
@@ -1,7 +1,8 @@
1#ifndef _SWAY_CRITERIA_H 1#ifndef _SWAY_CRITERIA_H
2#define _SWAY_CRITERIA_H 2#define _SWAY_CRITERIA_H
3 3
4#include <pcre.h> 4#define PCRE2_CODE_UNIT_WIDTH 8
5#include <pcre2.h>
5#include "config.h" 6#include "config.h"
6#include "list.h" 7#include "list.h"
7#include "tree/view.h" 8#include "tree/view.h"
@@ -15,13 +16,13 @@ enum criteria_type {
15}; 16};
16 17
17enum pattern_type { 18enum pattern_type {
18 PATTERN_PCRE, 19 PATTERN_PCRE2,
19 PATTERN_FOCUSED, 20 PATTERN_FOCUSED,
20}; 21};
21 22
22struct pattern { 23struct pattern {
23 enum pattern_type match_type; 24 enum pattern_type match_type;
24 pcre *regex; 25 pcre2_code *regex;
25}; 26};
26 27
27struct criteria { 28struct criteria {
@@ -42,6 +43,7 @@ struct criteria {
42 struct pattern *window_role; 43 struct pattern *window_role;
43 enum atom_name window_type; 44 enum atom_name window_type;
44#endif 45#endif
46 bool all;
45 bool floating; 47 bool floating;
46 bool tiling; 48 bool tiling;
47 char urgent; // 'l' for latest or 'o' for oldest 49 char urgent; // 'l' for latest or 'o' for oldest
diff --git a/include/sway/desktop.h b/include/sway/desktop.h
deleted file mode 100644
index c969a76b..00000000
--- a/include/sway/desktop.h
+++ /dev/null
@@ -1,13 +0,0 @@
1#include <wlr/types/wlr_surface.h>
2
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 7d66e699..527d0350 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -4,7 +4,7 @@
4#include <stdint.h> 4#include <stdint.h>
5#include <wlr/types/wlr_pointer_constraints_v1.h> 5#include <wlr/types/wlr_pointer_constraints_v1.h>
6#include <wlr/types/wlr_pointer_gestures_v1.h> 6#include <wlr/types/wlr_pointer_gestures_v1.h>
7#include <wlr/types/wlr_surface.h> 7#include <wlr/types/wlr_compositor.h>
8#include "sway/input/seat.h" 8#include "sway/input/seat.h"
9#include "config.h" 9#include "config.h"
10 10
@@ -35,7 +35,8 @@ struct sway_cursor {
35 pixman_region32_t confine; // invalid if active_constraint == NULL 35 pixman_region32_t confine; // invalid if active_constraint == NULL
36 bool active_confine_requires_warp; 36 bool active_confine_requires_warp;
37 37
38 struct wlr_pointer_gestures_v1 *pointer_gestures; 38 struct wl_listener hold_begin;
39 struct wl_listener hold_end;
39 struct wl_listener pinch_begin; 40 struct wl_listener pinch_begin;
40 struct wl_listener pinch_update; 41 struct wl_listener pinch_update;
41 struct wl_listener pinch_end; 42 struct wl_listener pinch_end;
@@ -51,6 +52,7 @@ struct sway_cursor {
51 52
52 struct wl_listener touch_down; 53 struct wl_listener touch_down;
53 struct wl_listener touch_up; 54 struct wl_listener touch_up;
55 struct wl_listener touch_cancel;
54 struct wl_listener touch_motion; 56 struct wl_listener touch_motion;
55 struct wl_listener touch_frame; 57 struct wl_listener touch_frame;
56 bool simulating_pointer_from_touch; 58 bool simulating_pointer_from_touch;
@@ -62,6 +64,7 @@ struct sway_cursor {
62 struct wl_listener tool_proximity; 64 struct wl_listener tool_proximity;
63 struct wl_listener tool_button; 65 struct wl_listener tool_button;
64 bool simulating_pointer_from_tool_tip; 66 bool simulating_pointer_from_tool_tip;
67 bool simulating_pointer_from_tool_button;
65 uint32_t tool_buttons; 68 uint32_t tool_buttons;
66 69
67 struct wl_listener request_set_cursor; 70 struct wl_listener request_set_cursor;
@@ -105,12 +108,16 @@ void cursor_unhide(struct sway_cursor *cursor);
105int cursor_get_timeout(struct sway_cursor *cursor); 108int cursor_get_timeout(struct sway_cursor *cursor);
106void cursor_notify_key_press(struct sway_cursor *cursor); 109void cursor_notify_key_press(struct sway_cursor *cursor);
107 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
108void dispatch_cursor_button(struct sway_cursor *cursor, 115void dispatch_cursor_button(struct sway_cursor *cursor,
109 struct wlr_input_device *device, uint32_t time_msec, uint32_t button, 116 struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
110 enum wlr_button_state state); 117 enum wl_pointer_button_state state);
111 118
112void dispatch_cursor_axis(struct sway_cursor *cursor, 119void dispatch_cursor_axis(struct sway_cursor *cursor,
113 struct wlr_event_pointer_axis *event); 120 struct wlr_pointer_axis_event *event);
114 121
115void cursor_set_image(struct sway_cursor *cursor, const char *image, 122void cursor_set_image(struct sway_cursor *cursor, const char *image,
116 struct wl_client *client); 123 struct wl_client *client);
@@ -138,4 +145,6 @@ uint32_t get_mouse_button(const char *name, char **error);
138 145
139const char *get_mouse_button_name(uint32_t button); 146const char *get_mouse_button_name(uint32_t button);
140 147
148void handle_request_set_cursor_shape(struct wl_listener *listener, void *data);
149
141#endif 150#endif
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index c9bd08f0..45c75199 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -1,10 +1,10 @@
1#ifndef _SWAY_INPUT_INPUT_MANAGER_H 1#ifndef _SWAY_INPUT_INPUT_MANAGER_H
2#define _SWAY_INPUT_INPUT_MANAGER_H 2#define _SWAY_INPUT_INPUT_MANAGER_H
3#include <libinput.h> 3#include <libinput.h>
4#include <wlr/types/wlr_input_inhibitor.h>
5#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> 4#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
6#include <wlr/types/wlr_virtual_keyboard_v1.h> 5#include <wlr/types/wlr_virtual_keyboard_v1.h>
7#include <wlr/types/wlr_virtual_pointer_v1.h> 6#include <wlr/types/wlr_virtual_pointer_v1.h>
7#include <wlr/types/wlr_transient_seat_v1.h>
8#include "sway/server.h" 8#include "sway/server.h"
9#include "sway/config.h" 9#include "sway/config.h"
10#include "list.h" 10#include "list.h"
@@ -21,10 +21,11 @@ struct sway_input_manager {
21 struct wl_list devices; 21 struct wl_list devices;
22 struct wl_list seats; 22 struct wl_list seats;
23 23
24 struct wlr_input_inhibit_manager *inhibit;
25 struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; 24 struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit;
26 struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; 25 struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard;
27 struct wlr_virtual_pointer_manager_v1 *virtual_pointer; 26 struct wlr_virtual_pointer_manager_v1 *virtual_pointer;
27 struct wlr_pointer_gestures_v1 *pointer_gestures;
28 struct wlr_transient_seat_manager_v1 *transient_seat_manager;
28 29
29 struct wl_listener new_input; 30 struct wl_listener new_input;
30 struct wl_listener inhibit_activate; 31 struct wl_listener inhibit_activate;
@@ -32,6 +33,7 @@ struct sway_input_manager {
32 struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor; 33 struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor;
33 struct wl_listener virtual_keyboard_new; 34 struct wl_listener virtual_keyboard_new;
34 struct wl_listener virtual_pointer_new; 35 struct wl_listener virtual_pointer_new;
36 struct wl_listener transient_seat_create;
35}; 37};
36 38
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/keyboard.h b/include/sway/input/keyboard.h
index 2c61e5a7..571d9e6f 100644
--- a/include/sway/input/keyboard.h
+++ b/include/sway/input/keyboard.h
@@ -50,6 +50,7 @@ struct sway_shortcut_state {
50 50
51struct sway_keyboard { 51struct sway_keyboard {
52 struct sway_seat_device *seat_device; 52 struct sway_seat_device *seat_device;
53 struct wlr_keyboard *wlr;
53 54
54 struct xkb_keymap *keymap; 55 struct xkb_keymap *keymap;
55 xkb_layout_index_t effective_layout; 56 xkb_layout_index_t effective_layout;
diff --git a/include/sway/input/libinput.h b/include/sway/input/libinput.h
index 890d632e..1f84a8e3 100644
--- a/include/sway/input/libinput.h
+++ b/include/sway/input/libinput.h
@@ -2,7 +2,10 @@
2#define _SWAY_INPUT_LIBINPUT_H 2#define _SWAY_INPUT_LIBINPUT_H
3#include "sway/input/input-manager.h" 3#include "sway/input/input-manager.h"
4 4
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 4118df66..428f9679 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -3,7 +3,9 @@
3 3
4#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> 4#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
5#include <wlr/types/wlr_layer_shell_v1.h> 5#include <wlr/types/wlr_layer_shell_v1.h>
6#include <wlr/types/wlr_scene.h>
6#include <wlr/types/wlr_seat.h> 7#include <wlr/types/wlr_seat.h>
8#include <wlr/types/wlr_touch.h>
7#include <wlr/util/edges.h> 9#include <wlr/util/edges.h>
8#include "sway/config.h" 10#include "sway/config.h"
9#include "sway/input/input-manager.h" 11#include "sway/input/input-manager.h"
@@ -15,19 +17,41 @@ struct sway_seat;
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_event_pointer_axis *event); 23 struct wlr_pointer_axis_event *event);
24 void (*hold_begin)(struct sway_seat *seat,
25 struct wlr_pointer_hold_begin_event *event);
26 void (*hold_end)(struct sway_seat *seat,
27 struct wlr_pointer_hold_end_event *event);
28 void (*pinch_begin)(struct sway_seat *seat,
29 struct wlr_pointer_pinch_begin_event *event);
30 void (*pinch_update)(struct sway_seat *seat,
31 struct wlr_pointer_pinch_update_event *event);
32 void (*pinch_end)(struct sway_seat *seat,
33 struct wlr_pointer_pinch_end_event *event);
34 void (*swipe_begin)(struct sway_seat *seat,
35 struct wlr_pointer_swipe_begin_event *event);
36 void (*swipe_update)(struct sway_seat *seat,
37 struct wlr_pointer_swipe_update_event *event);
38 void (*swipe_end)(struct sway_seat *seat,
39 struct wlr_pointer_swipe_end_event *event);
22 void (*rebase)(struct sway_seat *seat, uint32_t time_msec); 40 void (*rebase)(struct sway_seat *seat, uint32_t time_msec);
41 void (*touch_motion)(struct sway_seat *seat,
42 struct wlr_touch_motion_event *event, double lx, double ly);
43 void (*touch_up)(struct sway_seat *seat,
44 struct wlr_touch_up_event *event);
45 void (*touch_down)(struct sway_seat *seat,
46 struct wlr_touch_down_event *event, double lx, double ly);
47 void (*touch_cancel)(struct sway_seat *seat,
48 struct wlr_touch_cancel_event *event);
23 void (*tablet_tool_motion)(struct sway_seat *seat, 49 void (*tablet_tool_motion)(struct sway_seat *seat,
24 struct sway_tablet_tool *tool, uint32_t time_msec); 50 struct sway_tablet_tool *tool, uint32_t time_msec);
25 void (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool, 51 void (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool,
26 uint32_t time_msec, enum wlr_tablet_tool_tip_state state); 52 uint32_t time_msec, enum wlr_tablet_tool_tip_state state);
27 void (*end)(struct sway_seat *seat); 53 void (*end)(struct sway_seat *seat);
28 void (*unref)(struct sway_seat *seat, struct sway_container *con); 54 void (*unref)(struct sway_seat *seat, struct sway_container *con);
29 void (*render)(struct sway_seat *seat, struct sway_output *output,
30 pixman_region32_t *damage);
31 bool allow_set_cursor; 55 bool allow_set_cursor;
32}; 56};
33 57
@@ -50,19 +74,6 @@ struct sway_seat_node {
50 struct wl_listener destroy; 74 struct wl_listener destroy;
51}; 75};
52 76
53struct sway_drag_icon {
54 struct sway_seat *seat;
55 struct wlr_drag_icon *wlr_drag_icon;
56 struct wl_list link; // sway_root::drag_icons
57
58 double x, y; // in layout-local coordinates
59
60 struct wl_listener surface_commit;
61 struct wl_listener map;
62 struct wl_listener unmap;
63 struct wl_listener destroy;
64};
65
66struct sway_drag { 77struct sway_drag {
67 struct sway_seat *seat; 78 struct sway_seat *seat;
68 struct wlr_drag *wlr_drag; 79 struct wlr_drag *wlr_drag;
@@ -73,16 +84,23 @@ struct sway_seat {
73 struct wlr_seat *wlr_seat; 84 struct wlr_seat *wlr_seat;
74 struct sway_cursor *cursor; 85 struct sway_cursor *cursor;
75 86
87 // Seat scene tree structure
88 // - scene_tree
89 // - drag icons
90 // - drag icon 1
91 // - drag icon 2
92 // - seatop specific stuff
93 struct wlr_scene_tree *scene_tree;
94 struct wlr_scene_tree *drag_icons;
95
76 bool has_focus; 96 bool has_focus;
77 struct wl_list focus_stack; // list of containers in focus order 97 struct wl_list focus_stack; // list of containers in focus order
78 struct sway_workspace *workspace; 98 struct sway_workspace *workspace;
79 char *prev_workspace_name; // for workspace back_and_forth 99 char *prev_workspace_name; // for workspace back_and_forth
80 100
81 // If the focused layer is set, views cannot receive keyboard focus
82 struct wlr_layer_surface_v1 *focused_layer; 101 struct wlr_layer_surface_v1 *focused_layer;
83 102 // If the exclusive layer is set, views cannot receive keyboard focus
84 // If exclusive_client is set, no other clients will receive input events 103 bool has_exclusive_layer;
85 struct wl_client *exclusive_client;
86 104
87 // Last touch point 105 // Last touch point
88 int32_t touch_id; 106 int32_t touch_id;
@@ -106,6 +124,7 @@ struct sway_seat {
106 struct wl_listener start_drag; 124 struct wl_listener start_drag;
107 struct wl_listener request_set_selection; 125 struct wl_listener request_set_selection;
108 struct wl_listener request_set_primary_selection; 126 struct wl_listener request_set_primary_selection;
127 struct wl_listener destroy;
109 128
110 struct wl_list devices; // sway_seat_device::link 129 struct wl_list devices; // sway_seat_device::link
111 struct wl_list keyboard_groups; // sway_keyboard_group::link 130 struct wl_list keyboard_groups; // sway_keyboard_group::link
@@ -141,6 +160,9 @@ void seat_add_device(struct sway_seat *seat,
141void seat_configure_device(struct sway_seat *seat, 160void seat_configure_device(struct sway_seat *seat,
142 struct sway_input_device *device); 161 struct sway_input_device *device);
143 162
163void seat_configure_device_mapping(struct sway_seat *seat,
164 struct sway_input_device *input_device);
165
144void seat_reset_device(struct sway_seat *seat, 166void seat_reset_device(struct sway_seat *seat,
145 struct sway_input_device *input_device); 167 struct sway_input_device *input_device);
146 168
@@ -171,8 +193,7 @@ void seat_set_focus_surface(struct sway_seat *seat,
171void seat_set_focus_layer(struct sway_seat *seat, 193void seat_set_focus_layer(struct sway_seat *seat,
172 struct wlr_layer_surface_v1 *layer); 194 struct wlr_layer_surface_v1 *layer);
173 195
174void seat_set_exclusive_client(struct sway_seat *seat, 196void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client);
175 struct wl_client *client);
176 197
177struct sway_node *seat_get_focus(struct sway_seat *seat); 198struct sway_node *seat_get_focus(struct sway_seat *seat);
178 199
@@ -231,7 +252,7 @@ void seat_idle_notify_activity(struct sway_seat *seat,
231 252
232bool 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);
233 254
234void drag_icon_update_position(struct sway_drag_icon *icon); 255void drag_icons_update_position(struct sway_seat *seat);
235 256
236enum wlr_edges find_resize_edge(struct sway_container *cont, 257enum wlr_edges find_resize_edge(struct sway_container *cont,
237 struct wlr_surface *surface, struct sway_cursor *cursor); 258 struct wlr_surface *surface, struct sway_cursor *cursor);
@@ -239,7 +260,13 @@ enum wlr_edges find_resize_edge(struct sway_container *cont,
239void seatop_begin_default(struct sway_seat *seat); 260void seatop_begin_default(struct sway_seat *seat);
240 261
241void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 262void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
242 uint32_t time_msec, int sx, int sy); 263 double sx, double sy);
264
265void seatop_begin_down_on_surface(struct sway_seat *seat,
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);
243 270
244void seatop_begin_move_floating(struct sway_seat *seat, 271void seatop_begin_move_floating(struct sway_seat *seat,
245 struct sway_container *con); 272 struct sway_container *con);
@@ -260,18 +287,18 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
260 struct sway_workspace *workspace); 287 struct sway_workspace *workspace);
261 288
262void 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,
263 uint32_t button, enum wlr_button_state state); 290 uint32_t button, enum wl_pointer_button_state state);
264 291
265void seat_consider_warp_to_focus(struct sway_seat *seat); 292void seat_consider_warp_to_focus(struct sway_seat *seat);
266 293
267void seatop_button(struct sway_seat *seat, uint32_t time_msec, 294void seatop_button(struct sway_seat *seat, uint32_t time_msec,
268 struct wlr_input_device *device, uint32_t button, 295 struct wlr_input_device *device, uint32_t button,
269 enum wlr_button_state state); 296 enum wl_pointer_button_state state);
270 297
271void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); 298void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec);
272 299
273void seatop_pointer_axis(struct sway_seat *seat, 300void seatop_pointer_axis(struct sway_seat *seat,
274 struct wlr_event_pointer_axis *event); 301 struct wlr_pointer_axis_event *event);
275 302
276void seatop_tablet_tool_tip(struct sway_seat *seat, 303void seatop_tablet_tool_tip(struct sway_seat *seat,
277 struct sway_tablet_tool *tool, uint32_t time_msec, 304 struct sway_tablet_tool *tool, uint32_t time_msec,
@@ -280,6 +307,37 @@ void seatop_tablet_tool_tip(struct sway_seat *seat,
280void seatop_tablet_tool_motion(struct sway_seat *seat, 307void seatop_tablet_tool_motion(struct sway_seat *seat,
281 struct sway_tablet_tool *tool, uint32_t time_msec); 308 struct sway_tablet_tool *tool, uint32_t time_msec);
282 309
310void seatop_hold_begin(struct sway_seat *seat,
311 struct wlr_pointer_hold_begin_event *event);
312void seatop_hold_end(struct sway_seat *seat,
313 struct wlr_pointer_hold_end_event *event);
314
315void seatop_pinch_begin(struct sway_seat *seat,
316 struct wlr_pointer_pinch_begin_event *event);
317void seatop_pinch_update(struct sway_seat *seat,
318 struct wlr_pointer_pinch_update_event *event);
319void seatop_pinch_end(struct sway_seat *seat,
320 struct wlr_pointer_pinch_end_event *event);
321
322void seatop_swipe_begin(struct sway_seat *seat,
323 struct wlr_pointer_swipe_begin_event *event);
324void seatop_swipe_update(struct sway_seat *seat,
325 struct wlr_pointer_swipe_update_event *event);
326void seatop_swipe_end(struct sway_seat *seat,
327 struct wlr_pointer_swipe_end_event *event);
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
283void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); 341void seatop_rebase(struct sway_seat *seat, uint32_t time_msec);
284 342
285/** 343/**
@@ -294,13 +352,6 @@ void seatop_end(struct sway_seat *seat);
294 */ 352 */
295void seatop_unref(struct sway_seat *seat, struct sway_container *con); 353void seatop_unref(struct sway_seat *seat, struct sway_container *con);
296 354
297/**
298 * Instructs a seatop to render anything that it needs to render
299 * (eg. dropzone for move-tiling)
300 */
301void seatop_render(struct sway_seat *seat, struct sway_output *output,
302 pixman_region32_t *damage);
303
304bool seatop_allows_set_cursor(struct sway_seat *seat); 355bool seatop_allows_set_cursor(struct sway_seat *seat);
305 356
306/** 357/**
diff --git a/include/sway/input/switch.h b/include/sway/input/switch.h
index 213b471d..de6787b7 100644
--- a/include/sway/input/switch.h
+++ b/include/sway/input/switch.h
@@ -5,6 +5,7 @@
5 5
6struct sway_switch { 6struct sway_switch {
7 struct sway_seat_device *seat_device; 7 struct sway_seat_device *seat_device;
8 struct wlr_switch *wlr;
8 enum wlr_switch_state state; 9 enum wlr_switch_state state;
9 enum wlr_switch_type type; 10 enum wlr_switch_type type;
10 11
diff --git a/include/sway/input/tablet.h b/include/sway/input/tablet.h
index d7e4c242..2fa5db6d 100644
--- a/include/sway/input/tablet.h
+++ b/include/sway/input/tablet.h
@@ -32,6 +32,7 @@ struct sway_tablet_pad {
32 struct wl_list link; 32 struct wl_list link;
33 struct sway_seat_device *seat_device; 33 struct sway_seat_device *seat_device;
34 struct sway_tablet *tablet; 34 struct sway_tablet *tablet;
35 struct wlr_tablet_pad *wlr;
35 struct wlr_tablet_v2_tablet_pad *tablet_v2_pad; 36 struct wlr_tablet_v2_tablet_pad *tablet_v2_pad;
36 37
37 struct wl_listener attach; 38 struct wl_listener attach;
@@ -62,7 +63,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad);
62 63
63void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad); 64void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad);
64 65
65void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, 66void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad,
66 struct wlr_surface *surface); 67 struct wlr_surface *surface);
67 68
68#endif 69#endif
diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h
index 37744266..1993f928 100644
--- a/include/sway/input/text_input.h
+++ b/include/sway/input/text_input.h
@@ -3,13 +3,12 @@
3 3
4#include <wlr/types/wlr_text_input_v3.h> 4#include <wlr/types/wlr_text_input_v3.h>
5#include <wlr/types/wlr_input_method_v2.h> 5#include <wlr/types/wlr_input_method_v2.h>
6#include <wlr/types/wlr_surface.h> 6#include <wlr/types/wlr_compositor.h>
7#include "sway/input/seat.h"
8 7
9/** 8/**
10 * The relay structure manages the relationship between text-input and 9 * The relay structure manages the relationship between text-input and
11 * input_method interfaces on a given seat. Multiple text-input interfaces may 10 * input_method interfaces on a given seat. Multiple text-input interfaces may
12 * be bound to a relay, but at most one will be focused (reveiving events) at 11 * be bound to a relay, but at most one will be focused (receiving events) at
13 * a time. At most one input-method interface may be bound to the seat. The 12 * a time. At most one input-method interface may be bound to the seat. The
14 * relay manages life cycle of both sides. When both sides are present and 13 * relay manages life cycle of both sides. When both sides are present and
15 * focused, the relay passes messages between them. 14 * focused, the relay passes messages between them.
@@ -22,18 +21,21 @@ struct sway_input_method_relay {
22 struct sway_seat *seat; 21 struct sway_seat *seat;
23 22
24 struct wl_list text_inputs; // sway_text_input::link 23 struct wl_list text_inputs; // sway_text_input::link
24 struct wl_list input_popups; // sway_input_popup::link
25 struct wlr_input_method_v2 *input_method; // doesn't have to be present 25 struct wlr_input_method_v2 *input_method; // doesn't have to be present
26 26
27 struct wl_listener text_input_new; 27 struct wl_listener text_input_new;
28 28
29 struct wl_listener input_method_new; 29 struct wl_listener input_method_new;
30 struct wl_listener input_method_commit; 30 struct wl_listener input_method_commit;
31 struct wl_listener input_method_new_popup_surface;
31 struct wl_listener input_method_grab_keyboard; 32 struct wl_listener input_method_grab_keyboard;
32 struct wl_listener input_method_destroy; 33 struct wl_listener input_method_destroy;
33 34
34 struct wl_listener input_method_keyboard_grab_destroy; 35 struct wl_listener input_method_keyboard_grab_destroy;
35}; 36};
36 37
38
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 82ac5368..fd6384e0 100644
--- a/include/sway/layers.h
+++ b/include/sway/layers.h
@@ -1,58 +1,44 @@
1#ifndef _SWAY_LAYERS_H 1#ifndef _SWAY_LAYERS_H
2#define _SWAY_LAYERS_H 2#define _SWAY_LAYERS_H
3#include <stdbool.h> 3#include <stdbool.h>
4#include <wlr/types/wlr_surface.h> 4#include <wlr/types/wlr_compositor.h>
5#include <wlr/types/wlr_layer_shell_v1.h> 5#include <wlr/types/wlr_layer_shell_v1.h>
6 6#include "sway/tree/view.h"
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; 16 bool mapped;
25 enum zwlr_layer_shell_v1_layer layer; 17
18 struct wlr_scene_tree *popups;
19 struct sway_popup_desc desc;
20
21 struct sway_output *output;
22 struct wlr_scene_layer_surface_v1 *scene;
23 struct wlr_scene_tree *tree;
24 struct wlr_layer_surface_v1 *layer_surface;
26}; 25};
27 26
28struct sway_layer_popup { 27struct sway_layer_popup {
29 struct wlr_xdg_popup *wlr_popup; 28 struct wlr_xdg_popup *wlr_popup;
30 enum layer_parent parent_type; 29 struct wlr_scene_tree *scene;
31 union { 30 struct sway_layer_surface *toplevel;
32 struct sway_layer_surface *parent_layer;
33 struct sway_layer_popup *parent_popup;
34 };
35 struct wl_listener map;
36 struct wl_listener unmap;
37 struct wl_listener destroy;
38 struct wl_listener commit;
39 struct wl_listener new_popup;
40};
41 31
42struct sway_layer_subsurface {
43 struct wlr_subsurface *wlr_subsurface;
44 struct sway_layer_surface *layer_surface;
45
46 struct wl_listener map;
47 struct wl_listener unmap;
48 struct wl_listener destroy; 32 struct wl_listener destroy;
33 struct wl_listener new_popup;
49 struct wl_listener commit; 34 struct wl_listener commit;
50}; 35};
51 36
52struct sway_output; 37struct sway_output;
53void arrange_layers(struct sway_output *output);
54 38
55struct sway_layer_surface *layer_from_wlr_layer_surface_v1( 39struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
56 struct wlr_layer_surface_v1 *layer_surface); 40 struct wlr_surface *surface);
41
42void arrange_layers(struct sway_output *output);
57 43
58#endif 44#endif
diff --git a/include/sway/output.h b/include/sway/output.h
index 5dfe0fff..d546d488 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -3,7 +3,9 @@
3#include <time.h> 3#include <time.h>
4#include <unistd.h> 4#include <unistd.h>
5#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/types/wlr_damage_ring.h>
6#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
8#include <wlr/types/wlr_scene.h>
7#include "config.h" 9#include "config.h"
8#include "sway/tree/node.h" 10#include "sway/tree/node.h"
9#include "sway/tree/view.h" 11#include "sway/tree/view.h"
@@ -18,43 +20,63 @@ struct sway_output_state {
18 20
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 DPMS'ed
36 struct wlr_output_mode *current_mode;
37 52
38 bool enabling, enabled; 53 bool enabled;
39 list_t *workspaces; 54 list_t *workspaces;
40 55
41 struct sway_output_state current; 56 struct sway_output_state current;
42 57
58 struct wl_listener layout_destroy;
43 struct wl_listener destroy; 59 struct wl_listener destroy;
44 struct wl_listener commit; 60 struct wl_listener commit;
45 struct wl_listener mode;
46 struct wl_listener present; 61 struct wl_listener present;
47 struct wl_listener damage_destroy; 62 struct wl_listener frame;
48 struct wl_listener damage_frame; 63 struct wl_listener request_state;
49 64
50 struct { 65 struct {
51 struct wl_signal destroy; 66 struct wl_signal disable;
52 } events; 67 } events;
53 68
54 struct timespec last_presentation; 69 struct timespec last_presentation;
55 uint32_t refresh_nsec; 70 uint32_t refresh_nsec;
56 int max_render_time; // In milliseconds 71 int max_render_time; // In milliseconds
57 struct wl_event_source *repaint_timer; 72 struct wl_event_source *repaint_timer;
73 bool gamma_lut_changed;
74};
75
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 3a5670d9..c71851f6 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -2,41 +2,43 @@
2#define _SWAY_SERVER_H 2#define _SWAY_SERVER_H
3#include <stdbool.h> 3#include <stdbool.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/backend.h>
6#include <wlr/backend/session.h>
7#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_compositor.h>
9#include <wlr/types/wlr_data_device.h>
10#include <wlr/types/wlr_input_method_v2.h>
11#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
12#include <wlr/types/wlr_layer_shell_v1.h>
13#include <wlr/types/wlr_output_management_v1.h>
14#include <wlr/types/wlr_output_power_management_v1.h>
15#include <wlr/types/wlr_presentation_time.h>
16#include <wlr/types/wlr_relative_pointer_v1.h>
17#include <wlr/types/wlr_server_decoration.h>
18#include <wlr/types/wlr_text_input_v3.h>
19#include <wlr/types/wlr_xdg_shell.h>
20#include "config.h" 5#include "config.h"
21#include "list.h" 6#include "list.h"
7#include "sway/desktop/idle_inhibit_v1.h"
22#if HAVE_XWAYLAND 8#if HAVE_XWAYLAND
23#include "sway/xwayland.h" 9#include "sway/xwayland.h"
24#endif 10#endif
25 11
26struct sway_transaction; 12struct sway_transaction;
27 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
28struct sway_server { 27struct sway_server {
29 struct wl_display *wl_display; 28 struct wl_display *wl_display;
30 struct wl_event_loop *wl_event_loop; 29 struct wl_event_loop *wl_event_loop;
31 const char *socket; 30 const char *socket;
32 31
33 struct wlr_backend *backend; 32 struct wlr_backend *backend;
34 struct wlr_backend *noop_backend; 33 struct wlr_session *session;
35 // secondary headless backend used for creating virtual outputs on-the-fly 34 // secondary headless backend used for creating virtual outputs on-the-fly
36 struct wlr_backend *headless_backend; 35 struct wlr_backend *headless_backend;
36 struct wlr_renderer *renderer;
37 struct wlr_allocator *allocator;
37 38
38 struct wlr_compositor *compositor; 39 struct wlr_compositor *compositor;
39 struct wl_listener compositor_new_surface; 40
41 struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1;
40 42
41 struct wlr_data_device_manager *data_device_manager; 43 struct wlr_data_device_manager *data_device_manager;
42 44
@@ -44,15 +46,16 @@ struct sway_server {
44 46
45 struct wl_listener new_output; 47 struct wl_listener new_output;
46 struct wl_listener output_layout_change; 48 struct wl_listener output_layout_change;
49 struct wl_listener renderer_lost;
47 50
48 struct wlr_idle *idle; 51 struct wlr_idle_notifier_v1 *idle_notifier_v1;
49 struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1; 52 struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1;
50 53
51 struct wlr_layer_shell_v1 *layer_shell; 54 struct wlr_layer_shell_v1 *layer_shell;
52 struct wl_listener layer_shell_surface; 55 struct wl_listener layer_shell_surface;
53 56
54 struct wlr_xdg_shell *xdg_shell; 57 struct wlr_xdg_shell *xdg_shell;
55 struct wl_listener xdg_shell_surface; 58 struct wl_listener xdg_shell_toplevel;
56 59
57 struct wlr_tablet_manager_v2 *tablet_v2; 60 struct wlr_tablet_manager_v2 *tablet_v2;
58 61
@@ -72,7 +75,8 @@ struct sway_server {
72 struct wl_listener xdg_decoration; 75 struct wl_listener xdg_decoration;
73 struct wl_list xdg_decorations; // sway_xdg_decoration::link 76 struct wl_list xdg_decorations; // sway_xdg_decoration::link
74 77
75 struct wlr_presentation *presentation; 78 struct wlr_drm_lease_v1_manager *drm_lease_manager;
79 struct wl_listener drm_lease_request;
76 80
77 struct wlr_pointer_constraints_v1 *pointer_constraints; 81 struct wlr_pointer_constraints_v1 *pointer_constraints;
78 struct wl_listener pointer_constraint; 82 struct wl_listener pointer_constraint;
@@ -81,14 +85,36 @@ struct sway_server {
81 struct wl_listener output_manager_apply; 85 struct wl_listener output_manager_apply;
82 struct wl_listener output_manager_test; 86 struct wl_listener output_manager_test;
83 87
88 struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1;
89 struct wl_listener gamma_control_set_gamma;
90
91 struct {
92 struct sway_session_lock *lock;
93 struct wlr_session_lock_manager_v1 *manager;
94
95 struct wl_listener new_lock;
96 struct wl_listener manager_destroy;
97 } session_lock;
98
84 struct wlr_output_power_manager_v1 *output_power_manager_v1; 99 struct wlr_output_power_manager_v1 *output_power_manager_v1;
85 struct wl_listener output_power_manager_set_mode; 100 struct wl_listener output_power_manager_set_mode;
86 struct wlr_input_method_manager_v2 *input_method; 101 struct wlr_input_method_manager_v2 *input_method;
87 struct wlr_text_input_manager_v3 *text_input; 102 struct wlr_text_input_manager_v3 *text_input;
103 struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list;
88 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; 104 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
105 struct wlr_content_type_manager_v1 *content_type_manager_v1;
106 struct wlr_data_control_manager_v1 *data_control_manager_v1;
107 struct wlr_screencopy_manager_v1 *screencopy_manager_v1;
108 struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1;
109 struct wlr_security_context_manager_v1 *security_context_manager_v1;
89 110
90 struct wlr_xdg_activation_v1 *xdg_activation_v1; 111 struct wlr_xdg_activation_v1 *xdg_activation_v1;
91 struct wl_listener xdg_activation_v1_request_activate; 112 struct wl_listener xdg_activation_v1_request_activate;
113 struct wl_listener xdg_activation_v1_new_token;
114
115 struct wl_listener request_set_cursor_shape;
116
117 struct wl_list pending_launcher_ctxs; // launcher_ctx::link
92 118
93 // The timeout for transactions, after which a transaction is applied 119 // The timeout for transactions, after which a transaction is applied
94 // regardless of readiness. 120 // regardless of readiness.
@@ -115,29 +141,30 @@ struct sway_debug {
115 bool noatomic; // Ignore atomic layout updates 141 bool noatomic; // Ignore atomic layout updates
116 bool txn_timings; // Log verbose messages about transactions 142 bool txn_timings; // Log verbose messages about transactions
117 bool txn_wait; // Always wait for the timeout before applying 143 bool txn_wait; // Always wait for the timeout before applying
118 144 bool legacy_wl_drm; // Enable the legacy wl_drm interface
119 enum {
120 DAMAGE_DEFAULT, // Default behaviour
121 DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged
122 DAMAGE_RERENDER, // Render the full output when any damage occurs
123 } damage;
124}; 145};
125 146
126extern struct sway_debug debug; 147extern struct sway_debug debug;
127 148
128/* Prepares an unprivileged server_init by performing all privileged operations in advance */ 149extern bool allow_unsupported_gpu;
129bool server_privileged_prepare(struct sway_server *server); 150
130bool server_init(struct sway_server *server); 151bool server_init(struct sway_server *server);
131void server_fini(struct sway_server *server); 152void server_fini(struct sway_server *server);
132bool server_start(struct sway_server *server); 153bool server_start(struct sway_server *server);
133void server_run(struct sway_server *server); 154void server_run(struct sway_server *server);
134 155
135void handle_compositor_new_surface(struct wl_listener *listener, void *data); 156void restore_nofile_limit(void);
157
136void handle_new_output(struct wl_listener *listener, void *data); 158void handle_new_output(struct wl_listener *listener, void *data);
137 159
138void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); 160void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data);
139void handle_layer_shell_surface(struct wl_listener *listener, void *data); 161void handle_layer_shell_surface(struct wl_listener *listener, void *data);
140void handle_xdg_shell_surface(struct wl_listener *listener, void *data); 162void sway_session_lock_init(void);
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);
141#if HAVE_XWAYLAND 168#if HAVE_XWAYLAND
142void handle_xwayland_surface(struct wl_listener *listener, void *data); 169void handle_xwayland_surface(struct wl_listener *listener, void *data);
143#endif 170#endif
@@ -146,5 +173,9 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data);
146void handle_pointer_constraint(struct wl_listener *listener, void *data); 173void handle_pointer_constraint(struct wl_listener *listener, void *data);
147void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, 174void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
148 void *data); 175 void *data);
176void xdg_activation_v1_handle_new_token(struct wl_listener *listener,
177 void *data);
178
179void set_rr_scheduling(void);
149 180
150#endif 181#endif
diff --git a/include/sway/surface.h b/include/sway/surface.h
deleted file mode 100644
index 4da96c02..00000000
--- a/include/sway/surface.h
+++ /dev/null
@@ -1,18 +0,0 @@
1#ifndef _SWAY_SURFACE_H
2#define _SWAY_SURFACE_H
3#include <wlr/types/wlr_surface.h>
4
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 38ee68bd..93f6bfbb 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -2,7 +2,8 @@
2#define _SWAY_CONTAINER_H 2#define _SWAY_CONTAINER_H
3#include <stdint.h> 3#include <stdint.h>
4#include <sys/types.h> 4#include <sys/types.h>
5#include <wlr/types/wlr_surface.h> 5#include <wlr/types/wlr_compositor.h>
6#include <wlr/types/wlr_scene.h>
6#include "list.h" 7#include "list.h"
7#include "sway/tree/node.h" 8#include "sway/tree/node.h"
8 9
@@ -68,11 +69,39 @@ struct sway_container {
68 struct sway_node node; 69 struct sway_node node;
69 struct sway_view *view; 70 struct sway_view *view;
70 71
72 struct wlr_scene_tree *scene_tree;
73
74 struct {
75 struct wlr_scene_tree *tree;
76
77 struct wlr_scene_tree *border;
78 struct wlr_scene_tree *background;
79
80 struct sway_text_node *title_text;
81 struct sway_text_node *marks_text;
82 } title_bar;
83
84 struct {
85 struct wlr_scene_tree *tree;
86
87 struct wlr_scene_rect *top;
88 struct wlr_scene_rect *bottom;
89 struct wlr_scene_rect *left;
90 struct wlr_scene_rect *right;
91 } border;
92
93 struct wlr_scene_tree *content_tree;
94 struct wlr_scene_buffer *output_handler;
95
96 struct wl_listener output_enter;
97 struct wl_listener output_leave;
98
71 struct sway_container_state current; 99 struct sway_container_state current;
72 struct sway_container_state pending; 100 struct sway_container_state pending;
73 101
74 char *title; // The view's title (unformatted) 102 char *title; // The view's title (unformatted)
75 char *formatted_title; // The title displayed in the title bar 103 char *formatted_title; // The title displayed in the title bar
104 int title_width;
76 105
77 enum sway_container_layout prev_split_layout; 106 enum sway_container_layout prev_split_layout;
78 107
@@ -100,33 +129,19 @@ struct sway_container {
100 double child_total_width; 129 double child_total_width;
101 double child_total_height; 130 double child_total_height;
102 131
103 // In most cases this is the same as the content x and y, but if the view
104 // refuses to resize to the content dimensions then it can be smaller.
105 // These are in layout coordinates.
106 double surface_x, surface_y;
107
108 // Outputs currently being intersected
109 list_t *outputs; // struct sway_output
110
111 // Indicates that the container is a scratchpad container. 132 // Indicates that the container is a scratchpad container.
112 // Both hidden and visible scratchpad containers have scratchpad=true. 133 // Both hidden and visible scratchpad containers have scratchpad=true.
113 // Hidden scratchpad containers have a NULL parent. 134 // Hidden scratchpad containers have a NULL parent.
114 bool scratchpad; 135 bool scratchpad;
115 136
116 float alpha; 137 // Stores last output size and position for adjusting coordinates of
138 // scratchpad windows.
139 // Unused for non-scratchpad windows.
140 struct wlr_box transform;
117 141
118 struct wlr_texture *title_focused; 142 float alpha;
119 struct wlr_texture *title_focused_inactive;
120 struct wlr_texture *title_unfocused;
121 struct wlr_texture *title_urgent;
122 size_t title_height;
123 size_t title_baseline;
124 143
125 list_t *marks; // char * 144 list_t *marks; // char *
126 struct wlr_texture *marks_focused;
127 struct wlr_texture *marks_focused_inactive;
128 struct wlr_texture *marks_unfocused;
129 struct wlr_texture *marks_urgent;
130 145
131 struct { 146 struct {
132 struct wl_signal destroy; 147 struct wl_signal destroy;
@@ -146,19 +161,6 @@ void container_begin_destroy(struct sway_container *con);
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,18 +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 185
186/** 186void container_update_marks(struct sway_container *container);
187 * Calculate the container's title_height property.
188 */
189void container_calculate_title_height(struct sway_container *container);
190 187
191size_t container_build_representation(enum sway_container_layout layout, 188size_t container_build_representation(enum sway_container_layout layout,
192 list_t *children, char *buffer); 189 list_t *children, char *buffer);
@@ -201,6 +198,9 @@ size_t container_titlebar_height(void);
201void floating_calculate_constraints(int *min_width, int *max_width, 198void floating_calculate_constraints(int *min_width, int *max_width,
202 int *min_height, int *max_height); 199 int *min_height, int *max_height);
203 200
201void floating_fix_coordinates(struct sway_container *con,
202 struct wlr_box *old, struct wlr_box *new);
203
204void container_floating_resize_and_center(struct sway_container *con); 204void container_floating_resize_and_center(struct sway_container *con);
205 205
206void container_floating_set_default_size(struct sway_container *con); 206void container_floating_set_default_size(struct sway_container *con);
@@ -220,11 +220,6 @@ void container_set_geometry_from_content(struct sway_container *con);
220bool container_is_floating(struct sway_container *container); 220bool container_is_floating(struct sway_container *container);
221 221
222/** 222/**
223 * Same as above, but for current container state.
224 */
225bool container_is_current_floating(struct sway_container *container);
226
227/**
228 * Get a container's box in layout coordinates. 223 * Get a container's box in layout coordinates.
229 */ 224 */
230void container_get_box(struct sway_container *container, struct wlr_box *box); 225void container_get_box(struct sway_container *container, struct wlr_box *box);
@@ -286,26 +281,12 @@ bool container_is_floating_or_child(struct sway_container *container);
286 */ 281 */
287bool container_is_fullscreen_or_child(struct sway_container *container); 282bool container_is_fullscreen_or_child(struct sway_container *container);
288 283
289/**
290 * Return the output which will be used for scale purposes.
291 * This is the most recently entered output.
292 * If the container is not on any output, return NULL.
293 */
294struct sway_output *container_get_effective_output(struct sway_container *con);
295
296void container_discover_outputs(struct sway_container *con);
297
298enum sway_container_layout container_parent_layout(struct sway_container *con); 284enum sway_container_layout container_parent_layout(struct sway_container *con);
299 285
300enum sway_container_layout container_current_parent_layout(
301 struct sway_container *con);
302
303list_t *container_get_siblings(struct sway_container *container); 286list_t *container_get_siblings(struct sway_container *container);
304 287
305int container_sibling_index(struct sway_container *child); 288int container_sibling_index(struct sway_container *child);
306 289
307list_t *container_get_current_siblings(struct sway_container *container);
308
309void container_handle_fullscreen_reparent(struct sway_container *con); 290void container_handle_fullscreen_reparent(struct sway_container *con);
310 291
311void container_add_child(struct sway_container *parent, 292void container_add_child(struct sway_container *parent,
@@ -353,8 +334,6 @@ bool container_has_mark(struct sway_container *container, char *mark);
353 334
354void container_add_mark(struct sway_container *container, char *mark); 335void container_add_mark(struct sway_container *container, char *mark);
355 336
356void container_update_marks_textures(struct sway_container *container);
357
358void container_raise_floating(struct sway_container *con); 337void container_raise_floating(struct sway_container *con);
359 338
360bool container_is_scratchpad_hidden(struct sway_container *con); 339bool container_is_scratchpad_hidden(struct sway_container *con);
@@ -369,7 +348,7 @@ bool container_is_sticky_or_child(struct sway_container *con);
369 * This will destroy pairs of redundant H/V splits 348 * This will destroy pairs of redundant H/V splits
370 * e.g. H[V[H[app app]] app] -> H[app app app] 349 * e.g. H[V[H[app app]] app] -> H[app app app]
371 * The middle "V[H[" are eliminated by a call to container_squash 350 * The middle "V[H[" are eliminated by a call to container_squash
372 * on the V[ con. It's grandchildren are added to it's parent. 351 * on the V[ con. It's grandchildren are added to its parent.
373 * 352 *
374 * This function is roughly equivalent to i3's tree_flatten here: 353 * This function is roughly equivalent to i3's tree_flatten here:
375 * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651 354 * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651
@@ -378,4 +357,10 @@ bool container_is_sticky_or_child(struct sway_container *con);
378 */ 357 */
379int container_squash(struct sway_container *con); 358int container_squash(struct sway_container *con);
380 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
381#endif 366#endif
diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h
index 470ee3b5..e2dbcdf0 100644
--- a/include/sway/tree/node.h
+++ b/include/sway/tree/node.h
@@ -1,6 +1,8 @@
1#ifndef _SWAY_NODE_H 1#ifndef _SWAY_NODE_H
2#define _SWAY_NODE_H 2#define _SWAY_NODE_H
3#include <wayland-server-core.h>
3#include <stdbool.h> 4#include <stdbool.h>
5#include <wlr/types/wlr_scene.h>
4#include "list.h" 6#include "list.h"
5 7
6#define MIN_SANE_W 100 8#define MIN_SANE_W 100
@@ -74,4 +76,15 @@ list_t *node_get_children(struct sway_node *node);
74 76
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 e8f4d573..15df0f55 100644
--- a/include/sway/tree/root.h
+++ b/include/sway/tree/root.h
@@ -3,6 +3,7 @@
3#include <wayland-server-core.h> 3#include <wayland-server-core.h>
4#include <wayland-util.h> 4#include <wayland-util.h>
5#include <wlr/types/wlr_output_layout.h> 5#include <wlr/types/wlr_output_layout.h>
6#include <wlr/types/wlr_scene.h>
6#include <wlr/render/wlr_texture.h> 7#include <wlr/render/wlr_texture.h>
7#include "sway/tree/container.h" 8#include "sway/tree/container.h"
8#include "sway/tree/node.h" 9#include "sway/tree/node.h"
@@ -16,10 +17,44 @@ struct sway_root {
16 struct wlr_output_layout *output_layout; 17 struct wlr_output_layout *output_layout;
17 18
18 struct wl_listener output_layout_change; 19 struct wl_listener output_layout_change;
20
21 // scene node layout:
22 // - root
23 // - staging
24 // - layer shell stuff
25 // - tiling
26 // - floating
27 // - fullscreen stuff
28 // - seat stuff
29 // - ext_session_lock
30 struct wlr_scene *root_scene;
31
32 // since wlr_scene nodes can't be orphaned and must always
33 // have a parent, use this staging scene_tree so that a
34 // node always have a valid parent. Nothing in this
35 // staging node will be visible.
36 struct wlr_scene_tree *staging;
37
38 // tree containing all layers the compositor will render. Cursor handling
39 // will end up iterating this tree.
40 struct wlr_scene_tree *layer_tree;
41
42 struct {
43 struct wlr_scene_tree *shell_background;
44 struct wlr_scene_tree *shell_bottom;
45 struct wlr_scene_tree *tiling;
46 struct wlr_scene_tree *floating;
47 struct wlr_scene_tree *shell_top;
48 struct wlr_scene_tree *fullscreen;
49 struct wlr_scene_tree *fullscreen_global;
19#if HAVE_XWAYLAND 50#if HAVE_XWAYLAND
20 struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link 51 struct wlr_scene_tree *unmanaged;
21#endif 52#endif
22 struct wl_list drag_icons; // sway_drag_icon::link 53 struct wlr_scene_tree *shell_overlay;
54 struct wlr_scene_tree *popup;
55 struct wlr_scene_tree *seat;
56 struct wlr_scene_tree *session_lock;
57 } layers;
23 58
24 // Includes disabled outputs 59 // Includes disabled outputs
25 struct wl_list all_outputs; // sway_output::link 60 struct wl_list all_outputs; // sway_output::link
@@ -28,10 +63,11 @@ struct sway_root {
28 double width, height; 63 double width, height;
29 64
30 list_t *outputs; // struct sway_output 65 list_t *outputs; // struct sway_output
66 list_t *non_desktop_outputs; // struct sway_output_non_desktop
31 list_t *scratchpad; // struct sway_container 67 list_t *scratchpad; // struct sway_container
32 68
33 // For when there's no connected outputs 69 // For when there's no connected outputs
34 struct sway_output *noop_output; 70 struct sway_output *fallback_output;
35 71
36 struct sway_container *fullscreen_global; 72 struct sway_container *fullscreen_global;
37 73
@@ -40,7 +76,7 @@ struct sway_root {
40 } events; 76 } events;
41}; 77};
42 78
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 923498ec..7faacdcc 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -1,8 +1,9 @@
1#ifndef _SWAY_VIEW_H 1#ifndef _SWAY_VIEW_H
2#define _SWAY_VIEW_H 2#define _SWAY_VIEW_H
3#include <wayland-server-core.h> 3#include <wayland-server-core.h>
4#include <wlr/types/wlr_surface.h> 4#include <wlr/types/wlr_compositor.h>
5#include "config.h" 5#include <wlr/types/wlr_scene.h>
6#include "sway/config.h"
6#if HAVE_XWAYLAND 7#if HAVE_XWAYLAND
7#include <wlr/xwayland.h> 8#include <wlr/xwayland.h>
8#endif 9#endif
@@ -45,10 +46,6 @@ struct sway_view_impl {
45 void (*set_fullscreen)(struct sway_view *view, bool fullscreen); 46 void (*set_fullscreen)(struct sway_view *view, bool fullscreen);
46 void (*set_resizing)(struct sway_view *view, bool resizing); 47 void (*set_resizing)(struct sway_view *view, bool resizing);
47 bool (*wants_floating)(struct sway_view *view); 48 bool (*wants_floating)(struct sway_view *view);
48 void (*for_each_surface)(struct sway_view *view,
49 wlr_surface_iterator_func_t iterator, void *user_data);
50 void (*for_each_popup_surface)(struct sway_view *view,
51 wlr_surface_iterator_func_t iterator, void *user_data);
52 bool (*is_transient_for)(struct sway_view *child, 49 bool (*is_transient_for)(struct sway_view *child,
53 struct sway_view *ancestor); 50 struct sway_view *ancestor);
54 void (*close)(struct sway_view *view); 51 void (*close)(struct sway_view *view);
@@ -56,24 +53,20 @@ struct sway_view_impl {
56 void (*destroy)(struct sway_view *view); 53 void (*destroy)(struct sway_view *view);
57}; 54};
58 55
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;
@@ -108,7 +97,7 @@ struct sway_view {
108 list_t *executed_criteria; // struct criteria * 97 list_t *executed_criteria; // struct criteria *
109 98
110 union { 99 union {
111 struct wlr_xdg_surface *wlr_xdg_surface; 100 struct wlr_xdg_toplevel *wlr_xdg_toplevel;
112#if HAVE_XWAYLAND 101#if HAVE_XWAYLAND
113 struct wlr_xwayland_surface *wlr_xwayland_surface; 102 struct wlr_xwayland_surface *wlr_xwayland_surface;
114#endif 103#endif
@@ -118,8 +107,6 @@ struct sway_view {
118 struct wl_signal unmap; 107 struct wl_signal unmap;
119 } events; 108 } events;
120 109
121 struct wl_listener surface_new_subsurface;
122
123 int max_render_time; // In milliseconds 110 int max_render_time; // In milliseconds
124 111
125 enum seat_config_shortcuts_inhibit shortcuts_inhibit; 112 enum seat_config_shortcuts_inhibit shortcuts_inhibit;
@@ -144,6 +131,8 @@ struct sway_xdg_shell_view {
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,71 +144,55 @@ struct sway_xwayland_view {
155 struct wl_listener set_title; 144 struct wl_listener set_title;
156 struct wl_listener set_class; 145 struct wl_listener set_class;
157 struct wl_listener set_role; 146 struct wl_listener set_role;
147 struct wl_listener set_startup_id;
158 struct wl_listener set_window_type; 148 struct wl_listener set_window_type;
159 struct wl_listener set_hints; 149 struct wl_listener set_hints;
160 struct wl_listener set_decorations; 150 struct wl_listener set_decorations;
151 struct wl_listener associate;
152 struct wl_listener dissociate;
161 struct wl_listener map; 153 struct wl_listener map;
162 struct wl_listener unmap; 154 struct wl_listener unmap;
163 struct wl_listener destroy; 155 struct wl_listener destroy;
164 struct wl_listener override_redirect; 156 struct wl_listener override_redirect;
157
158 struct wl_listener surface_tree_destroy;
165}; 159};
166 160
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
166 struct wl_listener request_activate;
173 struct wl_listener request_configure; 167 struct wl_listener request_configure;
174 struct wl_listener request_fullscreen; 168 struct wl_listener request_fullscreen;
175 struct wl_listener commit;
176 struct wl_listener set_geometry; 169 struct wl_listener set_geometry;
170 struct wl_listener associate;
171 struct wl_listener dissociate;
177 struct wl_listener map; 172 struct wl_listener map;
178 struct wl_listener unmap; 173 struct wl_listener unmap;
179 struct wl_listener destroy; 174 struct wl_listener destroy;
180 struct wl_listener override_redirect; 175 struct wl_listener override_redirect;
181}; 176};
182#endif 177#endif
183struct sway_view_child;
184
185struct sway_view_child_impl {
186 void (*get_root_coords)(struct sway_view_child *child, int *sx, int *sy);
187 void (*destroy)(struct sway_view_child *child);
188};
189
190/**
191 * A view child is a surface in the view tree, such as a subsurface or a popup.
192 */
193struct sway_view_child {
194 const struct sway_view_child_impl *impl;
195 struct wl_list link;
196 178
179struct sway_popup_desc {
180 struct wlr_scene_node *relative;
197 struct sway_view *view; 181 struct sway_view *view;
198 struct sway_view_child *parent;
199 struct wl_list children; // sway_view_child::link
200 struct wlr_surface *surface;
201 bool mapped;
202
203 struct wl_listener surface_commit;
204 struct wl_listener surface_new_subsurface;
205 struct wl_listener surface_map;
206 struct wl_listener surface_unmap;
207 struct wl_listener surface_destroy;
208 struct wl_listener view_unmap;
209};
210
211struct sway_subsurface {
212 struct sway_view_child child;
213
214 struct wl_listener destroy;
215}; 182};
216 183
217struct sway_xdg_popup { 184struct sway_xdg_popup {
218 struct sway_view_child child; 185 struct sway_view *view;
186
187 struct wlr_scene_tree *scene_tree;
188 struct wlr_scene_tree *xdg_surface_tree;
189 struct wlr_xdg_popup *wlr_xdg_popup;
219 190
220 struct wlr_xdg_surface *wlr_xdg_surface; 191 struct sway_popup_desc desc;
221 192
193 struct wl_listener surface_commit;
222 struct wl_listener new_popup; 194 struct wl_listener new_popup;
195 struct wl_listener reposition;
223 struct wl_listener destroy; 196 struct wl_listener destroy;
224}; 197};
225 198
@@ -268,7 +241,12 @@ void view_set_activated(struct sway_view *view, bool activated);
268/** 241/**
269 * Called when the view requests to be focused. 242 * Called when the view requests to be focused.
270 */ 243 */
271void 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);
272 250
273/** 251/**
274 * If possible, instructs the client to change their decoration mode. 252 * If possible, instructs the client to change their decoration mode.
@@ -287,43 +265,31 @@ void view_close(struct sway_view *view);
287 265
288void view_close_popups(struct sway_view *view); 266void view_close_popups(struct sway_view *view);
289 267
290void view_damage_from(struct sway_view *view);
291
292/**
293 * Iterate all surfaces of a view (toplevels + popups).
294 */
295void view_for_each_surface(struct sway_view *view,
296 wlr_surface_iterator_func_t iterator, void *user_data);
297
298/**
299 * Iterate all popup surfaces of a view.
300 */
301void view_for_each_popup_surface(struct sway_view *view,
302 wlr_surface_iterator_func_t iterator, void *user_data);
303
304// view implementation 268// view implementation
305 269
306void view_init(struct sway_view *view, enum sway_view_type type, 270bool view_init(struct sway_view *view, enum sway_view_type type,
307 const struct sway_view_impl *impl); 271 const struct sway_view_impl *impl);
308 272
309void view_destroy(struct sway_view *view); 273void view_destroy(struct sway_view *view);
310 274
311void view_begin_destroy(struct sway_view *view); 275void view_begin_destroy(struct sway_view *view);
312 276
277/**
278 * Map a view, ie. make it visible in the tree.
279 *
280 * `fullscreen` should be set to true (and optionally `fullscreen_output`
281 * should be populated) if the view should be made fullscreen immediately.
282 *
283 * `decoration` should be set to true if the client prefers CSD. The client's
284 * preference may be ignored.
285 */
313void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, 286void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
314 bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); 287 bool fullscreen, struct wlr_output *fullscreen_output, bool decoration);
315 288
316void view_unmap(struct sway_view *view); 289void view_unmap(struct sway_view *view);
317 290
318void view_update_size(struct sway_view *view); 291void view_update_size(struct sway_view *view);
319void view_center_surface(struct sway_view *view); 292void view_center_and_clip_surface(struct sway_view *view);
320
321void view_child_init(struct sway_view_child *child,
322 const struct sway_view_child_impl *impl, struct sway_view *view,
323 struct wlr_surface *surface);
324
325void view_child_destroy(struct sway_view_child *child);
326
327 293
328struct sway_view *view_from_wlr_xdg_surface( 294struct sway_view *view_from_wlr_xdg_surface(
329 struct wlr_xdg_surface *xdg_surface); 295 struct wlr_xdg_surface *xdg_surface);
@@ -333,6 +299,8 @@ struct sway_view *view_from_wlr_xwayland_surface(
333#endif 299#endif
334struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); 300struct sway_view *view_from_wlr_surface(struct wlr_surface *surface);
335 301
302void view_update_app_id(struct sway_view *view);
303
336/** 304/**
337 * Re-read the view's title property and update any relevant title bars. 305 * Re-read the view's title property and update any relevant title bars.
338 * The force argument makes it recreate the title bars even if the title hasn't 306 * The force argument makes it recreate the title bars even if the title hasn't
@@ -362,4 +330,8 @@ void view_save_buffer(struct sway_view *view);
362 330
363bool 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);
364 332
333void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);
334
335void view_send_frame_done(struct sway_view *view);
336
365#endif 337#endif
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index b3d93a81..58bde20c 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -2,6 +2,8 @@
2#define _SWAY_WORKSPACE_H 2#define _SWAY_WORKSPACE_H
3 3
4#include <stdbool.h> 4#include <stdbool.h>
5#include <wlr/types/wlr_scene.h>
6#include "sway/config.h"
5#include "sway/tree/container.h" 7#include "sway/tree/container.h"
6#include "sway/tree/node.h" 8#include "sway/tree/node.h"
7 9
@@ -22,6 +24,12 @@ struct sway_workspace_state {
22 24
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 545a66a8..197d2190 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -4,6 +4,7 @@
4#include "config.h" 4#include "config.h"
5#include "input.h" 5#include "input.h"
6#include "pool-buffer.h" 6#include "pool-buffer.h"
7#include "cursor-shape-v1-client-protocol.h"
7#include "wlr-layer-shell-unstable-v1-client-protocol.h" 8#include "wlr-layer-shell-unstable-v1-client-protocol.h"
8#include "xdg-output-unstable-v1-client-protocol.h" 9#include "xdg-output-unstable-v1-client-protocol.h"
9 10
@@ -30,6 +31,7 @@ struct swaybar {
30 struct wl_compositor *compositor; 31 struct wl_compositor *compositor;
31 struct zwlr_layer_shell_v1 *layer_shell; 32 struct zwlr_layer_shell_v1 *layer_shell;
32 struct zxdg_output_manager_v1 *xdg_output_manager; 33 struct zxdg_output_manager_v1 *xdg_output_manager;
34 struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
33 struct wl_shm *shm; 35 struct wl_shm *shm;
34 36
35 struct swaybar_config *config; 37 struct swaybar_config *config;
@@ -58,7 +60,6 @@ struct swaybar_output {
58 struct zxdg_output_v1 *xdg_output; 60 struct zxdg_output_v1 *xdg_output;
59 struct wl_surface *surface; 61 struct wl_surface *surface;
60 struct zwlr_layer_surface_v1 *layer_surface; 62 struct zwlr_layer_surface_v1 *layer_surface;
61 struct wl_region *input_region;
62 uint32_t wl_name; 63 uint32_t wl_name;
63 64
64 struct wl_list workspaces; // swaybar_workspace::link 65 struct wl_list workspaces; // swaybar_workspace::link
diff --git a/include/swaybar/config.h b/include/swaybar/config.h
index 4cacd21a..361acd99 100644
--- a/include/swaybar/config.h
+++ b/include/swaybar/config.h
@@ -6,6 +6,7 @@
6#include "../include/config.h" 6#include "../include/config.h"
7#include "list.h" 7#include "list.h"
8#include "util.h" 8#include "util.h"
9#include <pango/pangocairo.h>
9 10
10struct box_colors { 11struct box_colors {
11 uint32_t border; 12 uint32_t border;
@@ -28,7 +29,7 @@ struct swaybar_config {
28 char *status_command; 29 char *status_command;
29 bool pango_markup; 30 bool pango_markup;
30 uint32_t position; // zwlr_layer_surface_v1_anchor 31 uint32_t position; // zwlr_layer_surface_v1_anchor
31 char *font; 32 PangoFontDescription *font_description;
32 char *sep_symbol; 33 char *sep_symbol;
33 char *mode; 34 char *mode;
34 char *hidden_state; 35 char *hidden_state;
diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h
index df8cdd09..dced2a6c 100644
--- a/include/swaybar/i3bar.h
+++ b/include/swaybar/i3bar.h
@@ -19,6 +19,7 @@ struct i3bar_block {
19 // Airblader features 19 // Airblader features
20 uint32_t background; 20 uint32_t background;
21 uint32_t border; 21 uint32_t border;
22 bool border_set;
22 int border_top; 23 int border_top;
23 int border_bottom; 24 int border_bottom;
24 int border_left; 25 int border_left;
@@ -29,6 +30,6 @@ void i3bar_block_unref(struct i3bar_block *block);
29bool i3bar_handle_readable(struct status_line *status); 30bool i3bar_handle_readable(struct status_line *status);
30enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, 31enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
31 struct i3bar_block *block, double x, double y, double rx, double ry, 32 struct i3bar_block *block, double x, double y, double rx, double ry,
32 double w, double h, int scale, uint32_t button); 33 double w, double h, int scale, uint32_t button, bool released);
33 34
34#endif 35#endif
diff --git a/include/swaybar/image.h b/include/swaybar/image.h
new file mode 100644
index 00000000..53a210dd
--- /dev/null
+++ b/include/swaybar/image.h
@@ -0,0 +1,7 @@
1#ifndef _SWAYBAR_IMAGE_H
2#define _SWAYBAR_IMAGE_H
3#include <cairo.h>
4
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 9e39e716..fb9e9c21 100644
--- a/include/swaynag/swaynag.h
+++ b/include/swaynag/swaynag.h
@@ -4,8 +4,9 @@
4#include <strings.h> 4#include <strings.h>
5#include "list.h" 5#include "list.h"
6#include "pool-buffer.h" 6#include "pool-buffer.h"
7#include "cursor-shape-v1-client-protocol.h"
8
7#include "swaynag/types.h" 9#include "swaynag/types.h"
8#include "xdg-output-unstable-v1-client-protocol.h"
9 10
10#define SWAYNAG_MAX_HEIGHT 500 11#define SWAYNAG_MAX_HEIGHT 500
11 12
@@ -59,6 +60,7 @@ struct swaynag_button {
59struct swaynag_details { 60struct swaynag_details {
60 bool visible; 61 bool visible;
61 char *message; 62 char *message;
63 char *details_text;
62 64
63 int x; 65 int x;
64 int y; 66 int y;
@@ -75,18 +77,17 @@ struct swaynag_details {
75 77
76struct swaynag { 78struct swaynag {
77 bool run_display; 79 bool run_display;
78 int querying_outputs;
79 80
80 struct wl_display *display; 81 struct wl_display *display;
81 struct wl_compositor *compositor; 82 struct wl_compositor *compositor;
82 struct wl_seat *seat; 83 struct wl_seat *seat;
83 struct wl_shm *shm; 84 struct wl_shm *shm;
84 struct zxdg_output_manager_v1 *xdg_output_manager;
85 struct wl_list outputs; // swaynag_output::link 85 struct wl_list outputs; // swaynag_output::link
86 struct wl_list seats; // swaynag_seat::link 86 struct wl_list seats; // swaynag_seat::link
87 struct swaynag_output *output; 87 struct swaynag_output *output;
88 struct zwlr_layer_shell_v1 *layer_shell; 88 struct zwlr_layer_shell_v1 *layer_shell;
89 struct zwlr_layer_surface_v1 *layer_surface; 89 struct zwlr_layer_surface_v1 *layer_surface;
90 struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
90 struct wl_surface *surface; 91 struct wl_surface *surface;
91 92
92 uint32_t width; 93 uint32_t width;
diff --git a/include/swaynag/types.h b/include/swaynag/types.h
index 3c3b2754..9c3c50db 100644
--- a/include/swaynag/types.h
+++ b/include/swaynag/types.h
@@ -1,10 +1,14 @@
1#ifndef _SWAYNAG_TYPES_H 1#ifndef _SWAYNAG_TYPES_H
2#define _SWAYNAG_TYPES_H 2#define _SWAYNAG_TYPES_H
3 3
4#include <stdint.h>
5#include <pango/pangocairo.h>
6#include "list.h"
7
4struct swaynag_type { 8struct swaynag_type {
5 char *name; 9 char *name;
6 10
7 char *font; 11 PangoFontDescription *font_description;
8 char *output; 12 char *output;
9 uint32_t anchors; 13 uint32_t anchors;
10 int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset 14 int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset
diff --git a/meson.build b/meson.build
index 756857b7..1043e4ba 100644
--- a/meson.build
+++ b/meson.build
@@ -1,9 +1,9 @@
1project( 1project(
2 'sway', 2 'sway',
3 'c', 3 'c',
4 version: '1.6', 4 version: '1.10-dev',
5 license: 'MIT', 5 license: 'MIT',
6 meson_version: '>=0.58.1', 6 meson_version: '>=0.60.0',
7 default_options: [ 7 default_options: [
8 'c_std=c11', 8 'c_std=c11',
9 'warning_level=2', 9 'warning_level=2',
@@ -14,10 +14,12 @@ project(
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 ],
@@ -35,47 +37,19 @@ if is_freebsd
35 add_project_arguments('-D_C11_SOURCE', language: 'c') 37 add_project_arguments('-D_C11_SOURCE', language: 'c')
36endif 38endif
37 39
38jsonc = dependency('json-c', version: '>=0.13') 40# Execute the wlroots subproject, if any
39pcre = dependency('libpcre') 41wlroots_version = ['>=0.18.0', '<0.19.0']
40wayland_server = dependency('wayland-server') 42subproject(
41wayland_client = dependency('wayland-client')
42wayland_cursor = dependency('wayland-cursor')
43wayland_egl = dependency('wayland-egl')
44wayland_protos = dependency('wayland-protocols', version: '>=1.14')
45xkbcommon = dependency('xkbcommon')
46cairo = dependency('cairo')
47pango = dependency('pango')
48pangocairo = dependency('pangocairo')
49gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
50pixman = dependency('pixman-1')
51glesv2 = dependency('glesv2')
52libevdev = dependency('libevdev')
53libinput = dependency('libinput', version: '>=1.6.0')
54xcb = dependency('xcb', required: get_option('xwayland'))
55drm_full = dependency('libdrm') # only needed for drm_fourcc.h
56drm = drm_full.partial_dependency(compile_args: true, includes: true)
57libudev = dependency('libudev')
58bash_comp = dependency('bash-completion', required: false)
59fish_comp = dependency('fish', required: false)
60math = cc.find_library('m')
61rt = cc.find_library('rt')
62
63# Try first to find wlroots as a subproject, then as a system dependency
64wlroots_version = ['>=0.15.0', '<0.16.0']
65wlroots_proj = subproject(
66 'wlroots', 43 'wlroots',
67 default_options: ['examples=false'], 44 default_options: ['examples=false'],
68 required: false, 45 required: false,
69 version: wlroots_version, 46 version: wlroots_version,
70) 47)
71if wlroots_proj.found() 48wlroots = dependency('wlroots', version: wlroots_version)
72 wlroots = wlroots_proj.get_variable('wlroots')
73else
74 wlroots = dependency('wlroots', version: wlroots_version)
75endif
76
77wlroots_features = { 49wlroots_features = {
78 'xwayland': false, 50 'xwayland': false,
51 'libinput_backend': false,
52 'session': false,
79} 53}
80foreach name, _ : wlroots_features 54foreach name, _ : wlroots_features
81 var_name = 'have_' + name.underscorify() 55 var_name = 'have_' + name.underscorify()
@@ -86,25 +60,42 @@ endforeach
86if get_option('xwayland').enabled() and not wlroots_features['xwayland'] 60if get_option('xwayland').enabled() and not wlroots_features['xwayland']
87 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') 61 error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support')
88endif 62endif
89have_xwayland = xcb.found() and wlroots_features['xwayland'] 63
64null_dep = dependency('', required: false)
65
66jsonc = dependency('json-c', version: '>=0.13')
67pcre2 = dependency('libpcre2-8')
68wayland_server = dependency('wayland-server', version: '>=1.21.0')
69wayland_client = dependency('wayland-client')
70wayland_cursor = dependency('wayland-cursor')
71wayland_protos = dependency('wayland-protocols', version: '>=1.24')
72xkbcommon = dependency('xkbcommon', version: '>=1.5.0')
73cairo = dependency('cairo')
74pango = dependency('pango')
75pangocairo = dependency('pangocairo')
76gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
77pixman = dependency('pixman-1')
78libevdev = dependency('libevdev')
79libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.21.0') : null_dep
80xcb = dependency('xcb', required: get_option('xwayland'))
81drm = dependency('libdrm')
82libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep
83math = cc.find_library('m')
84rt = cc.find_library('rt')
85xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland'))
86threads = dependency('threads') # for pthread_setschedparam
87
88have_xwayland = xcb.found() and xcb_icccm.found() and wlroots_features['xwayland']
90 89
91if get_option('sd-bus-provider') == 'auto' 90if get_option('sd-bus-provider') == 'auto'
92 if not get_option('tray').disabled() 91 if not get_option('tray').disabled()
93 assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto') 92 assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto')
94 endif 93 endif
95 sdbus = dependency('libsystemd', 94 sdbus = dependency(['libsystemd', 'libelogind'],
96 required: false, 95 required: false,
97 version: '>=239', 96 version: '>=239',
98 not_found_message: 'libsystemd not found, trying libelogind',
99 ) 97 )
100 if not sdbus.found() 98 if not sdbus.found()
101 sdbus = dependency('libelogind',
102 required: false,
103 version: '>=239',
104 not_found_message: 'libelogind not found, trying basu',
105 )
106 endif
107 if not sdbus.found()
108 sdbus = dependency('basu', required: false) 99 sdbus = dependency('basu', required: false)
109 endif 100 endif
110else 101else
@@ -125,11 +116,15 @@ conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd
125conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') 116conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind')
126conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') 117conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu')
127conf_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))
128 124
129scdoc = 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'))
130if scdoc.found() 126if scdoc.found()
131 scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true) 127 scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true)
132 sh = find_program('sh', native: true)
133 mandir = get_option('mandir') 128 mandir = get_option('mandir')
134 man_files = [ 129 man_files = [
135 'sway/sway.1.scd', 130 'sway/sway.1.scd',
@@ -158,10 +153,10 @@ if scdoc.found()
158 output, 153 output,
159 input: filename, 154 input: filename,
160 output: output, 155 output: output,
161 command: [ 156 command: scdoc_prog,
162 sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output)
163 ],
164 install: true, 157 install: true,
158 feed: true,
159 capture: true,
165 install_dir: '@0@/man@1@'.format(mandir, section) 160 install_dir: '@0@/man@1@'.format(mandir, section)
166 ) 161 )
167 endforeach 162 endforeach
@@ -172,8 +167,8 @@ add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir
172version = '"@0@"'.format(meson.project_version()) 167version = '"@0@"'.format(meson.project_version())
173git = find_program('git', native: true, required: false) 168git = find_program('git', native: true, required: false)
174if git.found() 169if git.found()
175 git_commit = run_command([git, 'rev-parse', '--short', 'HEAD']) 170 git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false)
176 git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD']) 171 git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
177 if git_commit.returncode() == 0 and git_branch.returncode() == 0 172 if git_commit.returncode() == 0 and git_branch.returncode() == 0
178 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( 173 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format(
179 meson.project_version(), 174 meson.project_version(),
@@ -273,59 +268,7 @@ if get_option('default-wallpaper')
273 install_data(wallpaper_files, install_dir: wallpaper_install_dir) 268 install_data(wallpaper_files, install_dir: wallpaper_install_dir)
274endif 269endif
275 270
276if get_option('zsh-completions') 271subdir('completions')
277 zsh_files = files(
278 'completions/zsh/_sway',
279 'completions/zsh/_swaymsg',
280 )
281 zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions')
282
283 install_data(zsh_files, install_dir: zsh_install_dir)
284endif
285
286if get_option('bash-completions')
287 bash_files = files(
288 'completions/bash/sway',
289 'completions/bash/swaymsg',
290 )
291
292 if get_option('swaybar')
293 bash_files += files('completions/bash/swaybar')
294 endif
295
296 if bash_comp.found()
297 bash_install_dir = bash_comp.get_variable(
298 pkgconfig: 'completionsdir',
299 pkgconfig_define: ['datadir', datadir]
300 )
301 else
302 bash_install_dir = join_paths(datadir, 'bash-completion', 'completions')
303 endif
304
305 install_data(bash_files, install_dir: bash_install_dir)
306endif
307
308if get_option('fish-completions')
309 fish_files = files(
310 'completions/fish/sway.fish',
311 'completions/fish/swaymsg.fish',
312 )
313
314 if get_option('swaynag')
315 fish_files += files('completions/fish/swaynag.fish')
316 endif
317
318 if fish_comp.found()
319 fish_install_dir = fish_comp.get_variable(
320 pkgconfig: 'completionsdir',
321 pkgconfig_define: ['datadir', datadir]
322 )
323 else
324 fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d')
325 endif
326
327 install_data(fish_files, install_dir: fish_install_dir)
328endif
329 272
330summary({ 273summary({
331 'xwayland': have_xwayland, 274 'xwayland': have_xwayland,
@@ -333,4 +276,3 @@ summary({
333 'tray': have_tray, 276 'tray': have_tray,
334 'man-pages': scdoc.found(), 277 'man-pages': scdoc.found(),
335}, bool_yn: true) 278}, bool_yn: true)
336
diff --git a/meson_options.txt b/meson_options.txt
index 6ba67554..8d0d6509 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -6,6 +6,6 @@ option('swaybar', type: 'boolean', value: true, description: 'Enable support for
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 8c4ae0af..81edb584 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -1,80 +1,43 @@
1wl_protocol_dir = wayland_protos.get_variable(pkgconfig: '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 ['wlr-layer-shell-unstable-v1.xml'], 14 wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
19 ['idle.xml'], 15 wl_protocol_dir / 'staging/content-type/content-type-v1.xml',
20 ['wlr-input-inhibitor-unstable-v1.xml'], 16 wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
21 ['wlr-output-power-management-unstable-v1.xml'], 17 'wlr-layer-shell-unstable-v1.xml',
22] 18 'idle.xml',
23 19 'wlr-output-power-management-unstable-v1.xml',
24client_protocols = [
25 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
26 [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
27 ['wlr-layer-shell-unstable-v1.xml'],
28 ['wlr-input-inhibitor-unstable-v1.xml'],
29] 20]
30 21
31wl_protos_src = [] 22wl_protos_src = []
32wl_protos_headers = []
33 23
34foreach p : protocols 24foreach xml : protocols
35 xml = join_paths(p)
36 wl_protos_src += custom_target( 25 wl_protos_src += custom_target(
37 xml.underscorify() + '_server_c', 26 xml.underscorify() + '_c',
38 input: xml, 27 input: xml,
39 output: '@BASENAME@-protocol.c', 28 output: '@BASENAME@-protocol.c',
40 command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], 29 command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
41 ) 30 )
42 wl_protos_headers += custom_target( 31 wl_protos_src += custom_target(
43 xml.underscorify() + '_server_h', 32 xml.underscorify() + '_server_h',
44 input: xml, 33 input: xml,
45 output: '@BASENAME@-protocol.h', 34 output: '@BASENAME@-protocol.h',
46 command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], 35 command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
47 ) 36 )
48endforeach 37 wl_protos_src += custom_target(
49
50foreach p : client_protocols
51 xml = join_paths(p)
52 wl_protos_headers += custom_target(
53 xml.underscorify() + '_client_h', 38 xml.underscorify() + '_client_h',
54 input: xml, 39 input: xml,
55 output: '@BASENAME@-client-protocol.h', 40 output: '@BASENAME@-client-protocol.h',
56 command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], 41 command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
57 ) 42 )
58endforeach 43endforeach
59
60lib_client_protos = static_library(
61 'client_protos',
62 wl_protos_src + wl_protos_headers,
63 dependencies: wayland_client.partial_dependency(compile_args: true),
64)
65
66client_protos = declare_dependency(
67 link_with: lib_client_protos,
68 sources: wl_protos_headers,
69)
70
71lib_server_protos = static_library(
72 'server_protos',
73 wl_protos_src + wl_protos_headers,
74 dependencies: wayland_server.partial_dependency(compile_args: true),
75)
76
77server_protos = declare_dependency(
78 link_with: lib_server_protos,
79 sources: wl_protos_headers,
80)
diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml
deleted file mode 100644
index b62d1bb4..00000000
--- a/protocols/wlr-input-inhibitor-unstable-v1.xml
+++ /dev/null
@@ -1,67 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="wlr_input_inhibit_unstable_v1">
3 <copyright>
4 Copyright © 2018 Drew DeVault
5
6 Permission to use, copy, modify, distribute, and sell this
7 software and its documentation for any purpose is hereby granted
8 without fee, provided that the above copyright notice appear in
9 all copies and that both that copyright notice and this permission
10 notice appear in supporting documentation, and that the name of
11 the copyright holders not be used in advertising or publicity
12 pertaining to distribution of the software without specific,
13 written prior permission. The copyright holders make no
14 representations about the suitability of this software for any
15 purpose. It is provided "as is" without express or implied
16 warranty.
17
18 THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
19 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
23 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
25 THIS SOFTWARE.
26 </copyright>
27
28 <interface name="zwlr_input_inhibit_manager_v1" version="1">
29 <description summary="inhibits input events to other clients">
30 Clients can use this interface to prevent input events from being sent to
31 any surfaces but its own, which is useful for example in lock screen
32 software. It is assumed that access to this interface will be locked down
33 to whitelisted clients by the compositor.
34 </description>
35
36 <request name="get_inhibitor">
37 <description summary="inhibit input to other clients">
38 Activates the input inhibitor. As long as the inhibitor is active, the
39 compositor will not send input events to other clients.
40 </description>
41 <arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/>
42 </request>
43
44 <enum name="error">
45 <entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/>
46 </enum>
47 </interface>
48
49 <interface name="zwlr_input_inhibitor_v1" version="1">
50 <description summary="inhibits input to other clients">
51 While this resource exists, input to clients other than the owner of the
52 inhibitor resource will not receive input events. The client that owns
53 this resource will receive all input events normally. The compositor will
54 also disable all of its own input processing (such as keyboard shortcuts)
55 while the inhibitor is active.
56
57 The compositor may continue to send input events to selected clients,
58 such as an on-screen keyboard (via the input-method protocol).
59 </description>
60
61 <request name="destroy" type="destructor">
62 <description summary="destroy the input inhibitor object">
63 Destroy the inhibitor and allow other clients to receive input.
64 </description>
65 </request>
66 </interface>
67</protocol>
diff --git a/release.sh b/release.sh
new file mode 100755
index 00000000..62baf415
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,31 @@
1#!/bin/sh -eu
2
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 b09a04c7..8d003dfa 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <ctype.h> 1#include <ctype.h>
3#include <stdarg.h> 2#include <stdarg.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -46,11 +45,13 @@ static const struct cmd_handler handlers[] = {
46 { "assign", cmd_assign }, 45 { "assign", cmd_assign },
47 { "bar", cmd_bar }, 46 { "bar", cmd_bar },
48 { "bindcode", cmd_bindcode }, 47 { "bindcode", cmd_bindcode },
48 { "bindgesture", cmd_bindgesture },
49 { "bindswitch", cmd_bindswitch }, 49 { "bindswitch", cmd_bindswitch },
50 { "bindsym", cmd_bindsym }, 50 { "bindsym", cmd_bindsym },
51 { "client.background", cmd_client_noop }, 51 { "client.background", cmd_client_noop },
52 { "client.focused", cmd_client_focused }, 52 { "client.focused", cmd_client_focused },
53 { "client.focused_inactive", cmd_client_focused_inactive }, 53 { "client.focused_inactive", cmd_client_focused_inactive },
54 { "client.focused_tab_title", cmd_client_focused_tab_title },
54 { "client.placeholder", cmd_client_noop }, 55 { "client.placeholder", cmd_client_noop },
55 { "client.unfocused", cmd_client_unfocused }, 56 { "client.unfocused", cmd_client_unfocused },
56 { "client.urgent", cmd_client_urgent }, 57 { "client.urgent", cmd_client_urgent },
@@ -91,6 +92,7 @@ static const struct cmd_handler handlers[] = {
91 { "titlebar_border_thickness", cmd_titlebar_border_thickness }, 92 { "titlebar_border_thickness", cmd_titlebar_border_thickness },
92 { "titlebar_padding", cmd_titlebar_padding }, 93 { "titlebar_padding", cmd_titlebar_padding },
93 { "unbindcode", cmd_unbindcode }, 94 { "unbindcode", cmd_unbindcode },
95 { "unbindgesture", cmd_unbindgesture },
94 { "unbindswitch", cmd_unbindswitch }, 96 { "unbindswitch", cmd_unbindswitch },
95 { "unbindsym", cmd_unbindsym }, 97 { "unbindsym", cmd_unbindsym },
96 { "workspace", cmd_workspace }, 98 { "workspace", cmd_workspace },
@@ -101,6 +103,7 @@ static const struct cmd_handler handlers[] = {
101static const struct cmd_handler config_handlers[] = { 103static const struct cmd_handler config_handlers[] = {
102 { "default_orientation", cmd_default_orientation }, 104 { "default_orientation", cmd_default_orientation },
103 { "include", cmd_include }, 105 { "include", cmd_include },
106 { "primary_selection", cmd_primary_selection },
104 { "swaybg_command", cmd_swaybg_command }, 107 { "swaybg_command", cmd_swaybg_command },
105 { "swaynag_command", cmd_swaynag_command }, 108 { "swaynag_command", cmd_swaynag_command },
106 { "workspace_layout", cmd_workspace_layout }, 109 { "workspace_layout", cmd_workspace_layout },
@@ -144,7 +147,7 @@ static int handler_compare(const void *_a, const void *_b) {
144 return strcasecmp(a->command, b->command); 147 return strcasecmp(a->command, b->command);
145} 148}
146 149
147const struct cmd_handler *find_handler(char *line, 150const struct cmd_handler *find_handler(const char *line,
148 const struct cmd_handler *handlers, size_t handlers_size) { 151 const struct cmd_handler *handlers, size_t handlers_size) {
149 if (!handlers || !handlers_size) { 152 if (!handlers || !handlers_size) {
150 return NULL; 153 return NULL;
@@ -377,10 +380,13 @@ struct cmd_results *config_command(char *exec, char **new_block) {
377 sway_log(SWAY_INFO, "Config command: %s", exec); 380 sway_log(SWAY_INFO, "Config command: %s", exec);
378 const struct cmd_handler *handler = find_core_handler(argv[0]); 381 const struct cmd_handler *handler = find_core_handler(argv[0]);
379 if (!handler || !handler->handle) { 382 if (!handler || !handler->handle) {
380 const char *error = handler 383 if (handler) {
381 ? "Command '%s' is shimmed, but unimplemented" 384 results = cmd_results_new(CMD_INVALID,
382 : "Unknown/invalid command '%s'"; 385 "Command '%s' is shimmed, but unimplemented", argv[0]);
383 results = cmd_results_new(CMD_INVALID, error, argv[0]); 386 } else {
387 results = cmd_results_new(CMD_INVALID,
388 "Unknown/invalid command '%s'", argv[0]);
389 }
384 goto cleanup; 390 goto cleanup;
385 } 391 }
386 392
@@ -406,6 +412,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
406 && handler->handle != cmd_bindsym 412 && handler->handle != cmd_bindsym
407 && handler->handle != cmd_bindcode 413 && handler->handle != cmd_bindcode
408 && handler->handle != cmd_bindswitch 414 && handler->handle != cmd_bindswitch
415 && handler->handle != cmd_bindgesture
409 && handler->handle != cmd_set 416 && handler->handle != cmd_set
410 && handler->handle != cmd_for_window 417 && handler->handle != cmd_for_window
411 && (*argv[i] == '\"' || *argv[i] == '\'')) { 418 && (*argv[i] == '\"' || *argv[i] == '\'')) {
@@ -465,34 +472,6 @@ struct cmd_results *config_commands_command(char *exec) {
465 goto cleanup; 472 goto cleanup;
466 } 473 }
467 474
468 enum command_context context = 0;
469
470 struct {
471 char *name;
472 enum command_context context;
473 } context_names[] = {
474 { "config", CONTEXT_CONFIG },
475 { "binding", CONTEXT_BINDING },
476 { "ipc", CONTEXT_IPC },
477 { "criteria", CONTEXT_CRITERIA },
478 { "all", CONTEXT_ALL },
479 };
480
481 for (int i = 1; i < argc; ++i) {
482 size_t j;
483 for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) {
484 if (strcmp(context_names[j].name, argv[i]) == 0) {
485 break;
486 }
487 }
488 if (j == sizeof(context_names) / sizeof(context_names[0])) {
489 results = cmd_results_new(CMD_INVALID,
490 "Invalid command context %s", argv[i]);
491 goto cleanup;
492 }
493 context |= context_names[j].context;
494 }
495
496 results = cmd_results_new(CMD_SUCCESS, NULL); 475 results = cmd_results_new(CMD_SUCCESS, NULL);
497 476
498cleanup: 477cleanup:
@@ -509,14 +488,10 @@ struct cmd_results *cmd_results_new(enum cmd_status status,
509 } 488 }
510 results->status = status; 489 results->status = status;
511 if (format) { 490 if (format) {
512 char *error = malloc(256);
513 va_list args; 491 va_list args;
514 va_start(args, format); 492 va_start(args, format);
515 if (error) { 493 results->error = vformat_str(format, args);
516 vsnprintf(error, 256, format, args);
517 }
518 va_end(args); 494 va_end(args);
519 results->error = error;
520 } else { 495 } else {
521 results->error = NULL; 496 results->error = NULL;
522 } 497 }
diff --git a/sway/commands/assign.c b/sway/commands/assign.c
index 976bc3cc..bf95cf00 100644
--- a/sway/commands/assign.c
+++ b/sway/commands/assign.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -17,7 +16,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) {
17 char *err_str = NULL; 16 char *err_str = NULL;
18 struct criteria *criteria = criteria_parse(argv[0], &err_str); 17 struct criteria *criteria = criteria_parse(argv[0], &err_str);
19 if (!criteria) { 18 if (!criteria) {
20 error = cmd_results_new(CMD_INVALID, err_str); 19 error = cmd_results_new(CMD_INVALID, "%s", err_str);
21 free(err_str); 20 free(err_str);
22 return error; 21 return error;
23 } 22 }
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index 8571d282..635e895b 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
@@ -73,12 +72,10 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
73 } 72 }
74 ++argv; --argc; 73 ++argv; --argc;
75 } else if (config->reading && !config->current_bar) { 74 } else if (config->reading && !config->current_bar) {
76 int len = snprintf(NULL, 0, "bar-%d", config->bars->length) + 1; 75 id = format_str("bar-%d", config->bars->length);
77 id = malloc(len * sizeof(char));
78 if (!id) { 76 if (!id) {
79 return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id"); 77 return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id");
80 } 78 }
81 snprintf(id, len, "bar-%d", config->bars->length);
82 } else if (!config->reading && strcmp(argv[0], "mode") != 0 && 79 } else if (!config->reading && strcmp(argv[0], "mode") != 0 &&
83 strcmp(argv[0], "hidden_state") != 0) { 80 strcmp(argv[0], "hidden_state") != 0) {
84 if (is_subcommand(argv[0])) { 81 if (is_subcommand(argv[0])) {
diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c
index b4b5bc45..8a837e3f 100644
--- a/sway/commands/bar/bind.c
+++ b/sway/commands/bar/bind.c
@@ -96,7 +96,7 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code,
96 } 96 }
97 if (message) { 97 if (message) {
98 free_bar_binding(binding); 98 free_bar_binding(binding);
99 error = cmd_results_new(CMD_INVALID, message); 99 error = cmd_results_new(CMD_INVALID, "%s", message);
100 free(message); 100 free(message);
101 return error; 101 return error;
102 } else if (!binding->button) { 102 } else if (!binding->button) {
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c
index 891c87af..0c074679 100644
--- a/sway/commands/bar/font.c
+++ b/sway/commands/bar/font.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 1f08a5d2..7b38831e 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -54,7 +53,7 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
54 } 53 }
55 54
56 const char *state = argv[0]; 55 const char *state = argv[0];
57 if (config->reading) { 56 if (config->current_bar) {
58 error = bar_set_hidden_state(config->current_bar, state); 57 error = bar_set_hidden_state(config->current_bar, state);
59 } else { 58 } else {
60 const char *id = argc == 2 ? argv[1] : NULL; 59 const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c
index 6ac07843..fee21709 100644
--- a/sway/commands/bar/icon_theme.c
+++ b/sway/commands/bar/icon_theme.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "config.h" 2#include "config.h"
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
index a9a61743..46cf4ca9 100644
--- a/sway/commands/bar/id.c
+++ b/sway/commands/bar/id.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 8b3fb275..d69e910b 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -58,7 +57,7 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
58 } 57 }
59 58
60 const char *mode = argv[0]; 59 const char *mode = argv[0];
61 if (config->reading) { 60 if (config->current_bar) {
62 error = bar_set_mode(config->current_bar, mode); 61 error = bar_set_mode(config->current_bar, mode);
63 } else { 62 } else {
64 const char *id = argc == 2 ? argv[1] : NULL; 63 const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c
index cac1d056..51730176 100644
--- a/sway/commands/bar/output.c
+++ b/sway/commands/bar/output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c
index b207de0b..94f530ec 100644
--- a/sway/commands/bar/position.c
+++ b/sway/commands/bar/position.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c
index 6737d4d2..50e9a873 100644
--- a/sway/commands/bar/separator_symbol.c
+++ b/sway/commands/bar/separator_symbol.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/tray_bind.c b/sway/commands/bar/tray_bind.c
index 243834ba..3dc9bc4c 100644
--- a/sway/commands/bar/tray_bind.c
+++ b/sway/commands/bar/tray_bind.c
@@ -26,7 +26,7 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) {
26 } 26 }
27 if (message) { 27 if (message) {
28 free(binding); 28 free(binding);
29 error = cmd_results_new(CMD_INVALID, message); 29 error = cmd_results_new(CMD_INVALID, "%s", message);
30 free(message); 30 free(message);
31 return error; 31 return error;
32 } else if (!binding->button) { 32 } else if (!binding->button) {
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c
index eb3b486e..679facf7 100644
--- a/sway/commands/bar/tray_output.c
+++ b/sway/commands/bar/tray_output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "config.h" 2#include "config.h"
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index 4c67b3ce..268f2855 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <libevdev/libevdev.h> 1#include <libevdev/libevdev.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <string.h> 3#include <string.h>
@@ -47,7 +46,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a,
47 if (binding_a->type != binding_b->type) { 46 if (binding_a->type != binding_b->type) {
48 return false; 47 return false;
49 } 48 }
50 if (binding_a->state != binding_b->state) { 49 if (binding_a->trigger != binding_b->trigger) {
51 return false; 50 return false;
52 } 51 }
53 if ((binding_a->flags & BINDING_LOCKED) != 52 if ((binding_a->flags & BINDING_LOCKED) !=
@@ -127,7 +126,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
127 if (!button) { 126 if (!button) {
128 if (message) { 127 if (message) {
129 struct cmd_results *error = 128 struct cmd_results *error =
130 cmd_results_new(CMD_INVALID, message); 129 cmd_results_new(CMD_INVALID, "%s", message);
131 free(message); 130 free(message);
132 return error; 131 return error;
133 } else { 132 } else {
@@ -143,7 +142,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
143 if (!button) { 142 if (!button) {
144 if (message) { 143 if (message) {
145 struct cmd_results *error = 144 struct cmd_results *error =
146 cmd_results_new(CMD_INVALID, message); 145 cmd_results_new(CMD_INVALID, "%s", message);
147 free(message); 146 free(message);
148 return error; 147 return error;
149 } else { 148 } else {
@@ -182,7 +181,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
182 uint32_t button = get_mouse_bindsym(name, &message); 181 uint32_t button = get_mouse_bindsym(name, &message);
183 if (message) { 182 if (message) {
184 struct cmd_results *error = 183 struct cmd_results *error =
185 cmd_results_new(CMD_INVALID, message); 184 cmd_results_new(CMD_INVALID, "%s", message);
186 free(message); 185 free(message);
187 return error; 186 return error;
188 } else if (button) { 187 } else if (button) {
@@ -372,6 +371,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
372 strlen("--input-device=")) == 0) { 371 strlen("--input-device=")) == 0) {
373 free(binding->input); 372 free(binding->input);
374 binding->input = strdup(argv[0] + strlen("--input-device=")); 373 binding->input = strdup(argv[0] + strlen("--input-device="));
374 strip_quotes(binding->input);
375 } else if (strcmp("--no-warn", argv[0]) == 0) { 375 } else if (strcmp("--no-warn", argv[0]) == 0) {
376 warn = false; 376 warn = false;
377 } else if (strcmp("--no-repeat", argv[0]) == 0) { 377 } else if (strcmp("--no-repeat", argv[0]) == 0) {
@@ -538,7 +538,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
538 free_switch_binding(binding); 538 free_switch_binding(binding);
539 return cmd_results_new(CMD_FAILURE, 539 return cmd_results_new(CMD_FAILURE,
540 "Invalid %s command (expected binding with the form " 540 "Invalid %s command (expected binding with the form "
541 "<switch>:<state>)", bindtype, argc); 541 "<switch>:<state>)", bindtype);
542 } 542 }
543 if (strcmp(split->items[0], "tablet") == 0) { 543 if (strcmp(split->items[0], "tablet") == 0) {
544 binding->type = WLR_SWITCH_TYPE_TABLET_MODE; 544 binding->type = WLR_SWITCH_TYPE_TABLET_MODE;
@@ -548,20 +548,21 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
548 free_switch_binding(binding); 548 free_switch_binding(binding);
549 return cmd_results_new(CMD_FAILURE, 549 return cmd_results_new(CMD_FAILURE,
550 "Invalid %s command (expected switch binding: " 550 "Invalid %s command (expected switch binding: "
551 "unknown switch %s)", bindtype, split->items[0]); 551 "unknown switch %s)", bindtype,
552 (const char *)split->items[0]);
552 } 553 }
553 if (strcmp(split->items[1], "on") == 0) { 554 if (strcmp(split->items[1], "on") == 0) {
554 binding->state = WLR_SWITCH_STATE_ON; 555 binding->trigger = SWAY_SWITCH_TRIGGER_ON;
555 } else if (strcmp(split->items[1], "off") == 0) { 556 } else if (strcmp(split->items[1], "off") == 0) {
556 binding->state = WLR_SWITCH_STATE_OFF; 557 binding->trigger = SWAY_SWITCH_TRIGGER_OFF;
557 } else if (strcmp(split->items[1], "toggle") == 0) { 558 } else if (strcmp(split->items[1], "toggle") == 0) {
558 binding->state = WLR_SWITCH_STATE_TOGGLE; 559 binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE;
559 } else { 560 } else {
560 free_switch_binding(binding); 561 free_switch_binding(binding);
561 return cmd_results_new(CMD_FAILURE, 562 return cmd_results_new(CMD_FAILURE,
562 "Invalid %s command " 563 "Invalid %s command "
563 "(expected switch state: unknown state %d)", 564 "(expected switch state: unknown state %s)",
564 bindtype, split->items[0]); 565 bindtype, (const char *)split->items[1]);
565 } 566 }
566 list_free_items_and_destroy(split); 567 list_free_items_and_destroy(split);
567 568
diff --git a/sway/commands/client.c b/sway/commands/client.c
index dd0694df..fd2ac7a8 100644
--- a/sway/commands/client.c
+++ b/sway/commands/client.c
@@ -5,9 +5,8 @@
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "util.h" 6#include "util.h"
7 7
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,
@@ -18,6 +17,12 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
18 return error; 17 return error;
19 } 18 }
20 19
20 if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) {
21 sway_log(SWAY_ERROR,
22 "Warning: indicator and child_border colors have no effect for %s",
23 cmd_name);
24 }
25
21 struct border_colors colors = {0}; 26 struct border_colors colors = {0};
22 const char *ind_hex = argc > 3 ? argv[3] : default_indicator; 27 const char *ind_hex = argc > 3 ? argv[3] : default_indicator;
23 const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background 28 const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background
@@ -45,12 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
45 memcpy(class, &colors, sizeof(struct border_colors)); 50 memcpy(class, &colors, sizeof(struct border_colors));
46 51
47 if (config->active) { 52 if (config->active) {
48 root_for_each_container(rebuild_textures_iterator, NULL); 53 root_for_each_container(container_update_iterator, NULL);
49
50 for (int i = 0; i < root->outputs->length; ++i) {
51 struct sway_output *output = root->outputs->items[i];
52 output_damage_whole(output);
53 }
54 } 54 }
55 55
56 return cmd_results_new(CMD_SUCCESS, NULL); 56 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -80,3 +80,13 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) {
80 sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); 80 sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]);
81 return cmd_results_new(CMD_SUCCESS, NULL); 81 return cmd_results_new(CMD_SUCCESS, NULL);
82} 82}
83
84struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) {
85 struct cmd_results *result = handle_command(argc, argv,
86 "client.focused_tab_title",
87 &config->border_colors.focused_tab_title, "#2e9ef4ff");
88 if (result && result->status == CMD_SUCCESS) {
89 config->has_focused_tab_title = true;
90 }
91 return result;
92}
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index fce337d5..8bc1048c 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <stdint.h> 2#include <stdint.h>
4#include <string.h> 3#include <string.h>
@@ -7,6 +6,8 @@
7#include <signal.h> 6#include <signal.h>
8#include "sway/commands.h" 7#include "sway/commands.h"
9#include "sway/config.h" 8#include "sway/config.h"
9#include "sway/server.h"
10#include "sway/desktop/launcher.h"
10#include "sway/tree/container.h" 11#include "sway/tree/container.h"
11#include "sway/tree/root.h" 12#include "sway/tree/root.h"
12#include "sway/tree/workspace.h" 13#include "sway/tree/workspace.h"
@@ -24,11 +25,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
24 return error; 25 return error;
25} 26}
26 27
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
27struct cmd_results *cmd_exec_process(int argc, char **argv) { 38struct cmd_results *cmd_exec_process(int argc, char **argv) {
28 struct cmd_results *error = NULL; 39 struct cmd_results *error = NULL;
29 char *cmd = NULL; 40 char *cmd = NULL;
41 bool no_startup_id = false;
30 if (strcmp(argv[0], "--no-startup-id") == 0) { 42 if (strcmp(argv[0], "--no-startup-id") == 0) {
31 sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); 43 no_startup_id = true;
32 --argc; ++argv; 44 --argc; ++argv;
33 if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { 45 if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) {
34 return error; 46 return error;
@@ -50,9 +62,11 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
50 } 62 }
51 63
52 pid_t pid, child; 64 pid_t pid, child;
65 struct launcher_ctx *ctx = launcher_ctx_create_internal();
53 // Fork process 66 // Fork process
54 if ((pid = fork()) == 0) { 67 if ((pid = fork()) == 0) {
55 // Fork child process again 68 // Fork child process again
69 restore_nofile_limit();
56 setsid(); 70 setsid();
57 sigset_t set; 71 sigset_t set;
58 sigemptyset(&set); 72 sigemptyset(&set);
@@ -61,6 +75,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
61 close(fd[0]); 75 close(fd[0]);
62 if ((child = fork()) == 0) { 76 if ((child = fork()) == 0) {
63 close(fd[1]); 77 close(fd[1]);
78 if (ctx) {
79 export_xdga_token(ctx);
80 }
81 if (ctx && !no_startup_id) {
82 export_startup_id(ctx);
83 }
64 execlp("sh", "sh", "-c", cmd, (void *)NULL); 84 execlp("sh", "sh", "-c", cmd, (void *)NULL);
65 sway_log_errno(SWAY_ERROR, "execlp failed"); 85 sway_log_errno(SWAY_ERROR, "execlp failed");
66 _exit(1); 86 _exit(1);
@@ -88,8 +108,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
88 waitpid(pid, NULL, 0); 108 waitpid(pid, NULL, 0);
89 if (child > 0) { 109 if (child > 0) {
90 sway_log(SWAY_DEBUG, "Child process created with pid %d", child); 110 sway_log(SWAY_DEBUG, "Child process created with pid %d", child);
91 root_record_workspace_pid(child); 111 if (ctx != NULL) {
112 sway_log(SWAY_DEBUG, "Recording workspace for process %d", child);
113 ctx->pid = child;
114 }
92 } else { 115 } else {
116 launcher_ctx_destroy(ctx);
93 return cmd_results_new(CMD_FAILURE, "Second fork() failed"); 117 return cmd_results_new(CMD_FAILURE, "Second fork() failed");
94 } 118 }
95 119
diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c
index 3a1d606a..e8c24ace 100644
--- a/sway/commands/floating_minmax_size.c
+++ b/sway/commands/floating_minmax_size.c
@@ -23,16 +23,16 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
23 char *err; 23 char *err;
24 int width = (int)strtol(argv[0], &err, 10); 24 int width = (int)strtol(argv[0], &err, 10);
25 if (*err) { 25 if (*err) {
26 return cmd_results_new(CMD_INVALID, cmd_name, usage); 26 return cmd_results_new(CMD_INVALID, "%s", usage);
27 } 27 }
28 28
29 if (strcmp(argv[1], "x") != 0) { 29 if (strcmp(argv[1], "x") != 0) {
30 return cmd_results_new(CMD_INVALID, cmd_name, usage); 30 return cmd_results_new(CMD_INVALID, "%s", usage);
31 } 31 }
32 32
33 int height = (int)strtol(argv[2], &err, 10); 33 int height = (int)strtol(argv[2], &err, 10);
34 if (*err) { 34 if (*err) {
35 return cmd_results_new(CMD_INVALID, cmd_name, usage); 35 return cmd_results_new(CMD_INVALID, "%s", usage);
36 } 36 }
37 37
38 *config_width = width; 38 *config_width = width;
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 6771ca2f..facd82de 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -54,7 +54,7 @@ static bool get_direction_from_next_prev(struct sway_container *container,
54 } else { 54 } else {
55 return false; 55 return false;
56 } 56 }
57 57
58 return true; 58 return true;
59} 59}
60 60
@@ -267,6 +267,11 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
267 new_focus = seat_get_focus_inactive_tiling(seat, ws); 267 new_focus = seat_get_focus_inactive_tiling(seat, ws);
268 } 268 }
269 if (new_focus) { 269 if (new_focus) {
270 struct sway_container *new_focus_view =
271 seat_get_focus_inactive_view(seat, &new_focus->node);
272 if (new_focus_view) {
273 new_focus = new_focus_view;
274 }
270 seat_set_focus_container(seat, new_focus); 275 seat_set_focus_container(seat, new_focus);
271 276
272 // If we're on the floating layer and the floating container area 277 // If we're on the floating layer and the floating container area
@@ -280,7 +285,7 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
280 } 285 }
281 } else { 286 } else {
282 return cmd_results_new(CMD_FAILURE, 287 return cmd_results_new(CMD_FAILURE,
283 "Failed to find a %s container in workspace", 288 "Failed to find a %s container in workspace.",
284 floating ? "floating" : "tiling"); 289 floating ? "floating" : "tiling");
285 } 290 }
286 return cmd_results_new(CMD_SUCCESS, NULL); 291 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -290,7 +295,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
290 int argc, char **argv) { 295 int argc, char **argv) {
291 if (!argc) { 296 if (!argc) {
292 return cmd_results_new(CMD_INVALID, 297 return cmd_results_new(CMD_INVALID,
293 "Expected 'focus output <direction|name>'"); 298 "Expected 'focus output <direction|name>'.");
294 } 299 }
295 char *identifier = join_args(argv, argc); 300 char *identifier = join_args(argv, argc);
296 struct sway_output *output = output_by_name_or_id(identifier); 301 struct sway_output *output = output_by_name_or_id(identifier);
@@ -300,13 +305,13 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
300 if (!parse_direction(identifier, &direction)) { 305 if (!parse_direction(identifier, &direction)) {
301 free(identifier); 306 free(identifier);
302 return cmd_results_new(CMD_INVALID, 307 return cmd_results_new(CMD_INVALID,
303 "There is no output with that name"); 308 "There is no output with that name.");
304 } 309 }
305 struct sway_workspace *ws = seat_get_focused_workspace(seat); 310 struct sway_workspace *ws = seat_get_focused_workspace(seat);
306 if (!ws) { 311 if (!ws) {
307 free(identifier); 312 free(identifier);
308 return cmd_results_new(CMD_FAILURE, 313 return cmd_results_new(CMD_FAILURE,
309 "No focused workspace to base directions off of"); 314 "No focused workspace to base directions off of.");
310 } 315 }
311 output = output_get_in_direction(ws->output, direction); 316 output = output_get_in_direction(ws->output, direction);
312 317
@@ -370,10 +375,14 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
370 struct sway_seat *seat = config->handler_context.seat; 375 struct sway_seat *seat = config->handler_context.seat;
371 if (node->type < N_WORKSPACE) { 376 if (node->type < N_WORKSPACE) {
372 return cmd_results_new(CMD_FAILURE, 377 return cmd_results_new(CMD_FAILURE,
373 "Command 'focus' cannot be used above the workspace level"); 378 "Command 'focus' cannot be used above the workspace level.");
374 } 379 }
375 380
376 if (argc == 0 && container) { 381 if (argc == 0) {
382 if (!container) {
383 return cmd_results_new(CMD_FAILURE, "No container to focus was specified.");
384 }
385
377 if (container_is_scratchpad_hidden_or_child(container)) { 386 if (container_is_scratchpad_hidden_or_child(container)) {
378 root_scratchpad_show(container); 387 root_scratchpad_show(container);
379 } 388 }
@@ -446,7 +455,8 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
446 return cmd_results_new(CMD_FAILURE, ""); 455 return cmd_results_new(CMD_FAILURE, "");
447 } 456 }
448 struct sway_node *next_focus = NULL; 457 struct sway_node *next_focus = NULL;
449 if (container_is_floating(container)) { 458 if (container_is_floating(container) &&
459 container->pending.fullscreen_mode == FULLSCREEN_NONE) {
450 next_focus = node_get_in_direction_floating(container, seat, direction); 460 next_focus = node_get_in_direction_floating(container, seat, direction);
451 } else { 461 } else {
452 next_focus = node_get_in_direction_tiling(container, seat, direction, descend); 462 next_focus = node_get_in_direction_tiling(container, seat, direction, descend);
diff --git a/sway/commands/font.c b/sway/commands/font.c
index c54365b5..9920d03e 100644
--- a/sway/commands/font.c
+++ b/sway/commands/font.c
@@ -1,9 +1,9 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
5#include "log.h" 4#include "log.h"
6#include "stringop.h" 5#include "stringop.h"
6#include <pango/pangocairo.h>
7 7
8struct cmd_results *cmd_font(int argc, char **argv) { 8struct cmd_results *cmd_font(int argc, char **argv) {
9 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
@@ -16,12 +16,34 @@ struct cmd_results *cmd_font(int argc, char **argv) {
16 if (strncmp(font, "pango:", 6) == 0) { 16 if (strncmp(font, "pango:", 6) == 0) {
17 config->pango_markup = true; 17 config->pango_markup = true;
18 config->font = strdup(font + 6); 18 config->font = strdup(font + 6);
19 free(font);
19 } else { 20 } else {
20 config->pango_markup = false; 21 config->pango_markup = false;
21 config->font = strdup(font); 22 config->font = font;
22 } 23 }
23 24
24 free(font); 25 // Parse the font early so we can reject it if it's not valid for pango.
25 config_update_font_height(true); 26 // Also avoids re-parsing each time we render text.
27 PangoFontDescription *font_description = pango_font_description_from_string(config->font);
28
29 const char *family = pango_font_description_get_family(font_description);
30 if (family == NULL) {
31 pango_font_description_free(font_description);
32 return cmd_results_new(CMD_FAILURE, "Invalid font family.");
33 }
34
35 const gint size = pango_font_description_get_size(font_description);
36 if (size == 0) {
37 pango_font_description_free(font_description);
38 return cmd_results_new(CMD_FAILURE, "Invalid font size.");
39 }
40
41 if (config->font_description != NULL) {
42 pango_font_description_free(config->font_description);
43 }
44
45 config->font_description = font_description;
46 config_update_font_height();
47
26 return cmd_results_new(CMD_SUCCESS, NULL); 48 return cmd_results_new(CMD_SUCCESS, NULL);
27} 49}
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c
index ee9f4647..905e6776 100644
--- a/sway/commands/for_window.c
+++ b/sway/commands/for_window.c
@@ -14,7 +14,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) {
14 char *err_str = NULL; 14 char *err_str = NULL;
15 struct criteria *criteria = criteria_parse(argv[0], &err_str); 15 struct criteria *criteria = criteria_parse(argv[0], &err_str);
16 if (!criteria) { 16 if (!criteria) {
17 error = cmd_results_new(CMD_INVALID, err_str); 17 error = cmd_results_new(CMD_INVALID, "%s", err_str);
18 free(err_str); 18 free(err_str);
19 return error; 19 return error;
20 } 20 }
diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c
new file mode 100644
index 00000000..90a20716
--- /dev/null
+++ b/sway/commands/gesture.c
@@ -0,0 +1,165 @@
1#include "sway/config.h"
2
3#include "gesture.h"
4#include "log.h"
5#include "stringop.h"
6#include "sway/commands.h"
7
8void free_gesture_binding(struct sway_gesture_binding *binding) {
9 if (!binding) {
10 return;
11 }
12 free(binding->input);
13 free(binding->command);
14 free(binding);
15}
16
17/**
18 * Returns true if the bindings have the same gesture type, direction, etc
19 */
20static bool binding_gesture_equal(struct sway_gesture_binding *binding_a,
21 struct sway_gesture_binding *binding_b) {
22 if (strcmp(binding_a->input, binding_b->input) != 0) {
23 return false;
24 }
25
26 if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) {
27 return false;
28 }
29
30 if ((binding_a->flags & BINDING_EXACT) !=
31 (binding_b->flags & BINDING_EXACT)) {
32 return false;
33 }
34 return true;
35}
36
37/**
38 * Add gesture binding to config
39 */
40static struct cmd_results *gesture_binding_add(
41 struct sway_gesture_binding *binding,
42 const char *gesturecombo, bool warn) {
43 list_t *mode_bindings = config->current_mode->gesture_bindings;
44 // overwrite the binding if it already exists
45 bool overwritten = false;
46 for (int i = 0; i < mode_bindings->length; ++i) {
47 struct sway_gesture_binding *config_binding = mode_bindings->items[i];
48 if (binding_gesture_equal(binding, config_binding)) {
49 sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`",
50 gesturecombo, binding->command, config_binding->command);
51 if (warn) {
52 config_add_swaynag_warning("Overwriting binding"
53 "'%s' to `%s` from `%s`",
54 gesturecombo, binding->command,
55 config_binding->command);
56 }
57 free_gesture_binding(config_binding);
58 mode_bindings->items[i] = binding;
59 overwritten = true;
60 }
61 }
62
63 if (!overwritten) {
64 list_add(mode_bindings, binding);
65 sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`",
66 gesturecombo, binding->command);
67 }
68
69 return cmd_results_new(CMD_SUCCESS, NULL);
70}
71
72/**
73 * Remove gesture binding from config
74 */
75static struct cmd_results *gesture_binding_remove(
76 struct sway_gesture_binding *binding, const char *gesturecombo) {
77 list_t *mode_bindings = config->current_mode->gesture_bindings;
78 for (int i = 0; i < mode_bindings->length; ++i) {
79 struct sway_gesture_binding *config_binding = mode_bindings->items[i];
80 if (binding_gesture_equal(binding, config_binding)) {
81 free_gesture_binding(config_binding);
82 free_gesture_binding(binding);
83 list_del(mode_bindings, i);
84 sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture",
85 gesturecombo);
86 return cmd_results_new(CMD_SUCCESS, NULL);
87 }
88 }
89
90 free_gesture_binding(binding);
91 return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`",
92 gesturecombo);
93}
94
95/**
96 * Parse and execute bindgesture or unbindgesture command.
97 */
98static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) {
99 int minargs = 2;
100 char *bindtype = "bindgesture";
101 if (unbind) {
102 minargs--;
103 bindtype = "unbindgesture";
104 }
105
106 struct cmd_results *error = NULL;
107 if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {
108 return error;
109 }
110 struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding));
111 if (!binding) {
112 return cmd_results_new(CMD_FAILURE, "Unable to allocate binding");
113 }
114 binding->input = strdup("*");
115
116 bool warn = true;
117
118 // Handle flags
119 while (argc > 0) {
120 if (strcmp("--exact", argv[0]) == 0) {
121 binding->flags |= BINDING_EXACT;
122 } else if (strcmp("--no-warn", argv[0]) == 0) {
123 warn = false;
124 } else if (strncmp("--input-device=", argv[0],
125 strlen("--input-device=")) == 0) {
126 free(binding->input);
127 binding->input = strdup(argv[0] + strlen("--input-device="));
128 } else {
129 break;
130 }
131 argv++;
132 argc--;
133 }
134
135 if (argc < minargs) {
136 free(binding);
137 return cmd_results_new(CMD_FAILURE,
138 "Invalid %s command (expected at least %d "
139 "non-option arguments, got %d)", bindtype, minargs, argc);
140 }
141
142 char* errmsg = NULL;
143 if ((errmsg = gesture_parse(argv[0], &binding->gesture))) {
144 free(binding);
145 struct cmd_results *final = cmd_results_new(CMD_FAILURE,
146 "Invalid %s command (%s)",
147 bindtype, errmsg);
148 free(errmsg);
149 return final;
150 }
151
152 if (unbind) {
153 return gesture_binding_remove(binding, argv[0]);
154 }
155 binding->command = join_args(argv + 1, argc - 1);
156 return gesture_binding_add(binding, argv[0], warn);
157}
158
159struct cmd_results *cmd_bindgesture(int argc, char **argv) {
160 return cmd_bind_or_unbind_gesture(argc, argv, false);
161}
162
163struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
164 return cmd_bind_or_unbind_gesture(argc, argv, true);
165}
diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c
index 9a1d8445..43bd6dc8 100644
--- a/sway/commands/hide_edge_borders.c
+++ b/sway/commands/hide_edge_borders.c
@@ -20,7 +20,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
20 } 20 }
21 21
22 if (!argc) { 22 if (!argc) {
23 return cmd_results_new(CMD_INVALID, expected_syntax); 23 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
24 } 24 }
25 25
26 if (strcmp(argv[0], "none") == 0) { 26 if (strcmp(argv[0], "none") == 0) {
@@ -38,7 +38,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
38 config->hide_edge_borders = E_NONE; 38 config->hide_edge_borders = E_NONE;
39 config->hide_edge_borders_smart = ESMART_NO_GAPS; 39 config->hide_edge_borders_smart = ESMART_NO_GAPS;
40 } else { 40 } else {
41 return cmd_results_new(CMD_INVALID, expected_syntax); 41 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
42 } 42 }
43 config->hide_lone_tab = hide_lone_tab; 43 config->hide_lone_tab = hide_lone_tab;
44 44
diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c
index aebc2bf9..6125736a 100644
--- a/sway/commands/inhibit_idle.c
+++ b/sway/commands/inhibit_idle.c
@@ -41,7 +41,7 @@ struct cmd_results *cmd_inhibit_idle(int argc, char **argv) {
41 sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); 41 sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor);
42 } else { 42 } else {
43 inhibitor->mode = mode; 43 inhibitor->mode = mode;
44 sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); 44 sway_idle_inhibit_v1_check_active();
45 } 45 }
46 } else if (!clear) { 46 } else if (!clear) {
47 sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); 47 sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode);
diff --git a/sway/commands/input.c b/sway/commands/input.c
index 77acb671..306c40f7 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -14,6 +14,7 @@ static const struct cmd_handler input_handlers[] = {
14 { "drag", input_cmd_drag }, 14 { "drag", input_cmd_drag },
15 { "drag_lock", input_cmd_drag_lock }, 15 { "drag_lock", input_cmd_drag_lock },
16 { "dwt", input_cmd_dwt }, 16 { "dwt", input_cmd_dwt },
17 { "dwtp", input_cmd_dwtp },
17 { "events", input_cmd_events }, 18 { "events", input_cmd_events },
18 { "left_handed", input_cmd_left_handed }, 19 { "left_handed", input_cmd_left_handed },
19 { "map_from_region", input_cmd_map_from_region }, 20 { "map_from_region", input_cmd_map_from_region },
@@ -24,7 +25,9 @@ static const struct cmd_handler input_handlers[] = {
24 { "pointer_accel", input_cmd_pointer_accel }, 25 { "pointer_accel", input_cmd_pointer_accel },
25 { "repeat_delay", input_cmd_repeat_delay }, 26 { "repeat_delay", input_cmd_repeat_delay },
26 { "repeat_rate", input_cmd_repeat_rate }, 27 { "repeat_rate", input_cmd_repeat_rate },
28 { "rotation_angle", input_cmd_rotation_angle },
27 { "scroll_button", input_cmd_scroll_button }, 29 { "scroll_button", input_cmd_scroll_button },
30 { "scroll_button_lock", input_cmd_scroll_button_lock },
28 { "scroll_factor", input_cmd_scroll_factor }, 31 { "scroll_factor", input_cmd_scroll_factor },
29 { "scroll_method", input_cmd_scroll_method }, 32 { "scroll_method", input_cmd_scroll_method },
30 { "tap", input_cmd_tap }, 33 { "tap", input_cmd_tap },
diff --git a/sway/commands/input/calibration_matrix.c b/sway/commands/input/calibration_matrix.c
index 38749fbb..53fe2c35 100644
--- a/sway/commands/input/calibration_matrix.c
+++ b/sway/commands/input/calibration_matrix.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/dwtp.c b/sway/commands/input/dwtp.c
new file mode 100644
index 00000000..232e2b26
--- /dev/null
+++ b/sway/commands/input/dwtp.c
@@ -0,0 +1,25 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/config.h"
4#include "sway/commands.h"
5#include "sway/input/input-manager.h"
6#include "util.h"
7
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 d6548a68..ecac8e6c 100644
--- a/sway/commands/input/xkb_switch_layout.c
+++ b/sway/commands/input/xkb_switch_layout.c
@@ -1,10 +1,15 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
2#include <wlr/interfaces/wlr_keyboard.h>
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "log.h" 6#include "log.h"
7 7
8struct xkb_switch_layout_action {
9 struct wlr_keyboard *keyboard;
10 xkb_layout_index_t layout;
11};
12
8static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { 13static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) {
9 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); 14 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
10 if (idx >= num_layouts) { 15 if (idx >= num_layouts) {
@@ -28,10 +33,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) {
28 return layout_idx; 33 return layout_idx;
29} 34}
30 35
31static void switch_layout_relative(struct wlr_keyboard *kbd, int dir) { 36static xkb_layout_index_t get_layout_relative(struct wlr_keyboard *kbd, int dir) {
32 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); 37 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
33 xkb_layout_index_t idx = get_current_layout_index(kbd); 38 xkb_layout_index_t idx = get_current_layout_index(kbd);
34 switch_layout(kbd, (idx + num_layouts + dir) % num_layouts); 39 return (idx + num_layouts + dir) % num_layouts;
35} 40}
36 41
37struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { 42struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
@@ -66,6 +71,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
66 relative = 0; 71 relative = 0;
67 } 72 }
68 73
74 struct xkb_switch_layout_action *actions = calloc(
75 wl_list_length(&server.input->devices),
76 sizeof(struct xkb_switch_layout_action));
77 size_t actions_len = 0;
78
79 if (!actions) {
80 return cmd_results_new(CMD_FAILURE, "Unable to allocate actions");
81 }
82
83 /* Calculate new indexes first because switching a layout in one
84 keyboard may result in a change on other keyboards as well because
85 of keyboard groups. */
69 struct sway_input_device *dev; 86 struct sway_input_device *dev;
70 wl_list_for_each(dev, &server.input->devices, link) { 87 wl_list_for_each(dev, &server.input->devices, link) {
71 if (strcmp(ic->identifier, "*") != 0 && 88 if (strcmp(ic->identifier, "*") != 0 &&
@@ -76,12 +93,22 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
76 if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { 93 if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
77 continue; 94 continue;
78 } 95 }
96
97 struct xkb_switch_layout_action *action =
98 &actions[actions_len++];
99
100 action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device);
79 if (relative) { 101 if (relative) {
80 switch_layout_relative(dev->wlr_device->keyboard, relative); 102 action->layout = get_layout_relative(action->keyboard, relative);
81 } else { 103 } else {
82 switch_layout(dev->wlr_device->keyboard, layout); 104 action->layout = layout;
83 } 105 }
84 } 106 }
85 107
108 for (size_t i = 0; i < actions_len; i++) {
109 switch_layout(actions[i].keyboard, actions[i].layout);
110 }
111 free(actions);
112
86 return cmd_results_new(CMD_SUCCESS, NULL); 113 return cmd_results_new(CMD_SUCCESS, NULL);
87} 114}
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index d0e21d77..2d14ea9c 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 2ba61b38..12ce4839 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -153,7 +153,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
153 workspace->output); 153 workspace->output);
154 } 154 }
155 if (new_layout == L_NONE) { 155 if (new_layout == L_NONE) {
156 return cmd_results_new(CMD_INVALID, expected_syntax); 156 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
157 } 157 }
158 if (new_layout != old_layout) { 158 if (new_layout != old_layout) {
159 if (container) { 159 if (container) {
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
index aa5f185c..2bfc86b3 100644
--- a/sway/commands/mark.c
+++ b/sway/commands/mark.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -59,7 +58,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
59 } 58 }
60 59
61 free(mark); 60 free(mark);
62 container_update_marks_textures(container); 61 container_update_marks(container);
63 if (container->view) { 62 if (container->view) {
64 view_execute_criteria(container->view); 63 view_execute_criteria(container->view);
65 } 64 }
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index e23e4ee4..b3216967 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -11,10 +10,12 @@
11// Must be in order for the bsearch 10// Must be in order for the bsearch
12static const struct cmd_handler mode_handlers[] = { 11static const struct cmd_handler mode_handlers[] = {
13 { "bindcode", cmd_bindcode }, 12 { "bindcode", cmd_bindcode },
13 { "bindgesture", cmd_bindgesture },
14 { "bindswitch", cmd_bindswitch }, 14 { "bindswitch", cmd_bindswitch },
15 { "bindsym", cmd_bindsym }, 15 { "bindsym", cmd_bindsym },
16 { "set", cmd_set }, 16 { "set", cmd_set },
17 { "unbindcode", cmd_unbindcode }, 17 { "unbindcode", cmd_unbindcode },
18 { "unbindgesture", cmd_unbindgesture },
18 { "unbindswitch", cmd_unbindswitch }, 19 { "unbindswitch", cmd_unbindswitch },
19 { "unbindsym", cmd_unbindsym }, 20 { "unbindsym", cmd_unbindsym },
20}; 21};
@@ -59,6 +60,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
59 mode->keycode_bindings = create_list(); 60 mode->keycode_bindings = create_list();
60 mode->mouse_bindings = create_list(); 61 mode->mouse_bindings = create_list();
61 mode->switch_bindings = create_list(); 62 mode->switch_bindings = create_list();
63 mode->gesture_bindings = create_list();
62 mode->pango = pango; 64 mode->pango = pango;
63 list_add(config->modes, mode); 65 list_add(config->modes, mode);
64 } 66 }
diff --git a/sway/commands/move.c b/sway/commands/move.c
index f2702fa1..8addf26e 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <math.h> 2#include <math.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -206,9 +205,17 @@ static void container_move_to_workspace(struct sway_container *container,
206 container_detach(container); 205 container_detach(container);
207 workspace_add_floating(workspace, container); 206 workspace_add_floating(workspace, container);
208 container_handle_fullscreen_reparent(container); 207 container_handle_fullscreen_reparent(container);
209 // If changing output, center it within the workspace 208 // If changing output, adjust the coordinates of the window.
210 if (old_output != workspace->output && !container->pending.fullscreen_mode) { 209 if (old_output != workspace->output && !container->pending.fullscreen_mode) {
211 container_floating_move_to_center(container); 210 struct wlr_box workspace_box, old_workspace_box;
211 workspace_get_box(workspace, &workspace_box);
212 workspace_get_box(old_workspace, &old_workspace_box);
213 floating_fix_coordinates(container, &old_workspace_box, &workspace_box);
214 if (container->scratchpad && workspace->output) {
215 struct wlr_box output_box;
216 output_get_box(workspace->output, &output_box);
217 container->transform = workspace_box;
218 }
212 } 219 }
213 } else { 220 } else {
214 container_detach(container); 221 container_detach(container);
@@ -462,7 +469,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
462 if (strcasecmp(argv[1], "number") == 0) { 469 if (strcasecmp(argv[1], "number") == 0) {
463 // move [window|container] [to] "workspace number x" 470 // move [window|container] [to] "workspace number x"
464 if (argc < 3) { 471 if (argc < 3) {
465 return cmd_results_new(CMD_INVALID, expected_syntax); 472 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
466 } 473 }
467 if (!isdigit(argv[2][0])) { 474 if (!isdigit(argv[2][0])) {
468 return cmd_results_new(CMD_INVALID, 475 return cmd_results_new(CMD_INVALID,
@@ -522,7 +529,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
522 } 529 }
523 destination = &dest_con->node; 530 destination = &dest_con->node;
524 } else { 531 } else {
525 return cmd_results_new(CMD_INVALID, expected_syntax); 532 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
526 } 533 }
527 534
528 if (destination->type == N_CONTAINER && 535 if (destination->type == N_CONTAINER &&
@@ -686,6 +693,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
686 arrange_output(old_output); 693 arrange_output(old_output);
687 arrange_output(new_output); 694 arrange_output(new_output);
688 695
696 struct sway_seat *seat = config->handler_context.seat;
697 seat_consider_warp_to_focus(seat);
698
689 return cmd_results_new(CMD_SUCCESS, NULL); 699 return cmd_results_new(CMD_SUCCESS, NULL);
690} 700}
691 701
@@ -759,15 +769,6 @@ static struct cmd_results *cmd_move_in_direction(
759 ipc_event_window(container, "move"); 769 ipc_event_window(container, "move");
760 } 770 }
761 771
762 // Hack to re-focus container
763 seat_set_raw_focus(config->handler_context.seat, &new_ws->node);
764 seat_set_focus_container(config->handler_context.seat, container);
765
766 if (old_ws != new_ws) {
767 ipc_event_workspace(old_ws, new_ws, "focus");
768 workspace_detect_urgent(old_ws);
769 workspace_detect_urgent(new_ws);
770 }
771 container_end_mouse_operation(container); 772 container_end_mouse_operation(container);
772 773
773 return cmd_results_new(CMD_SUCCESS, NULL); 774 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -788,15 +789,15 @@ static struct cmd_results *cmd_move_to_position_pointer(
788 struct wlr_output *output = wlr_output_layout_output_at( 789 struct wlr_output *output = wlr_output_layout_output_at(
789 root->output_layout, cursor->x, cursor->y); 790 root->output_layout, cursor->x, cursor->y);
790 if (output) { 791 if (output) {
791 struct wlr_box *box = 792 struct wlr_box box;
792 wlr_output_layout_get_box(root->output_layout, output); 793 wlr_output_layout_get_box(root->output_layout, output, &box);
793 lx = fmax(lx, box->x); 794 lx = fmax(lx, box.x);
794 ly = fmax(ly, box->y); 795 ly = fmax(ly, box.y);
795 if (lx + container->pending.width > box->x + box->width) { 796 if (lx + container->pending.width > box.x + box.width) {
796 lx = box->x + box->width - container->pending.width; 797 lx = box.x + box.width - container->pending.width;
797 } 798 }
798 if (ly + container->pending.height > box->y + box->height) { 799 if (ly + container->pending.height > box.y + box.height) {
799 ly = box->y + box->height - container->pending.height; 800 ly = box.y + box.height - container->pending.height;
800 } 801 }
801 } 802 }
802 803
@@ -818,7 +819,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
818 } 819 }
819 820
820 if (!argc) { 821 if (!argc) {
821 return cmd_results_new(CMD_INVALID, expected_position_syntax); 822 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
822 } 823 }
823 824
824 bool absolute = false; 825 bool absolute = false;
@@ -828,19 +829,19 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
828 ++argv; 829 ++argv;
829 } 830 }
830 if (!argc) { 831 if (!argc) {
831 return cmd_results_new(CMD_INVALID, expected_position_syntax); 832 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
832 } 833 }
833 if (strcmp(argv[0], "position") == 0) { 834 if (strcmp(argv[0], "position") == 0) {
834 --argc; 835 --argc;
835 ++argv; 836 ++argv;
836 } 837 }
837 if (!argc) { 838 if (!argc) {
838 return cmd_results_new(CMD_INVALID, expected_position_syntax); 839 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
839 } 840 }
840 if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || 841 if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 ||
841 strcmp(argv[0], "pointer") == 0) { 842 strcmp(argv[0], "pointer") == 0) {
842 if (absolute) { 843 if (absolute) {
843 return cmd_results_new(CMD_INVALID, expected_position_syntax); 844 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
844 } 845 }
845 return cmd_move_to_position_pointer(container); 846 return cmd_move_to_position_pointer(container);
846 } else if (strcmp(argv[0], "center") == 0) { 847 } else if (strcmp(argv[0], "center") == 0) {
@@ -862,7 +863,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
862 } 863 }
863 864
864 if (argc < 2) { 865 if (argc < 2) {
865 return cmd_results_new(CMD_FAILURE, expected_position_syntax); 866 return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax);
866 } 867 }
867 868
868 struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 869 struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
@@ -874,13 +875,17 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
874 return cmd_results_new(CMD_INVALID, "Invalid x position specified"); 875 return cmd_results_new(CMD_INVALID, "Invalid x position specified");
875 } 876 }
876 877
878 if (argc < 1) {
879 return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax);
880 }
881
877 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 882 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
878 // Y direction 883 // Y direction
879 num_consumed_args = parse_movement_amount(argc, argv, &ly); 884 num_consumed_args = parse_movement_amount(argc, argv, &ly);
880 argc -= num_consumed_args; 885 argc -= num_consumed_args;
881 argv += num_consumed_args; 886 argv += num_consumed_args;
882 if (argc > 0) { 887 if (argc > 0) {
883 return cmd_results_new(CMD_INVALID, expected_position_syntax); 888 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
884 } 889 }
885 if (ly.unit == MOVEMENT_UNIT_INVALID) { 890 if (ly.unit == MOVEMENT_UNIT_INVALID) {
886 return cmd_results_new(CMD_INVALID, "Invalid y position specified"); 891 return cmd_results_new(CMD_INVALID, "Invalid y position specified");
@@ -1026,13 +1031,13 @@ struct cmd_results *cmd_move(int argc, char **argv) {
1026 } 1031 }
1027 1032
1028 if (!argc) { 1033 if (!argc) {
1029 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1034 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1030 } 1035 }
1031 1036
1032 // Only `move [window|container] [to] workspace` supports 1037 // Only `move [window|container] [to] workspace` supports
1033 // `--no-auto-back-and-forth` so treat others as invalid syntax 1038 // `--no-auto-back-and-forth` so treat others as invalid syntax
1034 if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { 1039 if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) {
1035 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1040 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1036 } 1041 }
1037 1042
1038 if (strcasecmp(argv[0], "workspace") == 0 || 1043 if (strcasecmp(argv[0], "workspace") == 0 ||
@@ -1046,5 +1051,5 @@ struct cmd_results *cmd_move(int argc, char **argv) {
1046 strcasecmp(argv[1], "position") == 0)) { 1051 strcasecmp(argv[1], "position") == 0)) {
1047 return cmd_move_to_position(argc, argv); 1052 return cmd_move_to_position(argc, argv);
1048 } 1053 }
1049 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1054 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1050} 1055}
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c
index 2001e04f..ccfdec82 100644
--- a/sway/commands/no_focus.c
+++ b/sway/commands/no_focus.c
@@ -13,7 +13,7 @@ struct cmd_results *cmd_no_focus(int argc, char **argv) {
13 char *err_str = NULL; 13 char *err_str = NULL;
14 struct criteria *criteria = criteria_parse(argv[0], &err_str); 14 struct criteria *criteria = criteria_parse(argv[0], &err_str);
15 if (!criteria) { 15 if (!criteria) {
16 error = cmd_results_new(CMD_INVALID, err_str); 16 error = cmd_results_new(CMD_INVALID, "%s", err_str);
17 free(err_str); 17 free(err_str);
18 return error; 18 return error;
19 } 19 }
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c
index 96e6228e..610cecc6 100644
--- a/sway/commands/opacity.c
+++ b/sway/commands/opacity.c
@@ -37,6 +37,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) {
37 } 37 }
38 38
39 con->alpha = val; 39 con->alpha = val;
40 container_damage_whole(con); 40 container_update(con);
41
41 return cmd_results_new(CMD_SUCCESS, NULL); 42 return cmd_results_new(CMD_SUCCESS, NULL);
42} 43}
diff --git a/sway/commands/output.c b/sway/commands/output.c
index 4418f23f..5e5d31b3 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -15,8 +15,11 @@ static const struct cmd_handler output_handlers[] = {
15 { "enable", output_cmd_enable }, 15 { "enable", output_cmd_enable },
16 { "max_render_time", output_cmd_max_render_time }, 16 { "max_render_time", output_cmd_max_render_time },
17 { "mode", output_cmd_mode }, 17 { "mode", output_cmd_mode },
18 { "modeline", output_cmd_modeline },
18 { "pos", output_cmd_position }, 19 { "pos", output_cmd_position },
19 { "position", output_cmd_position }, 20 { "position", output_cmd_position },
21 { "power", output_cmd_power },
22 { "render_bit_depth", output_cmd_render_bit_depth },
20 { "res", output_cmd_mode }, 23 { "res", output_cmd_mode },
21 { "resolution", output_cmd_mode }, 24 { "resolution", output_cmd_mode },
22 { "scale", output_cmd_scale }, 25 { "scale", output_cmd_scale },
@@ -24,6 +27,7 @@ static const struct cmd_handler output_handlers[] = {
24 { "subpixel", output_cmd_subpixel }, 27 { "subpixel", output_cmd_subpixel },
25 { "toggle", output_cmd_toggle }, 28 { "toggle", output_cmd_toggle },
26 { "transform", output_cmd_transform }, 29 { "transform", output_cmd_transform },
30 { "unplug", output_cmd_unplug },
27}; 31};
28 32
29struct cmd_results *cmd_output(int argc, char **argv) { 33struct cmd_results *cmd_output(int argc, char **argv) {
@@ -32,9 +36,9 @@ struct cmd_results *cmd_output(int argc, char **argv) {
32 return error; 36 return error;
33 } 37 }
34 38
35 // The NOOP-1 output is a dummy output used when there's no outputs 39 // The HEADLESS-1 output is a dummy output used when there's no outputs
36 // connected. It should never be configured. 40 // connected. It should never be configured.
37 if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { 41 if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) {
38 return cmd_results_new(CMD_FAILURE, 42 return cmd_results_new(CMD_FAILURE,
39 "Refusing to configure the no op output"); 43 "Refusing to configure the no op output");
40 } 44 }
@@ -51,7 +55,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {
51 if (!sway_output) { 55 if (!sway_output) {
52 return cmd_results_new(CMD_FAILURE, "Unknown output"); 56 return cmd_results_new(CMD_FAILURE, "Unknown output");
53 } 57 }
54 if (sway_output == root->noop_output) { 58 if (sway_output == root->fallback_output) {
55 return cmd_results_new(CMD_FAILURE, 59 return cmd_results_new(CMD_FAILURE,
56 "Refusing to configure the no op output"); 60 "Refusing to configure the no op output");
57 } 61 }
@@ -99,15 +103,18 @@ struct cmd_results *cmd_output(int argc, char **argv) {
99 103
100 bool background = output->background; 104 bool background = output->background;
101 105
102 output = store_output_config(output); 106 store_output_config(output);
103 107
104 // If reloading, the output configs will be applied after reading the 108 // If reloading, the output configs will be applied after reading the
105 // entire config and before the deferred commands so that an auto generated 109 // entire config and before the deferred commands so that an auto generated
106 // workspace name is not given to re-enabled outputs. 110 // workspace name is not given to re-enabled outputs.
107 if (!config->reloading && !config->validating) { 111 if (!config->reloading && !config->validating) {
108 apply_output_config_to_outputs(output); 112 apply_all_output_configs();
109 if (background) { 113 if (background) {
110 spawn_swaybg(); 114 if (!spawn_swaybg()) {
115 return cmd_results_new(CMD_FAILURE,
116 "Failed to apply background configuration");
117 }
111 } 118 }
112 } 119 }
113 120
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 68ee9fe1..55bd7671 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <libgen.h> 1#include <libgen.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <string.h> 3#include <string.h>
@@ -102,19 +101,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
102 } 101 }
103 102
104 char *conf_path = dirname(conf); 103 char *conf_path = dirname(conf);
105 char *rel_path = src; 104 char *real_src = malloc(strlen(conf_path) + strlen(src) + 2);
106 src = malloc(strlen(conf_path) + strlen(src) + 2); 105 if (!real_src) {
107 if (!src) { 106 free(src);
108 free(rel_path);
109 free(conf); 107 free(conf);
110 sway_log(SWAY_ERROR, "Unable to allocate memory"); 108 sway_log(SWAY_ERROR, "Unable to allocate memory");
111 return cmd_results_new(CMD_FAILURE, 109 return cmd_results_new(CMD_FAILURE,
112 "Unable to allocate resources"); 110 "Unable to allocate resources");
113 } 111 }
114 112
115 sprintf(src, "%s/%s", conf_path, rel_path); 113 snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src);
116 free(rel_path); 114 free(src);
117 free(conf); 115 free(conf);
116 src = real_src;
118 } 117 }
119 118
120 bool can_access = access(src, F_OK) != -1; 119 bool can_access = access(src, F_OK) != -1;
@@ -123,7 +122,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
123 src); 122 src);
124 config_add_swaynag_warning("Unable to access background file '%s'", 123 config_add_swaynag_warning("Unable to access background file '%s'",
125 src); 124 src);
125 struct cmd_results *result = cmd_results_new(CMD_FAILURE,
126 "unable to access background file '%s'", src);
126 free(src); 127 free(src);
128 return result;
127 } else { 129 } else {
128 output->background = src; 130 output->background = src;
129 output->background_option = strdup(mode); 131 output->background_option = strdup(mode);
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c
index 638c0ade..c7adbd58 100644
--- a/sway/commands/output/dpms.c
+++ b/sway/commands/output/dpms.c
@@ -1,45 +1,8 @@
1#include "log.h"
1#include "sway/commands.h" 2#include "sway/commands.h"
2#include "sway/config.h"
3#include "sway/output.h"
4#include "util.h"
5#include <strings.h>
6 3
7struct cmd_results *output_cmd_dpms(int argc, char **argv) { 4struct cmd_results *output_cmd_dpms(int argc, char **argv) {
8 if (!config->handler_context.output_config) { 5 sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, "
9 return cmd_results_new(CMD_FAILURE, "Missing output config"); 6 "use \"output power\" instead");
10 } 7 return output_cmd_power(argc, argv);
11 if (!argc) {
12 return cmd_results_new(CMD_INVALID, "Missing dpms argument.");
13 }
14
15 enum config_dpms current_dpms = DPMS_ON;
16
17 if (strcasecmp(argv[0], "toggle") == 0) {
18
19 const char *oc_name = config->handler_context.output_config->name;
20 if (strcmp(oc_name, "*") == 0) {
21 return cmd_results_new(CMD_INVALID,
22 "Cannot apply toggle to all outputs.");
23 }
24
25 struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
26 if (!sway_output || !sway_output->wlr_output) {
27 return cmd_results_new(CMD_FAILURE,
28 "Cannot apply toggle to unknown output %s", oc_name);
29 }
30
31 if (sway_output->enabled && !sway_output->wlr_output->enabled) {
32 current_dpms = DPMS_OFF;
33 }
34 }
35
36 if (parse_boolean(argv[0], current_dpms == DPMS_ON)) {
37 config->handler_context.output_config->dpms_state = DPMS_ON;
38 } else {
39 config->handler_context.output_config->dpms_state = DPMS_OFF;
40 }
41
42 config->handler_context.leftovers.argc = argc - 1;
43 config->handler_context.leftovers.argv = argv + 1;
44 return NULL;
45} 8}
diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c
index 5b710713..019d625a 100644
--- a/sway/commands/output/mode.c
+++ b/sway/commands/output/mode.c
@@ -20,6 +20,9 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) {
20 output->custom_mode = 0; 20 output->custom_mode = 0;
21 } 21 }
22 22
23 // Reset custom modeline, if any
24 output->drm_mode.type = 0;
25
23 char *end; 26 char *end;
24 output->width = strtol(*argv, &end, 10); 27 output->width = strtol(*argv, &end, 10);
25 if (*end) { 28 if (*end) {
@@ -58,3 +61,58 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) {
58 return NULL; 61 return NULL;
59} 62}
60 63
64static bool parse_modeline(char **argv, drmModeModeInfo *mode) {
65 mode->type = DRM_MODE_TYPE_USERDEF;
66 mode->clock = strtof(argv[0], NULL) * 1000;
67 mode->hdisplay = strtol(argv[1], NULL, 10);
68 mode->hsync_start = strtol(argv[2], NULL, 10);
69 mode->hsync_end = strtol(argv[3], NULL, 10);
70 mode->htotal = strtol(argv[4], NULL, 10);
71 mode->vdisplay = strtol(argv[5], NULL, 10);
72 mode->vsync_start = strtol(argv[6], NULL, 10);
73 mode->vsync_end = strtol(argv[7], NULL, 10);
74 mode->vtotal = strtol(argv[8], NULL, 10);
75
76 mode->vrefresh = mode->clock * 1000.0 * 1000.0
77 / mode->htotal / mode->vtotal;
78 if (strcasecmp(argv[9], "+hsync") == 0) {
79 mode->flags |= DRM_MODE_FLAG_PHSYNC;
80 } else if (strcasecmp(argv[9], "-hsync") == 0) {
81 mode->flags |= DRM_MODE_FLAG_NHSYNC;
82 } else {
83 return false;
84 }
85
86 if (strcasecmp(argv[10], "+vsync") == 0) {
87 mode->flags |= DRM_MODE_FLAG_PVSYNC;
88 } else if (strcasecmp(argv[10], "-vsync") == 0) {
89 mode->flags |= DRM_MODE_FLAG_NVSYNC;
90 } else {
91 return false;
92 }
93
94 snprintf(mode->name, sizeof(mode->name), "%dx%d@%d",
95 mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000);
96
97 return true;
98}
99
100struct cmd_results *output_cmd_modeline(int argc, char **argv) {
101 if (!config->handler_context.output_config) {
102 return cmd_results_new(CMD_FAILURE, "Missing output config");
103 }
104 if (!argc) {
105 return cmd_results_new(CMD_INVALID, "Missing modeline argument.");
106 }
107
108 struct output_config *output = config->handler_context.output_config;
109
110 if (argc != 11 || !parse_modeline(argv, &output->drm_mode)) {
111 return cmd_results_new(CMD_INVALID, "Invalid modeline");
112 }
113
114 config->handler_context.leftovers.argc = argc - 12;
115 config->handler_context.leftovers.argv = argv + 12;
116 return NULL;
117}
118
diff --git a/sway/commands/output/power.c b/sway/commands/output/power.c
new file mode 100644
index 00000000..e6ae2852
--- /dev/null
+++ b/sway/commands/output/power.c
@@ -0,0 +1,43 @@
1#include <strings.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "sway/output.h"
5#include "util.h"
6
7struct cmd_results *output_cmd_power(int argc, char **argv) {
8 if (!config->handler_context.output_config) {
9 return cmd_results_new(CMD_FAILURE, "Missing output config");
10 }
11 if (argc == 0) {
12 return cmd_results_new(CMD_INVALID, "Missing power argument");
13 }
14
15 bool current = true;
16 if (strcasecmp(argv[0], "toggle") == 0) {
17 const char *oc_name = config->handler_context.output_config->name;
18 if (strcmp(oc_name, "*") == 0) {
19 return cmd_results_new(CMD_INVALID,
20 "Cannot apply toggle to all outputs");
21 }
22
23 struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
24 if (!sway_output || !sway_output->wlr_output) {
25 return cmd_results_new(CMD_FAILURE,
26 "Cannot apply toggle to unknown output %s", oc_name);
27 }
28
29 if (sway_output->enabled && !sway_output->wlr_output->enabled) {
30 current = false;
31 }
32 }
33
34 if (parse_boolean(argv[0], current)) {
35 config->handler_context.output_config->power = 1;
36 } else {
37 config->handler_context.output_config->power = 0;
38 }
39
40 config->handler_context.leftovers.argc = argc - 1;
41 config->handler_context.leftovers.argv = argv + 1;
42 return NULL;
43}
diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c
new file mode 100644
index 00000000..c419321e
--- /dev/null
+++ b/sway/commands/output/render_bit_depth.c
@@ -0,0 +1,29 @@
1#include <drm_fourcc.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5
6struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) {
7 if (!config->handler_context.output_config) {
8 return cmd_results_new(CMD_FAILURE, "Missing output config");
9 }
10 if (!argc) {
11 return cmd_results_new(CMD_INVALID, "Missing bit depth argument.");
12 }
13
14 if (strcmp(*argv, "8") == 0) {
15 config->handler_context.output_config->render_bit_depth =
16 RENDER_BIT_DEPTH_8;
17 } else if (strcmp(*argv, "10") == 0) {
18 config->handler_context.output_config->render_bit_depth =
19 RENDER_BIT_DEPTH_10;
20 } else {
21 return cmd_results_new(CMD_INVALID,
22 "Invalid bit depth. Must be a value in (8|10).");
23 }
24
25 config->handler_context.leftovers.argc = argc - 1;
26 config->handler_context.leftovers.argv = argv + 1;
27 return NULL;
28}
29
diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c
index 6342d526..c6b72845 100644
--- a/sway/commands/output/toggle.c
+++ b/sway/commands/output/toggle.c
@@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) {
29 config->handler_context.output_config->enabled = 1; 29 config->handler_context.output_config->enabled = 1;
30 } 30 }
31 31
32 free(oc); 32 free_output_config(oc);
33 config->handler_context.leftovers.argc = argc; 33 config->handler_context.leftovers.argc = argc;
34 config->handler_context.leftovers.argv = argv; 34 config->handler_context.leftovers.argv = argv;
35 return NULL; 35 return NULL;
diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c
index f4fcc8c9..8db71bb3 100644
--- a/sway/commands/output/transform.c
+++ b/sway/commands/output/transform.c
@@ -1,4 +1,5 @@
1#include <string.h> 1#include <string.h>
2#include <wlr/util/transform.h>
2#include "sway/commands.h" 3#include "sway/commands.h"
3#include "sway/config.h" 4#include "sway/config.h"
4#include "log.h" 5#include "log.h"
diff --git a/sway/commands/output/unplug.c b/sway/commands/output/unplug.c
new file mode 100644
index 00000000..dfef626f
--- /dev/null
+++ b/sway/commands/output/unplug.c
@@ -0,0 +1,54 @@
1#include <strings.h>
2#include <wlr/config.h>
3#include <wlr/backend/headless.h>
4#include <wlr/backend/wayland.h>
5#if WLR_HAS_X11_BACKEND
6#include <wlr/backend/x11.h>
7#endif
8#include "sway/commands.h"
9#include "sway/config.h"
10#include "sway/output.h"
11
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 3c994d54..6c0aac26 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -9,9 +8,8 @@
9#include "list.h" 8#include "list.h"
10#include "log.h" 9#include "log.h"
11 10
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,8 +46,7 @@ static void do_reload(void *data) {
48 } 46 }
49 list_free_items_and_destroy(bar_ids); 47 list_free_items_and_destroy(bar_ids);
50 48
51 config_update_font_height(true); 49 root_for_each_container(title_bar_update_iterator, NULL);
52 root_for_each_container(rebuild_textures_iterator, NULL);
53 50
54 arrange_root(); 51 arrange_root();
55} 52}
diff --git a/sway/commands/rename.c b/sway/commands/rename.c
index 3b855fdf..0d36cc21 100644
--- a/sway/commands/rename.c
+++ b/sway/commands/rename.c
@@ -7,6 +7,7 @@
7#include "sway/config.h" 7#include "sway/config.h"
8#include "sway/ipc-server.h" 8#include "sway/ipc-server.h"
9#include "sway/output.h" 9#include "sway/output.h"
10#include "sway/desktop/launcher.h"
10#include "sway/tree/container.h" 11#include "sway/tree/container.h"
11#include "sway/tree/workspace.h" 12#include "sway/tree/workspace.h"
12#include "sway/tree/root.h" 13#include "sway/tree/root.h"
@@ -25,7 +26,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
25 "Can't run this command while there's no outputs connected."); 26 "Can't run this command while there's no outputs connected.");
26 } 27 }
27 if (strcasecmp(argv[0], "workspace") != 0) { 28 if (strcasecmp(argv[0], "workspace") != 0) {
28 return cmd_results_new(CMD_INVALID, expected_syntax); 29 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
29 } 30 }
30 31
31 int argn = 1; 32 int argn = 1;
@@ -64,7 +65,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
64 ++argn; // move past "to" 65 ++argn; // move past "to"
65 66
66 if (argn >= argc) { 67 if (argn >= argc) {
67 return cmd_results_new(CMD_INVALID, expected_syntax); 68 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
68 } 69 }
69 70
70 char *new_name = join_args(argv + argn, argc - argn); 71 char *new_name = join_args(argv + argn, argc - argn);
@@ -91,8 +92,6 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
91 92
92 sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); 93 sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name);
93 94
94 root_rename_pid_workspaces(workspace->name, new_name);
95
96 free(workspace->name); 95 free(workspace->name);
97 workspace->name = new_name; 96 workspace->name = new_name;
98 97
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 425069de..32b746ea 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -75,6 +75,10 @@ void container_resize_tiled(struct sway_container *con,
75 return; 75 return;
76 } 76 }
77 77
78 if (container_is_scratchpad_hidden_or_child(con)) {
79 return;
80 }
81
78 // For HORIZONTAL or VERTICAL, we are growing in two directions so select 82 // For HORIZONTAL or VERTICAL, we are growing in two directions so select
79 // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. 83 // both adjacent siblings. For RIGHT or DOWN, just select the next sibling.
80 // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to 84 // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to
@@ -249,16 +253,35 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis,
249 struct movement_amount *amount) { 253 struct movement_amount *amount) {
250 struct sway_container *current = config->handler_context.container; 254 struct sway_container *current = config->handler_context.container;
251 255
256 if (container_is_scratchpad_hidden_or_child(current)) {
257 return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container");
258 }
259
252 if (amount->unit == MOVEMENT_UNIT_DEFAULT) { 260 if (amount->unit == MOVEMENT_UNIT_DEFAULT) {
253 amount->unit = MOVEMENT_UNIT_PPT; 261 amount->unit = MOVEMENT_UNIT_PPT;
254 } 262 }
255 if (amount->unit == MOVEMENT_UNIT_PPT) { 263 if (amount->unit == MOVEMENT_UNIT_PPT) {
264 struct sway_container *parent = current->pending.parent;
256 float pct = amount->amount / 100.0f; 265 float pct = amount->amount / 100.0f;
257 266
258 if (is_horizontal(axis)) { 267 if (is_horizontal(axis)) {
259 amount->amount = (float)current->pending.width * pct; 268 while (parent && parent->pending.layout != L_HORIZ) {
269 parent = parent->pending.parent;
270 }
271 if (parent) {
272 amount->amount = (float)parent->pending.width * pct;
273 } else {
274 amount->amount = (float)current->pending.workspace->width * pct;
275 }
260 } else { 276 } else {
261 amount->amount = (float)current->pending.height * pct; 277 while (parent && parent->pending.layout != L_VERT) {
278 parent = parent->pending.parent;
279 }
280 if (parent) {
281 amount->amount = (float)parent->pending.height * pct;
282 } else {
283 amount->amount = (float)current->pending.workspace->height * pct;
284 }
262 } 285 }
263 } 286 }
264 287
@@ -277,6 +300,11 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis,
277 */ 300 */
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 749235eb..df7c379d 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <linux/input-event-codes.h> 1#include <linux/input-event-codes.h>
3 2
4#include <strings.h> 3#include <strings.h>
@@ -18,7 +17,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
18 int argc, char **argv) { 17 int argc, char **argv) {
19 if (strcasecmp(argv[0], "move") == 0) { 18 if (strcasecmp(argv[0], "move") == 0) {
20 if (argc < 3) { 19 if (argc < 3) {
21 return cmd_results_new(CMD_INVALID, expected_syntax); 20 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
22 } 21 }
23 int delta_x = strtol(argv[1], NULL, 10); 22 int delta_x = strtol(argv[1], NULL, 10);
24 int delta_y = strtol(argv[2], NULL, 10); 23 int delta_y = strtol(argv[2], NULL, 10);
@@ -27,7 +26,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
27 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 26 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
28 } else if (strcasecmp(argv[0], "set") == 0) { 27 } else if (strcasecmp(argv[0], "set") == 0) {
29 if (argc < 3) { 28 if (argc < 3) {
30 return cmd_results_new(CMD_INVALID, expected_syntax); 29 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
31 } 30 }
32 // map absolute coords (0..1,0..1) to root container coords 31 // map absolute coords (0..1,0..1) to root container coords
33 float x = strtof(argv[1], NULL) / root->width; 32 float x = strtof(argv[1], NULL) / root->width;
@@ -37,7 +36,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
37 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 36 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
38 } else { 37 } else {
39 if (argc < 2) { 38 if (argc < 2) {
40 return cmd_results_new(CMD_INVALID, expected_syntax); 39 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
41 } 40 }
42 struct cmd_results *error = NULL; 41 struct cmd_results *error = NULL;
43 if ((error = press_or_release(cursor, argv[0], argv[1]))) { 42 if ((error = press_or_release(cursor, argv[0], argv[1]))) {
@@ -85,36 +84,36 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
85 84
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_event_pointer_axis event = { 113 struct wlr_pointer_axis_event event = {
115 .device = NULL, 114 .pointer = NULL,
116 .time_msec = 0, 115 .time_msec = 0,
117 .source = WLR_AXIS_SOURCE_WHEEL, 116 .source = WL_POINTER_AXIS_SOURCE_WHEEL,
118 .orientation = orientation, 117 .orientation = orientation,
119 .delta = delta * 15, 118 .delta = delta * 15,
120 .delta_discrete = delta 119 .delta_discrete = delta
diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c
index e09b82d9..f5177a47 100644
--- a/sway/commands/seat/hide_cursor.c
+++ b/sway/commands/seat/hide_cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c
index 82428f2c..2974453e 100644
--- a/sway/commands/seat/idle.c
+++ b/sway/commands/seat/idle.c
@@ -1,8 +1,8 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
5#include <stdint.h> 4#include <stdint.h>
5#include "log.h"
6#include "sway/commands.h" 6#include "sway/commands.h"
7#include "sway/config.h" 7#include "sway/config.h"
8#include "sway/input/seat.h" 8#include "sway/input/seat.h"
@@ -69,5 +69,10 @@ struct cmd_results *seat_cmd_idle_wake(int argc, char **argv) {
69 return cmd_results_new(CMD_FAILURE, "Invalid idle source"); 69 return cmd_results_new(CMD_FAILURE, "Invalid idle source");
70 } 70 }
71 config->handler_context.seat_config->idle_wake_sources = sources; 71 config->handler_context.seat_config->idle_wake_sources = sources;
72 sway_log(SWAY_INFO, "Warning: seat idle_wake is deprecated");
73 if (config->reading) {
74 config_add_swaynag_warning("seat idle_wake is deprecated. "
75 "Only seat idle_inhibit is supported.");
76 }
72 return cmd_results_new(CMD_SUCCESS, NULL); 77 return cmd_results_new(CMD_SUCCESS, NULL);
73} 78}
diff --git a/sway/commands/seat/xcursor_theme.c b/sway/commands/seat/xcursor_theme.c
index 202f35b9..61322a57 100644
--- a/sway/commands/seat/xcursor_theme.c
+++ b/sway/commands/seat/xcursor_theme.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/set.c b/sway/commands/set.c
index c539e9fc..ba384c7c 100644
--- a/sway/commands/set.c
+++ b/sway/commands/set.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
index 0d373b80..60cef9fa 100644
--- a/sway/commands/show_marks.c
+++ b/sway/commands/show_marks.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -10,8 +9,8 @@
10#include "stringop.h" 9#include "stringop.h"
11#include "util.h" 10#include "util.h"
12 11
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/smart_gaps.c b/sway/commands/smart_gaps.c
index b27f9ccd..a6d165dc 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -15,7 +15,12 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
15 return error; 15 return error;
16 } 16 }
17 17
18 config->smart_gaps = parse_boolean(argv[0], config->smart_gaps); 18 if (strcmp(argv[0], "inverse_outer") == 0) {
19 config->smart_gaps = SMART_GAPS_INVERSE_OUTER;
20 } else {
21 config->smart_gaps = parse_boolean(argv[0], config->smart_gaps)
22 ? SMART_GAPS_ON : SMART_GAPS_OFF;
23 }
19 24
20 arrange_root(); 25 arrange_root();
21 26
diff --git a/sway/commands/split.c b/sway/commands/split.c
index 3e25c6f7..500a497d 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -32,6 +32,24 @@ static struct cmd_results *do_split(int layout) {
32 return cmd_results_new(CMD_SUCCESS, NULL); 32 return cmd_results_new(CMD_SUCCESS, NULL);
33} 33}
34 34
35static struct cmd_results *do_unsplit(void) {
36 struct sway_container *con = config->handler_context.container;
37 struct sway_workspace *ws = config->handler_context.workspace;
38
39 if (con && con->pending.parent && con->pending.parent->pending.children->length == 1) {
40 container_flatten(con->pending.parent);
41 } else {
42 return cmd_results_new(CMD_FAILURE, "Can only flatten a child container with no siblings");
43 }
44
45 if (root->fullscreen_global) {
46 arrange_root();
47 } else {
48 arrange_workspace(ws);
49 }
50 return cmd_results_new(CMD_SUCCESS, NULL);
51}
52
35struct cmd_results *cmd_split(int argc, char **argv) { 53struct cmd_results *cmd_split(int argc, char **argv) {
36 struct cmd_results *error = NULL; 54 struct cmd_results *error = NULL;
37 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { 55 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
@@ -55,6 +73,9 @@ struct cmd_results *cmd_split(int argc, char **argv) {
55 } else { 73 } else {
56 return do_split(L_VERT); 74 return do_split(L_VERT);
57 } 75 }
76 } else if (strcasecmp(argv[0], "n") == 0 ||
77 strcasecmp(argv[0], "none") == 0) {
78 return do_unsplit();
58 } else { 79 } else {
59 return cmd_results_new(CMD_FAILURE, 80 return cmd_results_new(CMD_FAILURE,
60 "Invalid split command (expected either horizontal or vertical)."); 81 "Invalid split command (expected either horizontal or vertical).");
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index ce5e5128..e142eede 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -1,10 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <strings.h> 1#include <strings.h>
3#include "config.h" 2#include "config.h"
4#include "log.h" 3#include "log.h"
5#include "sway/commands.h" 4#include "sway/commands.h"
6#include "sway/output.h" 5#include "sway/output.h"
7#include "sway/tree/arrange.h" 6#include "sway/tree/arrange.h"
7#include "sway/tree/container.h"
8#include "sway/tree/root.h" 8#include "sway/tree/root.h"
9#include "sway/tree/view.h" 9#include "sway/tree/view.h"
10#include "sway/tree/workspace.h" 10#include "sway/tree/workspace.h"
@@ -13,180 +13,6 @@
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 enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
130 if (fs1) {
131 container_fullscreen_disable(con1);
132 }
133 if (fs2) {
134 container_fullscreen_disable(con2);
135 }
136
137 struct sway_seat *seat = config->handler_context.seat;
138 struct sway_container *focus = seat_get_focused_container(seat);
139 struct sway_workspace *vis1 =
140 output_get_active_workspace(con1->pending.workspace->output);
141 struct sway_workspace *vis2 =
142 output_get_active_workspace(con2->pending.workspace->output);
143 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a"
144 "workspace. This should not happen")) {
145 return;
146 }
147
148 char *stored_prev_name = NULL;
149 if (seat->prev_workspace_name) {
150 stored_prev_name = strdup(seat->prev_workspace_name);
151 }
152
153 swap_places(con1, con2);
154
155 if (!workspace_is_visible(vis1)) {
156 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));
157 }
158 if (!workspace_is_visible(vis2)) {
159 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));
160 }
161
162 swap_focus(con1, con2, seat, focus);
163
164 if (stored_prev_name) {
165 free(seat->prev_workspace_name);
166 seat->prev_workspace_name = stored_prev_name;
167 }
168
169 if (scratch1) {
170 root_scratchpad_add_container(con2, NULL);
171 if (!hidden1) {
172 root_scratchpad_show(con2);
173 }
174 }
175 if (scratch2) {
176 root_scratchpad_add_container(con1, NULL);
177 if (!hidden2) {
178 root_scratchpad_show(con1);
179 }
180 }
181
182 if (fs1) {
183 container_set_fullscreen(con2, fs1);
184 }
185 if (fs2) {
186 container_set_fullscreen(con1, fs2);
187 }
188}
189
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) {
@@ -247,6 +73,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
247 } else if (!current) { 73 } else if (!current) {
248 error = cmd_results_new(CMD_FAILURE, 74 error = cmd_results_new(CMD_FAILURE,
249 "Can only swap with containers and views"); 75 "Can only swap with containers and views");
76 } else if (current == other) {
77 error = cmd_results_new(CMD_FAILURE,
78 "Cannot swap a container with itself");
250 } else if (container_has_ancestor(current, other) 79 } else if (container_has_ancestor(current, other)
251 || container_has_ancestor(other, current)) { 80 || container_has_ancestor(other, current)) {
252 error = cmd_results_new(CMD_FAILURE, 81 error = cmd_results_new(CMD_FAILURE,
diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c
index c30355de..be298a29 100644
--- a/sway/commands/title_align.c
+++ b/sway/commands/title_align.c
@@ -4,6 +4,10 @@
4#include "sway/tree/container.h" 4#include "sway/tree/container.h"
5#include "sway/tree/root.h" 5#include "sway/tree/root.h"
6 6
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 9d312470..0b2ea265 100644
--- a/sway/commands/title_format.c
+++ b/sway/commands/title_format.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -23,6 +22,5 @@ struct cmd_results *cmd_title_format(int argc, char **argv) {
23 } 22 }
24 view->title_format = format; 23 view->title_format = format;
25 view_update_title(view, true); 24 view_update_title(view, true);
26 config_update_font_height(true);
27 return cmd_results_new(CMD_SUCCESS, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL);
28} 26}
diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c
index 7c27c163..fa3db3c5 100644
--- a/sway/commands/titlebar_border_thickness.c
+++ b/sway/commands/titlebar_border_thickness.c
@@ -27,7 +27,6 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) {
27 "Expected output to have a workspace"); 27 "Expected output to have a workspace");
28 } 28 }
29 arrange_workspace(ws); 29 arrange_workspace(ws);
30 output_damage_whole(output);
31 } 30 }
32 31
33 return cmd_results_new(CMD_SUCCESS, NULL); 32 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c
index 29ce59ff..6999f7a2 100644
--- a/sway/commands/titlebar_padding.c
+++ b/sway/commands/titlebar_padding.c
@@ -33,7 +33,6 @@ struct cmd_results *cmd_titlebar_padding(int argc, char **argv) {
33 for (int i = 0; i < root->outputs->length; ++i) { 33 for (int i = 0; i < root->outputs->length; ++i) {
34 struct sway_output *output = root->outputs->items[i]; 34 struct sway_output *output = root->outputs->items[i];
35 arrange_workspace(output_get_active_workspace(output)); 35 arrange_workspace(output_get_active_workspace(output));
36 output_damage_whole(output);
37 } 36 }
38 37
39 return cmd_results_new(CMD_SUCCESS, NULL); 38 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
index 19274dfb..4aba5bae 100644
--- a/sway/commands/unmark.c
+++ b/sway/commands/unmark.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -8,9 +7,13 @@
8#include "log.h" 7#include "log.h"
9#include "stringop.h" 8#include "stringop.h"
10 9
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 fde386c7..f9131e0f 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -1,3 +1,4 @@
1#undef _POSIX_C_SOURCE
1#define _XOPEN_SOURCE 700 // for realpath 2#define _XOPEN_SOURCE 700 // for realpath
2#include <stdio.h> 3#include <stdio.h>
3#include <stdbool.h> 4#include <stdbool.h>
@@ -36,19 +37,26 @@
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}
@@ -82,6 +90,12 @@ static void free_mode(struct sway_mode *mode) {
82 } 90 }
83 list_free(mode->switch_bindings); 91 list_free(mode->switch_bindings);
84 } 92 }
93 if (mode->gesture_bindings) {
94 for (int i = 0; i < mode->gesture_bindings->length; i++) {
95 free_gesture_binding(mode->gesture_bindings->items[i]);
96 }
97 list_free(mode->gesture_bindings);
98 }
85 free(mode); 99 free(mode);
86} 100}
87 101
@@ -222,6 +236,7 @@ static void config_defaults(struct sway_config *config) {
222 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; 236 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
223 if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; 237 if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;
224 if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; 238 if (!(config->current_mode->switch_bindings = create_list())) goto cleanup;
239 if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup;
225 list_add(config->modes, config->current_mode); 240 list_add(config->modes, config->current_mode);
226 241
227 config->floating_mod = 0; 242 config->floating_mod = 0;
@@ -236,7 +251,7 @@ static void config_defaults(struct sway_config *config) {
236 config->default_layout = L_NONE; 251 config->default_layout = L_NONE;
237 config->default_orientation = L_NONE; 252 config->default_orientation = L_NONE;
238 if (!(config->font = strdup("monospace 10"))) goto cleanup; 253 if (!(config->font = strdup("monospace 10"))) goto cleanup;
239 config->font_height = 17; // height of monospace 10 254 config->font_description = pango_font_description_from_string(config->font);
240 config->urgent_timeout = 500; 255 config->urgent_timeout = 500;
241 config->focus_on_window_activation = FOWA_URGENT; 256 config->focus_on_window_activation = FOWA_URGENT;
242 config->popup_during_fullscreen = POPUP_SMART; 257 config->popup_during_fullscreen = POPUP_SMART;
@@ -266,8 +281,9 @@ static void config_defaults(struct sway_config *config) {
266 config->title_align = ALIGN_LEFT; 281 config->title_align = ALIGN_LEFT;
267 config->tiling_drag = true; 282 config->tiling_drag = true;
268 config->tiling_drag_threshold = 9; 283 config->tiling_drag_threshold = 9;
284 config->primary_selection = true;
269 285
270 config->smart_gaps = false; 286 config->smart_gaps = SMART_GAPS_OFF;
271 config->gaps_inner = 0; 287 config->gaps_inner = 0;
272 config->gaps_outer.top = 0; 288 config->gaps_outer.top = 0;
273 config->gaps_outer.right = 0; 289 config->gaps_outer.right = 0;
@@ -291,6 +307,8 @@ static void config_defaults(struct sway_config *config) {
291 config->hide_edge_borders_smart = ESMART_OFF; 307 config->hide_edge_borders_smart = ESMART_OFF;
292 config->hide_lone_tab = false; 308 config->hide_lone_tab = false;
293 309
310 config->has_focused_tab_title = false;
311
294 // border colors 312 // border colors
295 color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); 313 color_to_rgba(config->border_colors.focused.border, 0x4C7899FF);
296 color_to_rgba(config->border_colors.focused.background, 0x285577FF); 314 color_to_rgba(config->border_colors.focused.background, 0x285577FF);
@@ -326,8 +344,14 @@ static void config_defaults(struct sway_config *config) {
326 344
327 // The keysym to keycode translation 345 // The keysym to keycode translation
328 struct xkb_rule_names rules = {0}; 346 struct xkb_rule_names rules = {0};
329 config->keysym_translation_state = 347 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
330 keysym_translation_state_create(rules); 348 if (config->keysym_translation_state == NULL) {
349 config->keysym_translation_state = keysym_translation_state_create(rules,
350 XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
351 }
352 if (config->keysym_translation_state == NULL) {
353 goto cleanup;
354 }
331 355
332 return; 356 return;
333cleanup: 357cleanup:
@@ -342,13 +366,7 @@ static char *config_path(const char *prefix, const char *config_folder) {
342 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { 366 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
343 return NULL; 367 return NULL;
344 } 368 }
345 369 return format_str("%s/%s/config", prefix, config_folder);
346 const char *filename = "config";
347
348 size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
349 char *path = calloc(size, sizeof(char));
350 snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
351 return path;
352} 370}
353 371
354static char *get_config_path(void) { 372static char *get_config_path(void) {
@@ -358,10 +376,7 @@ static char *get_config_path(void) {
358 376
359 const char *config_home = getenv("XDG_CONFIG_HOME"); 377 const char *config_home = getenv("XDG_CONFIG_HOME");
360 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { 378 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) {
361 size_t size_fallback = 1 + strlen(home) + strlen("/.config"); 379 config_home_fallback = format_str("%s/.config", home);
362 config_home_fallback = calloc(size_fallback, sizeof(char));
363 if (config_home_fallback != NULL)
364 snprintf(config_home_fallback, size_fallback, "%s/.config", home);
365 config_home = config_home_fallback; 380 config_home = config_home_fallback;
366 } 381 }
367 382
@@ -465,6 +480,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
465 old_config->xwayland ? "enabled" : "disabled"); 480 old_config->xwayland ? "enabled" : "disabled");
466 config->xwayland = old_config->xwayland; 481 config->xwayland = old_config->xwayland;
467 482
483 // primary_selection can only be enabled/disabled at launch
484 sway_log(SWAY_DEBUG, "primary_selection will remain %s",
485 old_config->primary_selection ? "enabled" : "disabled");
486 config->primary_selection = old_config->primary_selection;
487
468 if (!config->validating) { 488 if (!config->validating) {
469 if (old_config->swaybg_client != NULL) { 489 if (old_config->swaybg_client != NULL) {
470 wl_client_destroy(old_config->swaybg_client); 490 wl_client_destroy(old_config->swaybg_client);
@@ -484,56 +504,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
484 504
485 config->reading = true; 505 config->reading = true;
486 506
487 // Read security configs 507 bool success = load_config(path, config, &config->swaynag_config_errors);
488 // TODO: Security
489 bool success = true;
490 /*
491 DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
492 if (!dir) {
493 sway_log(SWAY_ERROR,
494 "%s does not exist, sway will have no security configuration"
495 " and will probably be broken", SYSCONFDIR "/sway/security.d");
496 } else {
497 list_t *secconfigs = create_list();
498 char *base = SYSCONFDIR "/sway/security.d/";
499 struct dirent *ent = readdir(dir);
500 struct stat s;
501 while (ent != NULL) {
502 char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1);
503 strcpy(_path, base);
504 strcat(_path, ent->d_name);
505 lstat(_path, &s);
506 if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') {
507 list_add(secconfigs, _path);
508 }
509 else {
510 free(_path);
511 }
512 ent = readdir(dir);
513 }
514 closedir(dir);
515
516 list_qsort(secconfigs, qstrcmp);
517 for (int i = 0; i < secconfigs->length; ++i) {
518 char *_path = secconfigs->items[i];
519 if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||
520 (((s.st_mode & 0777) != 0644) &&
521 (s.st_mode & 0777) != 0444)) {
522 sway_log(SWAY_ERROR,
523 "Refusing to load %s - it must be owned by root "
524 "and mode 644 or 444", _path);
525 success = false;
526 } else {
527 success = success && load_config(_path, config);
528 }
529 }
530
531 list_free_items_and_destroy(secconfigs);
532 }
533 */
534
535 success = success && load_config(path, config,
536 &config->swaynag_config_errors);
537 508
538 if (validating) { 509 if (validating) {
539 free_config(config); 510 free_config(config);
@@ -541,6 +512,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
541 return success; 512 return success;
542 } 513 }
543 514
515 // Only really necessary if not explicitly `font` is set in the config.
516 config_update_font_height();
517
544 if (is_active && !validating) { 518 if (is_active && !validating) {
545 input_manager_verify_fallback_seat(); 519 input_manager_verify_fallback_seat();
546 520
@@ -558,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
558 } 532 }
559 sway_switch_retrigger_bindings_for_all(); 533 sway_switch_retrigger_bindings_for_all();
560 534
561 reset_outputs(); 535 apply_all_output_configs();
562 spawn_swaybg(); 536 spawn_swaybg();
563 537
564 config->reloading = false; 538 config->reloading = false;
@@ -911,23 +885,18 @@ void config_add_swaynag_warning(char *fmt, ...) {
911 if (config->reading && !config->validating) { 885 if (config->reading && !config->validating) {
912 va_list args; 886 va_list args;
913 va_start(args, fmt); 887 va_start(args, fmt);
914 size_t length = vsnprintf(NULL, 0, fmt, args) + 1; 888 char *str = vformat_str(fmt, args);
915 va_end(args); 889 va_end(args);
916 890 if (str == NULL) {
917 char *temp = malloc(length + 1);
918 if (!temp) {
919 sway_log(SWAY_ERROR, "Failed to allocate buffer for warning.");
920 return; 891 return;
921 } 892 }
922 893
923 va_start(args, fmt);
924 vsnprintf(temp, length, fmt, args);
925 va_end(args);
926
927 swaynag_log(config->swaynag_command, &config->swaynag_config_errors, 894 swaynag_log(config->swaynag_command, &config->swaynag_config_errors,
928 "Warning on line %i (%s) '%s': %s", 895 "Warning on line %i (%s) '%s': %s",
929 config->current_config_line_number, config->current_config_path, 896 config->current_config_line_number, config->current_config_path,
930 config->current_config_line, temp); 897 config->current_config_line, str);
898
899 free(str);
931 } 900 }
932} 901}
933 902
@@ -967,7 +936,7 @@ char *do_var_replacement(char *str) {
967 int offset = find - str; 936 int offset = find - str;
968 strncpy(newptr, str, offset); 937 strncpy(newptr, str, offset);
969 newptr += offset; 938 newptr += offset;
970 strncpy(newptr, var->value, vvlen); 939 memcpy(newptr, var->value, vvlen);
971 newptr += vvlen; 940 newptr += vvlen;
972 strcpy(newptr, find + vnlen); 941 strcpy(newptr, find + vnlen);
973 free(str); 942 free(str);
@@ -991,31 +960,11 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
991 return lenient_strcmp(wsa->workspace, wsb->workspace); 960 return lenient_strcmp(wsa->workspace, wsb->workspace);
992} 961}
993 962
994static void find_font_height_iterator(struct sway_container *con, void *data) {
995 size_t amount_below_baseline = con->title_height - con->title_baseline;
996 size_t extended_height = config->font_baseline + amount_below_baseline;
997 if (extended_height > config->font_height) {
998 config->font_height = extended_height;
999 }
1000}
1001
1002static void find_baseline_iterator(struct sway_container *con, void *data) {
1003 bool *recalculate = data;
1004 if (*recalculate) {
1005 container_calculate_title_height(con);
1006 }
1007 if (con->title_baseline > config->font_baseline) {
1008 config->font_baseline = con->title_baseline;
1009 }
1010}
1011 963
1012void config_update_font_height(bool recalculate) { 964void config_update_font_height(void) {
1013 size_t prev_max_height = config->font_height; 965 int prev_max_height = config->font_height;
1014 config->font_height = 0;
1015 config->font_baseline = 0;
1016 966
1017 root_for_each_container(find_baseline_iterator, &recalculate); 967 get_text_metrics(config->font_description, &config->font_height, &config->font_baseline);
1018 root_for_each_container(find_font_height_iterator, NULL);
1019 968
1020 if (config->font_height != prev_max_height) { 969 if (config->font_height != prev_max_height) {
1021 arrange_root(); 970 arrange_root();
@@ -1049,8 +998,12 @@ void translate_keysyms(struct input_config *input_config) {
1049 998
1050 struct xkb_rule_names rules = {0}; 999 struct xkb_rule_names rules = {0};
1051 input_config_fill_rule_names(input_config, &rules); 1000 input_config_fill_rule_names(input_config, &rules);
1052 config->keysym_translation_state = 1001 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
1053 keysym_translation_state_create(rules); 1002 if (config->keysym_translation_state == NULL) {
1003 sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state "
1004 "for device '%s'", input_config->identifier);
1005 return;
1006 }
1054 1007
1055 for (int i = 0; i < config->modes->length; ++i) { 1008 for (int i = 0; i < config->modes->length; ++i) {
1056 struct sway_mode *mode = config->modes->items[i]; 1009 struct sway_mode *mode = config->modes->items[i];
diff --git a/sway/config/bar.c b/sway/config/bar.c
index e09add44..908b2865 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <signal.h> 1#include <signal.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdio.h> 3#include <stdio.h>
@@ -219,6 +218,8 @@ static void invoke_swaybar(struct bar_config *bar) {
219 sigprocmask(SIG_SETMASK, &set, NULL); 218 sigprocmask(SIG_SETMASK, &set, NULL);
220 signal(SIGPIPE, SIG_DFL); 219 signal(SIGPIPE, SIG_DFL);
221 220
221 restore_nofile_limit();
222
222 pid = fork(); 223 pid = fork();
223 if (pid < 0) { 224 if (pid < 0) {
224 sway_log_errno(SWAY_ERROR, "fork failed"); 225 sway_log_errno(SWAY_ERROR, "fork failed");
@@ -254,7 +255,6 @@ static void invoke_swaybar(struct bar_config *bar) {
254 } 255 }
255 256
256 sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); 257 sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id);
257 return;
258} 258}
259 259
260void 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 7d0ed395..54af5d8e 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,13 +1,15 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
2#include <drm_fourcc.h>
3#include <stdbool.h> 3#include <stdbool.h>
4#include <string.h> 4#include <string.h>
5#include <sys/socket.h> 5#include <sys/socket.h>
6#include <sys/wait.h> 6#include <sys/wait.h>
7#include <unistd.h> 7#include <unistd.h>
8#include <wlr/config.h>
8#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
9#include <wlr/types/wlr_output_layout.h> 10#include <wlr/types/wlr_output_layout.h>
10#include <wlr/types/wlr_output.h> 11#include <wlr/types/wlr_output.h>
12#include <wlr/types/wlr_output_swapchain_manager.h>
11#include "sway/config.h" 13#include "sway/config.h"
12#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
13#include "sway/output.h" 15#include "sway/output.h"
@@ -15,6 +17,10 @@
15#include "log.h" 17#include "log.h"
16#include "util.h" 18#include "util.h"
17 19
20#if WLR_HAS_DRM_BACKEND
21#include <wlr/backend/drm.h>
22#endif
23
18int output_name_cmp(const void *item, const void *data) { 24int output_name_cmp(const void *item, const void *data) {
19 const struct output_config *output = item; 25 const struct output_config *output = item;
20 const char *name = data; 26 const char *name = data;
@@ -25,8 +31,10 @@ int output_name_cmp(const void *item, const void *data) {
25void output_get_identifier(char *identifier, size_t len, 31void output_get_identifier(char *identifier, size_t len,
26 struct sway_output *output) { 32 struct sway_output *output) {
27 struct wlr_output *wlr_output = output->wlr_output; 33 struct wlr_output *wlr_output = output->wlr_output;
28 snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, 34 snprintf(identifier, len, "%s %s %s",
29 wlr_output->serial); 35 wlr_output->make ? wlr_output->make : "Unknown",
36 wlr_output->model ? wlr_output->model : "Unknown",
37 wlr_output->serial ? wlr_output->serial : "Unknown");
30} 38}
31 39
32const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { 40const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) {
@@ -58,6 +66,7 @@ struct output_config *new_output_config(const char *name) {
58 oc->width = oc->height = -1; 66 oc->width = oc->height = -1;
59 oc->refresh_rate = -1; 67 oc->refresh_rate = -1;
60 oc->custom_mode = -1; 68 oc->custom_mode = -1;
69 oc->drm_mode.type = -1;
61 oc->x = oc->y = -1; 70 oc->x = oc->y = -1;
62 oc->scale = -1; 71 oc->scale = -1;
63 oc->scale_filter = SCALE_FILTER_DEFAULT; 72 oc->scale_filter = SCALE_FILTER_DEFAULT;
@@ -65,10 +74,77 @@ struct output_config *new_output_config(const char *name) {
65 oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; 74 oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
66 oc->max_render_time = -1; 75 oc->max_render_time = -1;
67 oc->adaptive_sync = -1; 76 oc->adaptive_sync = -1;
77 oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
78 oc->power = -1;
68 return oc; 79 return oc;
69} 80}
70 81
71void 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) {
72 if (src->enabled != -1) { 148 if (src->enabled != -1) {
73 dst->enabled = src->enabled; 149 dst->enabled = src->enabled;
74 } 150 }
@@ -99,6 +175,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
99 if (src->custom_mode != -1) { 175 if (src->custom_mode != -1) {
100 dst->custom_mode = src->custom_mode; 176 dst->custom_mode = src->custom_mode;
101 } 177 }
178 if (src->drm_mode.type != (uint32_t) -1) {
179 memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode));
180 }
102 if (src->transform != -1) { 181 if (src->transform != -1) {
103 dst->transform = src->transform; 182 dst->transform = src->transform;
104 } 183 }
@@ -108,6 +187,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
108 if (src->adaptive_sync != -1) { 187 if (src->adaptive_sync != -1) {
109 dst->adaptive_sync = src->adaptive_sync; 188 dst->adaptive_sync = src->adaptive_sync;
110 } 189 }
190 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
191 dst->render_bit_depth = src->render_bit_depth;
192 }
111 if (src->background) { 193 if (src->background) {
112 free(dst->background); 194 free(dst->background);
113 dst->background = strdup(src->background); 195 dst->background = strdup(src->background);
@@ -120,155 +202,124 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
120 free(dst->background_fallback); 202 free(dst->background_fallback);
121 dst->background_fallback = strdup(src->background_fallback); 203 dst->background_fallback = strdup(src->background_fallback);
122 } 204 }
123 if (src->dpms_state != 0) { 205 if (src->power != -1) {
124 dst->dpms_state = src->dpms_state; 206 dst->power = src->power;
125 } 207 }
126} 208}
127 209
128static void merge_wildcard_on_all(struct output_config *wildcard) { 210void store_output_config(struct output_config *oc) {
129 for (int i = 0; i < config->output_configs->length; i++) { 211 bool merged = false;
130 struct output_config *oc = config->output_configs->items[i]; 212 bool wildcard = strcmp(oc->name, "*") == 0;
131 if (strcmp(wildcard->name, oc->name) != 0) { 213 struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name);
132 sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name);
133 merge_output_config(oc, wildcard);
134 }
135 }
136}
137 214
138static void merge_id_on_name(struct output_config *oc) {
139 char *id_on_name = NULL;
140 char id[128]; 215 char id[128];
141 char *name = NULL; 216 if (output) {
142 struct sway_output *output;
143 wl_list_for_each(output, &root->all_outputs, link) {
144 name = output->wlr_output->name;
145 output_get_identifier(id, sizeof(id), output); 217 output_get_identifier(id, sizeof(id), output);
146 if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) {
147 size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1;
148 id_on_name = malloc(length);
149 if (!id_on_name) {
150 sway_log(SWAY_ERROR, "Failed to allocate id on name string");
151 return;
152 }
153 snprintf(id_on_name, length, "%s on %s", id, name);
154 break;
155 }
156 }
157
158 if (!id_on_name) {
159 return;
160 } 218 }
161 219
162 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 220 for (int i = 0; i < config->output_configs->length; i++) {
163 if (i >= 0) { 221 struct output_config *old = config->output_configs->items[i];
164 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); 222
165 merge_output_config(config->output_configs->items[i], oc); 223 // If the old config matches the new config's name, regardless of
166 } else { 224 // whether it was name or identifier, merge on top of the existing
167 // If both a name and identifier config, exist generate an id on name 225 // config. If the new config is a wildcard, this also merges on top of
168 int ni = list_seq_find(config->output_configs, output_name_cmp, name); 226 // old wildcard configs.
169 int ii = list_seq_find(config->output_configs, output_name_cmp, id); 227 if (strcmp(old->name, oc->name) == 0) {
170 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) 228 merge_output_config(old, oc);
171 || (ii >= 0 && strcmp(oc->name, name) == 0)) { 229 merged = true;
172 struct output_config *ion_oc = new_output_config(id_on_name); 230 continue;
173 if (ni >= 0) {
174 merge_output_config(ion_oc, config->output_configs->items[ni]);
175 }
176 if (ii >= 0) {
177 merge_output_config(ion_oc, config->output_configs->items[ii]);
178 }
179 merge_output_config(ion_oc, oc);
180 list_add(config->output_configs, ion_oc);
181 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
182 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
183 "transform %d) (bg %s %s) (dpms %d) (max render time: %d)",
184 ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
185 ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
186 ion_oc->transform, ion_oc->background,
187 ion_oc->background_option, ion_oc->dpms_state,
188 ion_oc->max_render_time);
189 } 231 }
190 }
191 free(id_on_name);
192}
193 232
194struct output_config *store_output_config(struct output_config *oc) { 233 // If the new config is a wildcard config we supersede all non-wildcard
195 bool wildcard = strcmp(oc->name, "*") == 0; 234 // configs. Old wildcard configs have already been handled above.
196 if (wildcard) { 235 if (wildcard) {
197 merge_wildcard_on_all(oc); 236 supersede_output_config(old, oc);
198 } else { 237 continue;
199 merge_id_on_name(oc); 238 }
200 }
201 239
202 int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); 240 // If the new config matches an output's name, and the old config
203 if (i >= 0) { 241 // matches on that output's identifier, supersede it.
204 sway_log(SWAY_DEBUG, "Merging on top of existing output config"); 242 if (output && strcmp(old->name, id) == 0 &&
205 struct output_config *current = config->output_configs->items[i]; 243 strcmp(oc->name, output->wlr_output->name) == 0) {
206 merge_output_config(current, oc); 244 supersede_output_config(old, oc);
207 free_output_config(oc);
208 oc = current;
209 } else if (!wildcard) {
210 sway_log(SWAY_DEBUG, "Adding non-wildcard output config");
211 i = list_seq_find(config->output_configs, output_name_cmp, "*");
212 if (i >= 0) {
213 sway_log(SWAY_DEBUG, "Merging on top of output * config");
214 struct output_config *current = new_output_config(oc->name);
215 merge_output_config(current, config->output_configs->items[i]);
216 merge_output_config(current, oc);
217 free_output_config(oc);
218 oc = current;
219 } 245 }
220 list_add(config->output_configs, oc);
221 } else {
222 // New wildcard config. Just add it
223 sway_log(SWAY_DEBUG, "Adding output * config");
224 list_add(config->output_configs, oc);
225 } 246 }
226 247
227 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 248 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
228 "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) " 249 "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
229 "(max render time: %d)", 250 "(max render time: %d)",
230 oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, 251 oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
231 oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), 252 oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
232 oc->transform, oc->background, oc->background_option, oc->dpms_state, 253 oc->transform, oc->background, oc->background_option, oc->power,
233 oc->max_render_time); 254 oc->max_render_time);
234 255
235 return oc; 256 // If the configuration was not merged into an existing configuration, add
257 // it to the list. Otherwise we're done with it and can free it.
258 if (!merged) {
259 list_add(config->output_configs, oc);
260 } else {
261 free_output_config(oc);
262 }
236} 263}
237 264
238static void set_mode(struct wlr_output *output, int width, int height, 265static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
239 float refresh_rate, bool custom) { 266 int width, int height, float refresh_rate, bool custom) {
240 // Not all floating point integers can be represented exactly 267 // Not all floating point integers can be represented exactly
241 // as (int)(1000 * mHz / 1000.f) 268 // as (int)(1000 * mHz / 1000.f)
242 // round() the result to avoid any error 269 // round() the result to avoid any error
243 int mhz = (int)round(refresh_rate * 1000); 270 int mhz = (int)roundf(refresh_rate * 1000);
271 // If no target refresh rate is given, match highest available
272 mhz = mhz <= 0 ? INT_MAX : mhz;
244 273
245 if (wl_list_empty(&output->modes) || custom) { 274 if (wl_list_empty(&output->modes) || custom) {
246 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); 275 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
247 wlr_output_set_custom_mode(output, width, height, 276 wlr_output_state_set_custom_mode(pending, width, height,
248 refresh_rate > 0 ? mhz : 0); 277 refresh_rate > 0 ? mhz : 0);
249 return; 278 return;
250 } 279 }
251 280
252 struct wlr_output_mode *mode, *best = NULL; 281 struct wlr_output_mode *mode, *best = NULL;
282 int best_diff_mhz = INT_MAX;
253 wl_list_for_each(mode, &output->modes, link) { 283 wl_list_for_each(mode, &output->modes, link) {
254 if (mode->width == width && mode->height == height) { 284 if (mode->width == width && mode->height == height) {
255 if (mode->refresh == mhz) { 285 int diff_mhz = abs(mode->refresh - mhz);
256 best = mode; 286 if (diff_mhz < best_diff_mhz) {
257 break; 287 best_diff_mhz = diff_mhz;
258 }
259 if (best == NULL || mode->refresh > best->refresh) {
260 best = mode; 288 best = mode;
289 if (best_diff_mhz == 0) {
290 break;
291 }
261 } 292 }
262 } 293 }
263 } 294 }
264 if (!best) { 295 if (best) {
265 sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); 296 sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s",
266 sway_log(SWAY_INFO, "Picking preferred mode instead"); 297 best->width, best->height, best->refresh / 1000.f, output->name);
267 best = wlr_output_preferred_mode(output);
268 } else { 298 } else {
269 sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); 299 best = wlr_output_preferred_mode(output);
300 sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, "
301 "applying preferred mode (%dx%d@%.3fHz)",
302 width, height, refresh_rate,
303 best->width, best->height, best->refresh / 1000.f);
270 } 304 }
271 wlr_output_set_mode(output, best); 305 wlr_output_state_set_mode(pending, best);
306}
307
308static void set_modeline(struct wlr_output *output,
309 struct wlr_output_state *pending, drmModeModeInfo *drm_mode) {
310#if WLR_HAS_DRM_BACKEND
311 if (!wlr_output_is_drm(output)) {
312 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
313 return;
314 }
315 sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name);
316 struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
317 if (mode) {
318 wlr_output_state_set_mode(pending, mode);
319 }
320#else
321 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
322#endif
272} 323}
273 324
274/* Some manufacturers hardcode the aspect-ratio of the output in the physical 325/* Some manufacturers hardcode the aspect-ratio of the output in the physical
@@ -289,23 +340,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
289// 1 inch = 25.4 mm 340// 1 inch = 25.4 mm
290#define MM_PER_INCH 25.4 341#define MM_PER_INCH 25.4
291 342
292static int compute_default_scale(struct wlr_output *output) { 343static int compute_default_scale(struct wlr_output *output,
344 struct wlr_output_state *pending) {
293 struct wlr_box box = { .width = output->width, .height = output->height }; 345 struct wlr_box box = { .width = output->width, .height = output->height };
294 if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { 346 if (pending->committed & WLR_OUTPUT_STATE_MODE) {
295 switch (output->pending.mode_type) { 347 switch (pending->mode_type) {
296 case WLR_OUTPUT_STATE_MODE_FIXED: 348 case WLR_OUTPUT_STATE_MODE_FIXED:
297 box.width = output->pending.mode->width; 349 box.width = pending->mode->width;
298 box.height = output->pending.mode->height; 350 box.height = pending->mode->height;
299 break; 351 break;
300 case WLR_OUTPUT_STATE_MODE_CUSTOM: 352 case WLR_OUTPUT_STATE_MODE_CUSTOM:
301 box.width = output->pending.custom_mode.width; 353 box.width = pending->custom_mode.width;
302 box.height = output->pending.custom_mode.height; 354 box.height = pending->custom_mode.height;
303 break; 355 break;
304 } 356 }
305 } 357 }
306 enum wl_output_transform transform = output->transform; 358 enum wl_output_transform transform = output->transform;
307 if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { 359 if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) {
308 transform = output->pending.transform; 360 transform = pending->transform;
309 } 361 }
310 wlr_box_transform(&box, &box, transform, box.width, box.height); 362 wlr_box_transform(&box, &box, transform, box.width, box.height);
311 363
@@ -334,42 +386,90 @@ static int compute_default_scale(struct wlr_output *output) {
334 return 2; 386 return 2;
335} 387}
336 388
389/* Lists of formats to try, in order, when a specific render bit depth has
390 * been asked for. The second to last format in each list should always
391 * be XRGB8888, as a reliable backup in case the others are not available;
392 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */
393static const uint32_t *bit_depth_preferences[] = {
394 [RENDER_BIT_DEPTH_8] = (const uint32_t []){
395 DRM_FORMAT_XRGB8888,
396 DRM_FORMAT_INVALID,
397 },
398 [RENDER_BIT_DEPTH_10] = (const uint32_t []){
399 DRM_FORMAT_XRGB2101010,
400 DRM_FORMAT_XBGR2101010,
401 DRM_FORMAT_XRGB8888,
402 DRM_FORMAT_INVALID,
403 },
404};
405
337static void queue_output_config(struct output_config *oc, 406static void queue_output_config(struct output_config *oc,
338 struct sway_output *output) { 407 struct sway_output *output, struct wlr_output_state *pending) {
339 if (output == root->noop_output) { 408 if (output == root->fallback_output) {
340 return; 409 return;
341 } 410 }
342 411
343 struct wlr_output *wlr_output = output->wlr_output; 412 struct wlr_output *wlr_output = output->wlr_output;
344 413
345 if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { 414 if (oc && (!oc->enabled || oc->power == 0)) {
346 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 415 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
347 wlr_output_enable(wlr_output, false); 416 wlr_output_state_set_enabled(pending, false);
348 return; 417 return;
349 } 418 }
350 419
351 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); 420 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
352 wlr_output_enable(wlr_output, true); 421 wlr_output_state_set_enabled(pending, true);
353 422
354 if (oc && oc->width > 0 && oc->height > 0) { 423 if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
424 sway_log(SWAY_DEBUG, "Set %s modeline",
425 wlr_output->name);
426 set_modeline(wlr_output, pending, &oc->drm_mode);
427 } else if (oc && oc->width > 0 && oc->height > 0) {
355 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", 428 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
356 wlr_output->name, oc->width, oc->height, oc->refresh_rate); 429 wlr_output->name, oc->width, oc->height, oc->refresh_rate);
357 set_mode(wlr_output, oc->width, oc->height, 430 set_mode(wlr_output, pending, oc->width, oc->height,
358 oc->refresh_rate, oc->custom_mode == 1); 431 oc->refresh_rate, oc->custom_mode == 1);
359 } else if (!wl_list_empty(&wlr_output->modes)) { 432 } else if (!wl_list_empty(&wlr_output->modes)) {
360 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 433 sway_log(SWAY_DEBUG, "Set preferred mode");
361 wlr_output_set_mode(wlr_output, mode); 434 struct wlr_output_mode *preferred_mode =
435 wlr_output_preferred_mode(wlr_output);
436 wlr_output_state_set_mode(pending, preferred_mode);
437
438 if (!wlr_output_test_state(wlr_output, pending)) {
439 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
440 "falling back to another mode");
441 struct wlr_output_mode *mode;
442 wl_list_for_each(mode, &wlr_output->modes, link) {
443 if (mode == preferred_mode) {
444 continue;
445 }
446
447 wlr_output_state_set_mode(pending, mode);
448 if (wlr_output_test_state(wlr_output, pending)) {
449 break;
450 }
451 }
452 }
362 } 453 }
363 454
364 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 455 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
365 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, 456 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name,
366 sway_wl_output_subpixel_to_string(oc->subpixel)); 457 sway_wl_output_subpixel_to_string(oc->subpixel));
367 wlr_output_set_subpixel(wlr_output, oc->subpixel); 458 wlr_output_state_set_subpixel(pending, oc->subpixel);
368 } 459 }
369 460
461 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
370 if (oc && oc->transform >= 0) { 462 if (oc && oc->transform >= 0) {
371 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); 463 tr = oc->transform;
372 wlr_output_set_transform(wlr_output, oc->transform); 464#if WLR_HAS_DRM_BACKEND
465 } else if (wlr_output_is_drm(wlr_output)) {
466 tr = wlr_drm_connector_get_panel_orientation(wlr_output);
467 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr);
468#endif
469 }
470 if (wlr_output->transform != tr) {
471 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr);
472 wlr_output_state_set_transform(pending, tr);
373 } 473 }
374 474
375 // Apply the scale last before the commit, because the scale auto-detection 475 // Apply the scale last before the commit, because the scale auto-detection
@@ -377,50 +477,58 @@ static void queue_output_config(struct output_config *oc,
377 float scale; 477 float scale;
378 if (oc && oc->scale > 0) { 478 if (oc && oc->scale > 0) {
379 scale = oc->scale; 479 scale = oc->scale;
480
481 // The factional-scale-v1 protocol uses increments of 120ths to send
482 // the scale factor to the client. Adjust the scale so that we use the
483 // same value as the clients'.
484 float adjusted_scale = round(scale * 120) / 120;
485 if (scale != adjusted_scale) {
486 sway_log(SWAY_INFO, "Adjusting output scale from %f to %f",
487 scale, adjusted_scale);
488 scale = adjusted_scale;
489 }
380 } else { 490 } else {
381 scale = compute_default_scale(wlr_output); 491 scale = compute_default_scale(wlr_output, pending);
382 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); 492 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
383 } 493 }
384 if (scale != wlr_output->scale) { 494 if (scale != wlr_output->scale) {
385 sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); 495 sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale);
386 wlr_output_set_scale(wlr_output, scale); 496 wlr_output_state_set_scale(pending, scale);
387 } 497 }
388 498
389 if (oc && oc->adaptive_sync != -1) { 499 if (oc && oc->adaptive_sync != -1) {
390 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 500 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
391 oc->adaptive_sync); 501 oc->adaptive_sync);
392 wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); 502 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
393 } 503 if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) {
394} 504 sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring");
395 505 wlr_output_state_set_adaptive_sync_enabled(pending, false);
396bool apply_output_config(struct output_config *oc, struct sway_output *output) { 506 }
397 if (output == root->noop_output) {
398 return false;
399 } 507 }
400 508
401 struct wlr_output *wlr_output = output->wlr_output; 509 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
402 510 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth];
403 // Flag to prevent the output mode event handler from calling us 511 assert(fmts);
404 output->enabling = (!oc || oc->enabled);
405 512
406 queue_output_config(oc, output); 513 for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) {
514 wlr_output_state_set_render_format(pending, fmts[i]);
515 if (wlr_output_test_state(wlr_output, pending)) {
516 break;
517 }
407 518
408 if (!oc || oc->dpms_state != DPMS_OFF) { 519 sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
409 output->current_mode = wlr_output->pending.mode; 520 "failed to work, falling back to next in "
521 "list, 0x%08x", fmts[i], fmts[i + 1]);
522 }
410 } 523 }
524}
411 525
412 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); 526static bool finalize_output_config(struct output_config *oc, struct sway_output *output) {
413 if (!wlr_output_commit(wlr_output)) { 527 if (output == root->fallback_output) {
414 // Failed to commit output changes, maybe the output is missing a CRTC.
415 // Leave the output disabled for now and try again when the output gets
416 // the mode we asked for.
417 sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name);
418 output->enabling = false;
419 return false; 528 return false;
420 } 529 }
421 530
422 output->enabling = false; 531 struct wlr_output *wlr_output = output->wlr_output;
423
424 if (oc && !oc->enabled) { 532 if (oc && !oc->enabled) {
425 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); 533 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
426 if (output->enabled) { 534 if (output->enabled) {
@@ -430,10 +538,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
430 return true; 538 return true;
431 } 539 }
432 540
433 if (config->reloading) {
434 output_damage_whole(output);
435 }
436
437 if (oc) { 541 if (oc) {
438 enum scale_filter_mode scale_filter_old = output->scale_filter; 542 enum scale_filter_mode scale_filter_old = output->scale_filter;
439 switch (oc->scale_filter) { 543 switch (oc->scale_filter) {
@@ -450,6 +554,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
450 if (scale_filter_old != output->scale_filter) { 554 if (scale_filter_old != output->scale_filter) {
451 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, 555 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name,
452 sway_output_scale_filter_to_string(output->scale_filter)); 556 sway_output_scale_filter_to_string(output->scale_filter));
557 wlr_damage_ring_add_whole(&output->scene_output->damage_ring);
453 } 558 }
454 } 559 }
455 560
@@ -462,12 +567,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
462 } 567 }
463 568
464 // Update output->{lx, ly, width, height} 569 // Update output->{lx, ly, width, height}
465 struct wlr_box *output_box = 570 struct wlr_box output_box;
466 wlr_output_layout_get_box(root->output_layout, wlr_output); 571 wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box);
467 output->lx = output_box->x; 572 output->lx = output_box.x;
468 output->ly = output_box->y; 573 output->ly = output_box.y;
469 output->width = output_box->width; 574 output->width = output_box.width;
470 output->height = output_box->height; 575 output->height = output_box.height;
471 576
472 if (!output->enabled) { 577 if (!output->enabled) {
473 output_enable(output); 578 output_enable(output);
@@ -479,29 +584,13 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
479 output->max_render_time = oc->max_render_time; 584 output->max_render_time = oc->max_render_time;
480 } 585 }
481 586
482 // Reconfigure all devices, since input config may have been applied before
483 // this output came online, and some config items (like map_to_output) are
484 // dependent on an output being present.
485 input_manager_configure_all_inputs();
486 // Reconfigure the cursor images, since the scale may have changed.
487 input_manager_configure_xcursor();
488 return true; 587 return true;
489} 588}
490 589
491bool test_output_config(struct output_config *oc, struct sway_output *output) {
492 if (output == root->noop_output) {
493 return false;
494 }
495
496 queue_output_config(oc, output);
497 bool ok = wlr_output_test(output->wlr_output);
498 wlr_output_rollback(output->wlr_output);
499 return ok;
500}
501
502static void default_output_config(struct output_config *oc, 590static void default_output_config(struct output_config *oc,
503 struct wlr_output *wlr_output) { 591 struct wlr_output *wlr_output) {
504 oc->enabled = 1; 592 oc->enabled = 1;
593 oc->power = 1;
505 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 594 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
506 if (mode != NULL) { 595 if (mode != NULL) {
507 oc->width = mode->width; 596 oc->width = mode->width;
@@ -514,147 +603,170 @@ static void default_output_config(struct output_config *oc,
514 struct sway_output *output = wlr_output->data; 603 struct sway_output *output = wlr_output->data;
515 oc->subpixel = output->detected_subpixel; 604 oc->subpixel = output->detected_subpixel;
516 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; 605 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
517 oc->dpms_state = DPMS_ON;
518 oc->max_render_time = 0; 606 oc->max_render_time = 0;
519} 607}
520 608
521static struct output_config *get_output_config(char *identifier, 609// find_output_config returns a merged output_config containing all stored
522 struct sway_output *sway_output) { 610// configuration that applies to the specified output.
611struct output_config *find_output_config(struct sway_output *sway_output) {
523 const char *name = sway_output->wlr_output->name; 612 const char *name = sway_output->wlr_output->name;
613 struct output_config *oc = NULL;
524 614
525 struct output_config *oc_id_on_name = NULL; 615 struct output_config *result = new_output_config(name);
526 struct output_config *oc_name = NULL; 616 if (config->reloading) {
527 struct output_config *oc_id = NULL; 617 default_output_config(result, sway_output->wlr_output);
618 }
528 619
529 size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; 620 char id[128];
530 char *id_on_name = malloc(length); 621 output_get_identifier(id, sizeof(id), sway_output);
531 snprintf(id_on_name, length, "%s on %s", identifier, name);
532 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name);
533 if (i >= 0) {
534 oc_id_on_name = config->output_configs->items[i];
535 } else {
536 i = list_seq_find(config->output_configs, output_name_cmp, name);
537 if (i >= 0) {
538 oc_name = config->output_configs->items[i];
539 }
540 622
541 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 623 int i;
542 if (i >= 0) { 624 bool match = false;
543 oc_id = config->output_configs->items[i]; 625 if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
544 } 626 match = true;
627 oc = config->output_configs->items[i];
628 merge_output_config(result, oc);
545 } 629 }
546 630 if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
547 struct output_config *result = new_output_config("temp"); 631 match = true;
548 if (config->reloading) { 632 oc = config->output_configs->items[i];
549 default_output_config(result, sway_output->wlr_output); 633 merge_output_config(result, oc);
550 } 634 }
551 if (oc_id_on_name) { 635 if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
552 // Already have an identifier on name config, use that 636 match = true;
553 free(result->name); 637 oc = config->output_configs->items[i];
554 result->name = strdup(id_on_name); 638 merge_output_config(result, oc);
555 merge_output_config(result, oc_id_on_name); 639 }
556 } else if (oc_name && oc_id) { 640
557 // Generate a config named `<identifier> on <name>` which contains a 641 if (!match && !config->reloading) {
558 // merged copy of the identifier on name. This will make sure that both 642 // No name, identifier, or wildcard config. Since we are not
559 // identifier and name configs are respected, with identifier getting 643 // reloading with defaults, the output config will be empty, so
560 // priority 644 // just return NULL
561 struct output_config *temp = new_output_config(id_on_name); 645 free_output_config(result);
562 merge_output_config(temp, oc_name); 646 return NULL;
563 merge_output_config(temp, oc_id);
564 list_add(config->output_configs, temp);
565
566 free(result->name);
567 result->name = strdup(id_on_name);
568 merge_output_config(result, temp);
569
570 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
571 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
572 " (dpms %d) (max render time: %d)", result->name, result->enabled,
573 result->width, result->height, result->refresh_rate,
574 result->x, result->y, result->scale, result->transform,
575 result->background, result->background_option, result->dpms_state,
576 result->max_render_time);
577 } else if (oc_name) {
578 // No identifier config, just return a copy of the name config
579 free(result->name);
580 result->name = strdup(name);
581 merge_output_config(result, oc_name);
582 } else if (oc_id) {
583 // No name config, just return a copy of the identifier config
584 free(result->name);
585 result->name = strdup(identifier);
586 merge_output_config(result, oc_id);
587 } else {
588 i = list_seq_find(config->output_configs, output_name_cmp, "*");
589 if (i >= 0) {
590 // No name or identifier config, but there is a wildcard config
591 free(result->name);
592 result->name = strdup("*");
593 merge_output_config(result, config->output_configs->items[i]);
594 } else if (!config->reloading) {
595 // No name, identifier, or wildcard config. Since we are not
596 // reloading with defaults, the output config will be empty, so
597 // just return NULL
598 free_output_config(result);
599 result = NULL;
600 }
601 } 647 }
602 648
603 free(id_on_name);
604 return result; 649 return result;
605} 650}
606 651
607struct output_config *find_output_config(struct sway_output *output) { 652bool apply_output_configs(struct matched_output_config *configs,
608 char id[128]; 653 size_t configs_len, bool test_only) {
609 output_get_identifier(id, sizeof(id), output); 654 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
610 return get_output_config(id, output); 655 if (!states) {
611} 656 return false;
657 }
612 658
613void apply_output_config_to_outputs(struct output_config *oc) { 659 sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len);
614 // Try to find the output container and apply configuration now. If 660 for (size_t idx = 0; idx < configs_len; idx++) {
615 // this is during startup then there will be no container and config 661 struct matched_output_config *cfg = &configs[idx];
616 // will be applied during normal "new output" event from wlroots. 662 struct wlr_backend_output_state *backend_state = &states[idx];
617 bool wildcard = strcmp(oc->name, "*") == 0;
618 char id[128];
619 struct sway_output *sway_output, *tmp;
620 wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) {
621 char *name = sway_output->wlr_output->name;
622 output_get_identifier(id, sizeof(id), sway_output);
623 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
624 struct output_config *current = get_output_config(id, sway_output);
625 if (!current) {
626 // No stored output config matched, apply oc directly
627 sway_log(SWAY_DEBUG, "Applying oc directly");
628 current = new_output_config(oc->name);
629 merge_output_config(current, oc);
630 }
631 apply_output_config(current, sway_output);
632 free_output_config(current);
633 663
634 if (!wildcard) { 664 backend_state->output = cfg->output->wlr_output;
635 // Stop looking if the output config isn't applicable to all 665 wlr_output_state_init(&backend_state->base);
636 // outputs 666
637 break; 667 sway_log(SWAY_DEBUG, "Preparing config for %s",
638 } 668 cfg->output->wlr_output->name);
669 queue_output_config(cfg->config, cfg->output, &backend_state->base);
670 }
671
672 struct wlr_output_swapchain_manager swapchain_mgr;
673 wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend);
674
675 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);
676 if (!ok) {
677 sway_log(SWAY_ERROR, "Swapchain prepare failed");
678 goto out;
679 }
680
681 if (test_only) {
682 // The swapchain manager already did a test for us
683 goto out;
684 }
685
686 for (size_t idx = 0; idx < configs_len; idx++) {
687 struct matched_output_config *cfg = &configs[idx];
688 struct wlr_backend_output_state *backend_state = &states[idx];
689
690 struct wlr_scene_output_state_options opts = {
691 .swapchain = wlr_output_swapchain_manager_get_swapchain(
692 &swapchain_mgr, backend_state->output),
693 };
694 struct wlr_scene_output *scene_output = cfg->output->scene_output;
695 struct wlr_output_state *state = &backend_state->base;
696 if (!wlr_scene_output_build_state(scene_output, state, &opts)) {
697 sway_log(SWAY_ERROR, "Building output state for '%s' failed",
698 backend_state->output->name);
699 goto out;
639 } 700 }
640 } 701 }
641 702
703 ok = wlr_backend_commit(server.backend, states, configs_len);
704 if (!ok) {
705 sway_log(SWAY_ERROR, "Backend commit failed");
706 goto out;
707 }
708
709 sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len);
710
711 wlr_output_swapchain_manager_apply(&swapchain_mgr);
712
713 for (size_t idx = 0; idx < configs_len; idx++) {
714 struct matched_output_config *cfg = &configs[idx];
715 sway_log(SWAY_DEBUG, "Finalizing config for %s",
716 cfg->output->wlr_output->name);
717 finalize_output_config(cfg->config, cfg->output);
718 }
719
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
642 struct sway_seat *seat; 735 struct sway_seat *seat;
643 wl_list_for_each(seat, &server.input->seats, link) { 736 wl_list_for_each(seat, &server.input->seats, link) {
644 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 737 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
645 cursor_rebase(seat->cursor); 738 cursor_rebase(seat->cursor);
646 } 739 }
740
741 return ok;
647} 742}
648 743
649void reset_outputs(void) { 744void apply_all_output_configs(void) {
650 struct output_config *oc = NULL; 745 size_t configs_len = wl_list_length(&root->all_outputs);
651 int i = list_seq_find(config->output_configs, output_name_cmp, "*"); 746 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
652 if (i >= 0) { 747 if (!configs) {
653 oc = config->output_configs->items[i]; 748 return;
654 } else {
655 oc = store_output_config(new_output_config("*"));
656 } 749 }
657 apply_output_config_to_outputs(oc); 750
751 int config_idx = 0;
752 struct sway_output *sway_output;
753 wl_list_for_each(sway_output, &root->all_outputs, link) {
754 if (sway_output == root->fallback_output) {
755 configs_len--;
756 continue;
757 }
758
759 struct matched_output_config *config = &configs[config_idx++];
760 config->output = sway_output;
761 config->config = find_output_config(sway_output);
762 }
763
764 apply_output_configs(configs, configs_len, false);
765 for (size_t idx = 0; idx < configs_len; idx++) {
766 struct matched_output_config *cfg = &configs[idx];
767 free_output_config(cfg->config);
768 }
769 free(configs);
658} 770}
659 771
660void free_output_config(struct output_config *oc) { 772void free_output_config(struct output_config *oc) {
@@ -704,6 +816,8 @@ static bool _spawn_swaybg(char **command) {
704 sway_log_errno(SWAY_ERROR, "fork failed"); 816 sway_log_errno(SWAY_ERROR, "fork failed");
705 return false; 817 return false;
706 } else if (pid == 0) { 818 } else if (pid == 0) {
819 restore_nofile_limit();
820
707 pid = fork(); 821 pid = fork();
708 if (pid < 0) { 822 if (pid < 0) {
709 sway_log_errno(SWAY_ERROR, "fork failed"); 823 sway_log_errno(SWAY_ERROR, "fork failed");
@@ -719,7 +833,9 @@ static bool _spawn_swaybg(char **command) {
719 setenv("WAYLAND_SOCKET", wayland_socket_str, true); 833 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
720 834
721 execvp(command[0], command); 835 execvp(command[0], command);
722 sway_log_errno(SWAY_ERROR, "execvp failed"); 836 sway_log_errno(SWAY_ERROR, "failed to execute '%s' "
837 "(background configuration probably not applied)",
838 command[0]);
723 _exit(EXIT_FAILURE); 839 _exit(EXIT_FAILURE);
724 } 840 }
725 _exit(EXIT_SUCCESS); 841 _exit(EXIT_SUCCESS);
@@ -729,12 +845,13 @@ static bool _spawn_swaybg(char **command) {
729 sway_log_errno(SWAY_ERROR, "close failed"); 845 sway_log_errno(SWAY_ERROR, "close failed");
730 return false; 846 return false;
731 } 847 }
732 if (waitpid(pid, NULL, 0) < 0) { 848 int fork_status = 0;
849 if (waitpid(pid, &fork_status, 0) < 0) {
733 sway_log_errno(SWAY_ERROR, "waitpid failed"); 850 sway_log_errno(SWAY_ERROR, "waitpid failed");
734 return false; 851 return false;
735 } 852 }
736 853
737 return true; 854 return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS;
738} 855}
739 856
740bool 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 d2a5566f..e16b4fa8 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -1,9 +1,9 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdbool.h> 3#include <stdbool.h>
5#include <strings.h> 4#include <strings.h>
6#include <pcre.h> 5#define PCRE2_CODE_UNIT_WIDTH 8
6#include <pcre2.h>
7#include "sway/criteria.h" 7#include "sway/criteria.h"
8#include "sway/tree/container.h" 8#include "sway/tree/container.h"
9#include "sway/config.h" 9#include "sway/config.h"
@@ -18,6 +18,7 @@
18bool criteria_is_empty(struct criteria *criteria) { 18bool criteria_is_empty(struct criteria *criteria) {
19 return !criteria->title 19 return !criteria->title
20 && !criteria->shell 20 && !criteria->shell
21 && !criteria->all
21 && !criteria->app_id 22 && !criteria->app_id
22 && !criteria->con_mark 23 && !criteria->con_mark
23 && !criteria->con_id 24 && !criteria->con_id
@@ -40,17 +41,19 @@ bool criteria_is_empty(struct criteria *criteria) {
40char *error = NULL; 41char *error = NULL;
41 42
42// Returns error string on failure or NULL otherwise. 43// Returns error string on failure or NULL otherwise.
43static bool generate_regex(pcre **regex, char *value) { 44static bool generate_regex(pcre2_code **regex, char *value) {
44 const char *reg_err; 45 int errorcode;
45 int offset; 46 PCRE2_SIZE offset;
46
47 *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, &reg_err, &offset, NULL);
48 47
48 *regex = pcre2_compile((PCRE2_SPTR)value, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_UCP, &errorcode, &offset, NULL);
49 if (!*regex) { 49 if (!*regex) {
50 PCRE2_UCHAR buffer[256];
51 pcre2_get_error_message(errorcode, buffer, sizeof(buffer));
52
50 const char *fmt = "Regex compilation for '%s' failed: %s"; 53 const char *fmt = "Regex compilation for '%s' failed: %s";
51 int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; 54 int len = strlen(fmt) + strlen(value) + strlen((char*) buffer) - 3;
52 error = malloc(len); 55 error = malloc(len);
53 snprintf(error, len, fmt, value, reg_err); 56 snprintf(error, len, fmt, value, buffer);
54 return false; 57 return false;
55 } 58 }
56 59
@@ -66,7 +69,7 @@ static bool pattern_create(struct pattern **pattern, char *value) {
66 if (strcmp(value, "__focused__") == 0) { 69 if (strcmp(value, "__focused__") == 0) {
67 (*pattern)->match_type = PATTERN_FOCUSED; 70 (*pattern)->match_type = PATTERN_FOCUSED;
68 } else { 71 } else {
69 (*pattern)->match_type = PATTERN_PCRE; 72 (*pattern)->match_type = PATTERN_PCRE2;
70 if (!generate_regex(&(*pattern)->regex, value)) { 73 if (!generate_regex(&(*pattern)->regex, value)) {
71 return false; 74 return false;
72 }; 75 };
@@ -77,7 +80,7 @@ static bool pattern_create(struct pattern **pattern, char *value) {
77static void pattern_destroy(struct pattern *pattern) { 80static void pattern_destroy(struct pattern *pattern) {
78 if (pattern) { 81 if (pattern) {
79 if (pattern->regex) { 82 if (pattern->regex) {
80 pcre_free(pattern->regex); 83 pcre2_code_free(pattern->regex);
81 } 84 }
82 free(pattern); 85 free(pattern);
83 } 86 }
@@ -93,14 +96,18 @@ void criteria_destroy(struct criteria *criteria) {
93 pattern_destroy(criteria->window_role); 96 pattern_destroy(criteria->window_role);
94#endif 97#endif
95 pattern_destroy(criteria->con_mark); 98 pattern_destroy(criteria->con_mark);
96 free(criteria->workspace); 99 pattern_destroy(criteria->workspace);
100 free(criteria->target);
97 free(criteria->cmdlist); 101 free(criteria->cmdlist);
98 free(criteria->raw); 102 free(criteria->raw);
99 free(criteria); 103 free(criteria);
100} 104}
101 105
102static int regex_cmp(const char *item, const pcre *regex) { 106static int regex_cmp(const char *item, const pcre2_code *regex) {
103 return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); 107 pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL);
108 int result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL);
109 pcre2_match_data_free(match_data);
110 return result;
104} 111}
105 112
106#if HAVE_XWAYLAND 113#if HAVE_XWAYLAND
@@ -155,7 +162,7 @@ static bool criteria_matches_container(struct criteria *criteria,
155 bool exists = false; 162 bool exists = false;
156 struct sway_container *con = container; 163 struct sway_container *con = container;
157 for (int i = 0; i < con->marks->length; ++i) { 164 for (int i = 0; i < con->marks->length; ++i) {
158 if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) == 0) { 165 if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) {
159 exists = true; 166 exists = true;
160 break; 167 break;
161 } 168 }
@@ -183,7 +190,7 @@ static bool criteria_matches_view(struct criteria *criteria,
183 if (criteria->title) { 190 if (criteria->title) {
184 const char *title = view_get_title(view); 191 const char *title = view_get_title(view);
185 if (!title) { 192 if (!title) {
186 return false; 193 title = "";
187 } 194 }
188 195
189 switch (criteria->title->match_type) { 196 switch (criteria->title->match_type) {
@@ -192,8 +199,8 @@ static bool criteria_matches_view(struct criteria *criteria,
192 return false; 199 return false;
193 } 200 }
194 break; 201 break;
195 case PATTERN_PCRE: 202 case PATTERN_PCRE2:
196 if (regex_cmp(title, criteria->title->regex) != 0) { 203 if (regex_cmp(title, criteria->title->regex) < 0) {
197 return false; 204 return false;
198 } 205 }
199 break; 206 break;
@@ -203,7 +210,7 @@ static bool criteria_matches_view(struct criteria *criteria,
203 if (criteria->shell) { 210 if (criteria->shell) {
204 const char *shell = view_get_shell(view); 211 const char *shell = view_get_shell(view);
205 if (!shell) { 212 if (!shell) {
206 return false; 213 shell = "";
207 } 214 }
208 215
209 switch (criteria->shell->match_type) { 216 switch (criteria->shell->match_type) {
@@ -212,8 +219,8 @@ static bool criteria_matches_view(struct criteria *criteria,
212 return false; 219 return false;
213 } 220 }
214 break; 221 break;
215 case PATTERN_PCRE: 222 case PATTERN_PCRE2:
216 if (regex_cmp(shell, criteria->shell->regex) != 0) { 223 if (regex_cmp(shell, criteria->shell->regex) < 0) {
217 return false; 224 return false;
218 } 225 }
219 break; 226 break;
@@ -223,7 +230,7 @@ static bool criteria_matches_view(struct criteria *criteria,
223 if (criteria->app_id) { 230 if (criteria->app_id) {
224 const char *app_id = view_get_app_id(view); 231 const char *app_id = view_get_app_id(view);
225 if (!app_id) { 232 if (!app_id) {
226 return false; 233 app_id = "";
227 } 234 }
228 235
229 switch (criteria->app_id->match_type) { 236 switch (criteria->app_id->match_type) {
@@ -232,8 +239,8 @@ static bool criteria_matches_view(struct criteria *criteria,
232 return false; 239 return false;
233 } 240 }
234 break; 241 break;
235 case PATTERN_PCRE: 242 case PATTERN_PCRE2:
236 if (regex_cmp(app_id, criteria->app_id->regex) != 0) { 243 if (regex_cmp(app_id, criteria->app_id->regex) < 0) {
237 return false; 244 return false;
238 } 245 }
239 break; 246 break;
@@ -255,7 +262,7 @@ static bool criteria_matches_view(struct criteria *criteria,
255 if (criteria->class) { 262 if (criteria->class) {
256 const char *class = view_get_class(view); 263 const char *class = view_get_class(view);
257 if (!class) { 264 if (!class) {
258 return false; 265 class = "";
259 } 266 }
260 267
261 switch (criteria->class->match_type) { 268 switch (criteria->class->match_type) {
@@ -264,8 +271,8 @@ static bool criteria_matches_view(struct criteria *criteria,
264 return false; 271 return false;
265 } 272 }
266 break; 273 break;
267 case PATTERN_PCRE: 274 case PATTERN_PCRE2:
268 if (regex_cmp(class, criteria->class->regex) != 0) { 275 if (regex_cmp(class, criteria->class->regex) < 0) {
269 return false; 276 return false;
270 } 277 }
271 break; 278 break;
@@ -275,17 +282,17 @@ static bool criteria_matches_view(struct criteria *criteria,
275 if (criteria->instance) { 282 if (criteria->instance) {
276 const char *instance = view_get_instance(view); 283 const char *instance = view_get_instance(view);
277 if (!instance) { 284 if (!instance) {
278 return false; 285 instance = "";
279 } 286 }
280 287
281 switch (criteria->instance->match_type) { 288 switch (criteria->instance->match_type) {
282 case PATTERN_FOCUSED: 289 case PATTERN_FOCUSED:
283 if (focused && strcmp(instance, view_get_instance(focused))) { 290 if (focused && lenient_strcmp(instance, view_get_instance(focused))) {
284 return false; 291 return false;
285 } 292 }
286 break; 293 break;
287 case PATTERN_PCRE: 294 case PATTERN_PCRE2:
288 if (regex_cmp(instance, criteria->instance->regex) != 0) { 295 if (regex_cmp(instance, criteria->instance->regex) < 0) {
289 return false; 296 return false;
290 } 297 }
291 break; 298 break;
@@ -295,17 +302,17 @@ static bool criteria_matches_view(struct criteria *criteria,
295 if (criteria->window_role) { 302 if (criteria->window_role) {
296 const char *window_role = view_get_window_role(view); 303 const char *window_role = view_get_window_role(view);
297 if (!window_role) { 304 if (!window_role) {
298 return false; 305 window_role = "";
299 } 306 }
300 307
301 switch (criteria->window_role->match_type) { 308 switch (criteria->window_role->match_type) {
302 case PATTERN_FOCUSED: 309 case PATTERN_FOCUSED:
303 if (focused && strcmp(window_role, view_get_window_role(focused))) { 310 if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) {
304 return false; 311 return false;
305 } 312 }
306 break; 313 break;
307 case PATTERN_PCRE: 314 case PATTERN_PCRE2:
308 if (regex_cmp(window_role, criteria->window_role->regex) != 0) { 315 if (regex_cmp(window_role, criteria->window_role->regex) < 0) {
309 return false; 316 return false;
310 } 317 }
311 break; 318 break;
@@ -363,8 +370,8 @@ static bool criteria_matches_view(struct criteria *criteria,
363 return false; 370 return false;
364 } 371 }
365 break; 372 break;
366 case PATTERN_PCRE: 373 case PATTERN_PCRE2:
367 if (regex_cmp(ws->name, criteria->workspace->regex) != 0) { 374 if (regex_cmp(ws->name, criteria->workspace->regex) < 0) {
368 return false; 375 return false;
369 } 376 }
370 break; 377 break;
@@ -449,6 +456,7 @@ static enum atom_name parse_window_type(const char *type) {
449#endif 456#endif
450 457
451enum criteria_token { 458enum criteria_token {
459 T_ALL,
452 T_APP_ID, 460 T_APP_ID,
453 T_CON_ID, 461 T_CON_ID,
454 T_CON_MARK, 462 T_CON_MARK,
@@ -471,7 +479,9 @@ enum criteria_token {
471}; 479};
472 480
473static enum criteria_token token_from_name(char *name) { 481static enum criteria_token token_from_name(char *name) {
474 if (strcmp(name, "app_id") == 0) { 482 if (strcmp(name, "all") == 0) {
483 return T_ALL;
484 } else if (strcmp(name, "app_id") == 0) {
475 return T_APP_ID; 485 return T_APP_ID;
476 } else if (strcmp(name, "con_id") == 0) { 486 } else if (strcmp(name, "con_id") == 0) {
477 return T_CON_ID; 487 return T_CON_ID;
@@ -517,8 +527,8 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
517 return false; 527 return false;
518 } 528 }
519 529
520 // Require value, unless token is floating or tiled 530 // Require value, unless token is all, floating or tiled
521 if (!value && token != T_FLOATING && token != T_TILING) { 531 if (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) {
522 const char *fmt = "Token '%s' requires a value"; 532 const char *fmt = "Token '%s' requires a value";
523 int len = strlen(fmt) + strlen(name) - 1; 533 int len = strlen(fmt) + strlen(name) - 1;
524 error = malloc(len); 534 error = malloc(len);
@@ -528,6 +538,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
528 538
529 char *endptr = NULL; 539 char *endptr = NULL;
530 switch (token) { 540 switch (token) {
541 case T_ALL:
542 criteria->all = true;
543 break;
531 case T_TITLE: 544 case T_TITLE:
532 pattern_create(&criteria->title, value); 545 pattern_create(&criteria->title, value);
533 break; 546 break;
@@ -676,7 +689,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
676 } 689 }
677 name = calloc(head - namestart + 1, 1); 690 name = calloc(head - namestart + 1, 1);
678 if (head != namestart) { 691 if (head != namestart) {
679 strncpy(name, namestart, head - namestart); 692 memcpy(name, namestart, head - namestart);
680 } 693 }
681 // Parse token value 694 // Parse token value
682 skip_spaces(&head); 695 skip_spaces(&head);
@@ -703,7 +716,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
703 } 716 }
704 } 717 }
705 value = calloc(head - valuestart + 1, 1); 718 value = calloc(head - valuestart + 1, 1);
706 strncpy(value, valuestart, head - valuestart); 719 memcpy(value, valuestart, head - valuestart);
707 if (in_quotes) { 720 if (in_quotes) {
708 ++head; 721 ++head;
709 in_quotes = false; 722 in_quotes = false;
@@ -734,7 +747,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
734 ++head; 747 ++head;
735 int len = head - raw; 748 int len = head - raw;
736 criteria->raw = calloc(len + 1, 1); 749 criteria->raw = calloc(len + 1, 1);
737 strncpy(criteria->raw, raw, len); 750 memcpy(criteria->raw, raw, len);
738 return criteria; 751 return criteria;
739 752
740cleanup: 753cleanup:
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
deleted file mode 100644
index ec45d80a..00000000
--- a/sway/desktop/desktop.c
+++ /dev/null
@@ -1,39 +0,0 @@
1#include "sway/tree/container.h"
2#include "sway/desktop.h"
3#include "sway/output.h"
4
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 = wlr_output_layout_get_box(
10 root->output_layout, output->wlr_output);
11 output_damage_surface(output, lx - output_box->x,
12 ly - output_box->y, surface, whole);
13 }
14}
15
16void desktop_damage_whole_container(struct sway_container *con) {
17 for (int i = 0; i < root->outputs->length; ++i) {
18 struct sway_output *output = root->outputs->items[i];
19 output_damage_whole_container(output, con);
20 }
21}
22
23void desktop_damage_box(struct wlr_box *box) {
24 for (int i = 0; i < root->outputs->length; ++i) {
25 struct sway_output *output = root->outputs->items[i];
26 output_damage_box(output, box);
27 }
28}
29
30void desktop_damage_view(struct sway_view *view) {
31 desktop_damage_whole_container(view->container);
32 struct wlr_box box = {
33 .x = view->container->current.content_x - view->geometry.x,
34 .y = view->container->current.content_y - view->geometry.y,
35 .width = view->surface->current.width,
36 .height = view->surface->current.height,
37 };
38 desktop_damage_box(&box);
39}
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c
index a6ad7166..f3af7aa1 100644
--- a/sway/desktop/idle_inhibit_v1.c
+++ b/sway/desktop/idle_inhibit_v1.c
@@ -1,5 +1,5 @@
1#include <stdlib.h> 1#include <stdlib.h>
2#include <wlr/types/wlr_idle.h> 2#include <wlr/types/wlr_idle_notify_v1.h>
3#include "log.h" 3#include "log.h"
4#include "sway/desktop/idle_inhibit_v1.h" 4#include "sway/desktop/idle_inhibit_v1.h"
5#include "sway/input/seat.h" 5#include "sway/input/seat.h"
@@ -11,7 +11,7 @@
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,35 +41,36 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
42 inhibitor->destroy.notify = handle_destroy; 41 inhibitor->destroy.notify = handle_destroy;
43 wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); 42 wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy);
44 43
45 sway_idle_inhibit_v1_check_active(manager); 44 sway_idle_inhibit_v1_check_active();
46} 45}
47 46
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->view == view && 73 inhibitor->view == view) {
73 inhibitor->mode != INHIBIT_IDLE_APPLICATION) {
74 return inhibitor; 74 return inhibitor;
75 } 75 }
76 } 76 }
@@ -79,11 +79,11 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view(
79 79
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->view == view && 86 view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) {
86 inhibitor->mode == INHIBIT_IDLE_APPLICATION) {
87 return inhibitor; 87 return inhibitor;
88 } 88 }
89 } 89 }
@@ -130,8 +130,8 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) {
130 return false; 130 return false;
131} 131}
132 132
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 cadc702a..4b2584b6 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -3,9 +3,12 @@
3#include <string.h> 3#include <string.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/types/wlr_layer_shell_v1.h> 5#include <wlr/types/wlr_layer_shell_v1.h>
6#include <wlr/types/wlr_output_damage.h>
7#include <wlr/types/wlr_output.h> 6#include <wlr/types/wlr_output.h>
7#include <wlr/types/wlr_scene.h>
8#include <wlr/types/wlr_subcompositor.h>
9#include <wlr/types/wlr_xdg_shell.h>
8#include "log.h" 10#include "log.h"
11#include "sway/scene_descriptor.h"
9#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
10#include "sway/input/cursor.h" 13#include "sway/input/cursor.h"
11#include "sway/input/input-manager.h" 14#include "sway/input/input-manager.h"
@@ -16,163 +19,55 @@
16#include "sway/tree/arrange.h" 19#include "sway/tree/arrange.h"
17#include "sway/tree/workspace.h" 20#include "sway/tree/workspace.h"
18 21
19static void apply_exclusive(struct wlr_box *usable_area, 22struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
20 uint32_t anchor, int32_t exclusive, 23 struct wlr_surface *surface) {
21 int32_t margin_top, int32_t margin_right, 24 struct wlr_layer_surface_v1 *layer;
22 int32_t margin_bottom, int32_t margin_left) { 25 do {
23 if (exclusive <= 0) { 26 if (!surface) {
24 return; 27 return NULL;
25 } 28 }
26 struct { 29 // Topmost layer surface
27 uint32_t singular_anchor; 30 if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) {
28 uint32_t anchor_triplet; 31 return layer;
29 int *positive_axis; 32 }
30 int *negative_axis; 33 // Layer subsurface
31 int margin; 34 if (wlr_subsurface_try_from_wlr_surface(surface)) {
32 } edges[] = { 35 surface = wlr_surface_get_root_surface(surface);
33 // Top 36 continue;
34 { 37 }
35 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, 38
36 .anchor_triplet = 39 // Layer surface popup
37 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | 40 struct wlr_xdg_surface *xdg_surface = NULL;
38 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | 41 if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) &&
39 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, 42 xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) {
40 .positive_axis = &usable_area->y, 43 if (!xdg_surface->popup->parent) {
41 .negative_axis = &usable_area->height, 44 return NULL;
42 .margin = margin_top,
43 },
44 // Bottom
45 {
46 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
47 .anchor_triplet =
48 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
49 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
50 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
51 .positive_axis = NULL,
52 .negative_axis = &usable_area->height,
53 .margin = margin_bottom,
54 },
55 // Left
56 {
57 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT,
58 .anchor_triplet =
59 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
60 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
61 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
62 .positive_axis = &usable_area->x,
63 .negative_axis = &usable_area->width,
64 .margin = margin_left,
65 },
66 // Right
67 {
68 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
69 .anchor_triplet =
70 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
71 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
72 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
73 .positive_axis = NULL,
74 .negative_axis = &usable_area->width,
75 .margin = margin_right,
76 },
77 };
78 for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
79 if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet)
80 && exclusive + edges[i].margin > 0) {
81 if (edges[i].positive_axis) {
82 *edges[i].positive_axis += exclusive + edges[i].margin;
83 }
84 if (edges[i].negative_axis) {
85 *edges[i].negative_axis -= exclusive + edges[i].margin;
86 } 45 }
87 break; 46 surface = wlr_surface_get_root_surface(xdg_surface->popup->parent);
47 continue;
88 } 48 }
89 } 49
50 // Return early if the surface is not a layer/xdg_popup/sub surface
51 return NULL;
52 } while (true);
90} 53}
91 54
92static 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,
93 struct wlr_box *usable_area, bool exclusive) { 56 struct wlr_box *usable_area, struct wlr_scene_tree *tree) {
94 struct sway_layer_surface *sway_layer; 57 struct wlr_scene_node *node;
95 struct wlr_box full_area = { 0 }; 58 wl_list_for_each(node, &tree->children, link) {
96 wlr_output_effective_resolution(output->wlr_output, 59 struct sway_layer_surface *surface = scene_descriptor_try_get(node,
97 &full_area.width, &full_area.height); 60 SWAY_SCENE_DESC_LAYER_SHELL);
98 wl_list_for_each(sway_layer, list, link) { 61 // surface could be null during destruction
99 struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; 62 if (!surface) {
100 struct wlr_layer_surface_v1_state *state = &layer->current;
101 if (exclusive != (state->exclusive_zone > 0)) {
102 continue; 63 continue;
103 } 64 }
104 struct wlr_box bounds; 65
105 if (state->exclusive_zone == -1) { 66 if (!surface->scene->layer_surface->initialized) {
106 bounds = full_area;
107 } else {
108 bounds = *usable_area;
109 }
110 struct wlr_box box = {
111 .width = state->desired_width,
112 .height = state->desired_height
113 };
114 // Horizontal axis
115 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
116 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
117 if (box.width == 0) {
118 box.x = bounds.x;
119 } else if ((state->anchor & both_horiz) == both_horiz) {
120 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
121 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
122 box.x = bounds.x;
123 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
124 box.x = bounds.x + (bounds.width - box.width);
125 } else {
126 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
127 }
128 // Vertical axis
129 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
130 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
131 if (box.height == 0) {
132 box.y = bounds.y;
133 } else if ((state->anchor & both_vert) == both_vert) {
134 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
135 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
136 box.y = bounds.y;
137 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
138 box.y = bounds.y + (bounds.height - box.height);
139 } else {
140 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
141 }
142 // Margin
143 if (box.width == 0) {
144 box.x += state->margin.left;
145 box.width = bounds.width -
146 (state->margin.left + state->margin.right);
147 } else if ((state->anchor & both_horiz) == both_horiz) {
148 // don't apply margins
149 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
150 box.x += state->margin.left;
151 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
152 box.x -= state->margin.right;
153 }
154 if (box.height == 0) {
155 box.y += state->margin.top;
156 box.height = bounds.height -
157 (state->margin.top + state->margin.bottom);
158 } else if ((state->anchor & both_vert) == both_vert) {
159 // don't apply margins
160 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
161 box.y += state->margin.top;
162 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
163 box.y -= state->margin.bottom;
164 }
165 if (box.width < 0 || box.height < 0) {
166 // TODO: Bubble up a protocol error?
167 wlr_layer_surface_v1_close(layer);
168 continue; 67 continue;
169 } 68 }
170 // Apply 69
171 sway_layer->geo = box; 70 wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area);
172 apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
173 state->margin.top, state->margin.right,
174 state->margin.bottom, state->margin.left);
175 wlr_layer_surface_v1_configure(layer, box.width, box.height);
176 } 71 }
177} 72}
178 73
@@ -180,81 +75,94 @@ void arrange_layers(struct sway_output *output) {
180 struct wlr_box usable_area = { 0 }; 75 struct wlr_box usable_area = { 0 };
181 wlr_output_effective_resolution(output->wlr_output, 76 wlr_output_effective_resolution(output->wlr_output,
182 &usable_area.width, &usable_area.height); 77 &usable_area.width, &usable_area.height);
78 const struct wlr_box full_area = usable_area;
79
80 arrange_surface(output, &full_area, &usable_area, output->layers.shell_background);
81 arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom);
82 arrange_surface(output, &full_area, &usable_area, output->layers.shell_top);
83 arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay);
183 84
184 // Arrange exclusive surfaces from top->bottom 85 if (!wlr_box_equal(&usable_area, &output->usable_area)) {
185 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
186 &usable_area, true);
187 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
188 &usable_area, true);
189 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
190 &usable_area, true);
191 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
192 &usable_area, true);
193
194 if (memcmp(&usable_area, &output->usable_area,
195 sizeof(struct wlr_box)) != 0) {
196 sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); 86 sway_log(SWAY_DEBUG, "Usable area changed, rearranging output");
197 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 87 output->usable_area = usable_area;
198 arrange_output(output); 88 arrange_output(output);
89 } else {
90 arrange_popups(root->layers.popup);
199 } 91 }
92}
200 93
201 // Arrange non-exclusive surfaces from top->bottom 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,280 +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 wl_list_remove(&sway_layer->output_destroy.link);
275 wl_list_remove(&sway_layer->link);
276 wl_list_init(&sway_layer->link);
277
278 if (set_focus) {
279 struct sway_layer_surface *layer =
280 find_mapped_layer_by_client(client, sway_layer->layer_surface->output);
281 if (layer) {
282 seat_set_focus_layer(seat, layer->layer_surface);
283 }
284 }
285 175
286 sway_layer->layer_surface->output = NULL; 176 layer->output = NULL;
287 wlr_layer_surface_v1_close(sway_layer->layer_surface); 177 wlr_scene_node_destroy(&layer->scene->tree->node);
288} 178}
289 179
290static void handle_surface_commit(struct wl_listener *listener, void *data) { 180static void handle_node_destroy(struct wl_listener *listener, void *data) {
291 struct sway_layer_surface *layer = 181 struct sway_layer_surface *layer =
292 wl_container_of(listener, layer, surface_commit); 182 wl_container_of(listener, layer, node_destroy);
293 struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface;
294 struct wlr_output *wlr_output = layer_surface->output;
295 if (wlr_output == NULL) {
296 return;
297 }
298 183
299 struct sway_output *output = wlr_output->data; 184 // destroy the scene descriptor straight away if it exists, otherwise
300 struct wlr_box old_geo = layer->geo; 185 // we will try to reflow still considering the destroyed node.
301 arrange_layers(output); 186 scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL);
302
303 bool geo_changed =
304 memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0;
305 bool layer_changed = layer->layer != layer_surface->current.layer;
306 if (layer_changed) {
307 wl_list_remove(&layer->link);
308 wl_list_insert(&output->layers[layer_surface->current.layer],
309 &layer->link);
310 layer->layer = layer_surface->current.layer;
311 }
312 if (geo_changed || layer_changed) {
313 output_damage_surface(output, old_geo.x, old_geo.y,
314 layer_surface->surface, true);
315 output_damage_surface(output, layer->geo.x, layer->geo.y,
316 layer_surface->surface, true);
317 } else {
318 output_damage_surface(output, layer->geo.x, layer->geo.y,
319 layer_surface->surface, false);
320 }
321
322 transaction_commit_dirty();
323}
324 187
325static void unmap(struct sway_layer_surface *sway_layer) { 188 // Determine if this layer is being used by an exclusive client. If it is,
326 struct sway_seat *seat; 189 // try and find another layer owned by this client to pass focus to.
327 wl_list_for_each(seat, &server.input->seats, link) { 190 struct sway_seat *seat = input_manager_get_default_seat();
328 if (seat->focused_layer == sway_layer->layer_surface) { 191 struct wl_client *client =
329 seat_set_focus_layer(seat, NULL); 192 wl_resource_get_client(layer->layer_surface->resource);
193 if (!server.session_lock.lock) {
194 struct sway_layer_surface *consider_layer =
195 find_mapped_layer_by_client(client, layer->output);
196 if (consider_layer) {
197 seat_set_focus_layer(seat, consider_layer->layer_surface);
330 } 198 }
331 } 199 }
332 200
333 cursor_rebase_all(); 201 if (layer->output) {
334 202 arrange_layers(layer->output);
335 struct wlr_output *wlr_output = sway_layer->layer_surface->output; 203 transaction_commit_dirty();
336 if (wlr_output == NULL) {
337 return;
338 }
339 struct sway_output *output = wlr_output->data;
340 if (output == NULL) {
341 return;
342 }
343 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
344 sway_layer->layer_surface->surface, true);
345}
346
347static void handle_destroy(struct wl_listener *listener, void *data) {
348 struct sway_layer_surface *sway_layer =
349 wl_container_of(listener, sway_layer, destroy);
350 sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)",
351 sway_layer->layer_surface->namespace);
352 if (sway_layer->layer_surface->mapped) {
353 unmap(sway_layer);
354 } 204 }
355 wl_list_remove(&sway_layer->link);
356 wl_list_remove(&sway_layer->destroy.link);
357 wl_list_remove(&sway_layer->map.link);
358 wl_list_remove(&sway_layer->unmap.link);
359 wl_list_remove(&sway_layer->surface_commit.link);
360 wl_list_remove(&sway_layer->new_popup.link);
361 wl_list_remove(&sway_layer->new_subsurface.link);
362 if (sway_layer->layer_surface->output != NULL) {
363 struct sway_output *output = sway_layer->layer_surface->output->data;
364 if (output != NULL) {
365 arrange_layers(output);
366 transaction_commit_dirty();
367 }
368 wl_list_remove(&sway_layer->output_destroy.link);
369 sway_layer->layer_surface->output = NULL;
370 }
371 free(sway_layer);
372}
373 205
374static void handle_map(struct wl_listener *listener, void *data) { 206 wlr_scene_node_destroy(&layer->popups->node);
375 struct sway_layer_surface *sway_layer = wl_container_of(listener,
376 sway_layer, map);
377 struct sway_output *output = sway_layer->layer_surface->output->data;
378 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
379 sway_layer->layer_surface->surface, true);
380 wlr_surface_send_enter(sway_layer->layer_surface->surface,
381 sway_layer->layer_surface->output);
382 cursor_rebase_all();
383}
384 207
385static void handle_unmap(struct wl_listener *listener, void *data) { 208 wl_list_remove(&layer->map.link);
386 struct sway_layer_surface *sway_layer = wl_container_of( 209 wl_list_remove(&layer->unmap.link);
387 listener, sway_layer, unmap); 210 wl_list_remove(&layer->surface_commit.link);
388 unmap(sway_layer); 211 wl_list_remove(&layer->node_destroy.link);
389} 212 wl_list_remove(&layer->output_destroy.link);
390 213
391static void subsurface_damage(struct sway_layer_subsurface *subsurface, 214 layer->layer_surface->data = NULL;
392 bool whole) {
393 struct sway_layer_surface *layer = subsurface->layer_surface;
394 struct wlr_output *wlr_output = layer->layer_surface->output;
395 if (!wlr_output) {
396 return;
397 }
398 struct sway_output *output = wlr_output->data;
399 int ox = subsurface->wlr_subsurface->current.x + layer->geo.x;
400 int oy = subsurface->wlr_subsurface->current.y + layer->geo.y;
401 output_damage_surface(
402 output, ox, oy, subsurface->wlr_subsurface->surface, whole);
403}
404
405static void subsurface_handle_unmap(struct wl_listener *listener, void *data) {
406 struct sway_layer_subsurface *subsurface =
407 wl_container_of(listener, subsurface, unmap);
408 subsurface_damage(subsurface, true);
409}
410 215
411static void subsurface_handle_map(struct wl_listener *listener, void *data) { 216 free(layer);
412 struct sway_layer_subsurface *subsurface =
413 wl_container_of(listener, subsurface, map);
414 subsurface_damage(subsurface, true);
415} 217}
416 218
417static void subsurface_handle_commit(struct wl_listener *listener, void *data) { 219static void handle_surface_commit(struct wl_listener *listener, void *data) {
418 struct sway_layer_subsurface *subsurface = 220 struct sway_layer_surface *surface =
419 wl_container_of(listener, subsurface, commit); 221 wl_container_of(listener, surface, surface_commit);
420 subsurface_damage(subsurface, false);
421}
422
423static void subsurface_handle_destroy(struct wl_listener *listener,
424 void *data) {
425 struct sway_layer_subsurface *subsurface =
426 wl_container_of(listener, subsurface, destroy);
427
428 wl_list_remove(&subsurface->map.link);
429 wl_list_remove(&subsurface->unmap.link);
430 wl_list_remove(&subsurface->destroy.link);
431 wl_list_remove(&subsurface->commit.link);
432 free(subsurface);
433}
434 222
435static struct sway_layer_subsurface *create_subsurface( 223 struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface;
436 struct wlr_subsurface *wlr_subsurface, 224 if (!layer_surface->initialized) {
437 struct sway_layer_surface *layer_surface) { 225 return;
438 struct sway_layer_subsurface *subsurface =
439 calloc(1, sizeof(struct sway_layer_subsurface));
440 if (subsurface == NULL) {
441 return NULL;
442 } 226 }
443 227
444 subsurface->wlr_subsurface = wlr_subsurface; 228 uint32_t committed = layer_surface->current.committed;
445 subsurface->layer_surface = layer_surface; 229 if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) {
446 230 enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer;
447 subsurface->map.notify = subsurface_handle_map; 231 struct wlr_scene_tree *output_layer = sway_layer_get_scene(
448 wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); 232 surface->output, layer_type);
449 subsurface->unmap.notify = subsurface_handle_unmap; 233 wlr_scene_node_reparent(&surface->scene->tree->node, output_layer);
450 wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); 234 }
451 subsurface->destroy.notify = subsurface_handle_destroy;
452 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
453 subsurface->commit.notify = subsurface_handle_commit;
454 wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit);
455
456 return subsurface;
457}
458
459static void handle_new_subsurface(struct wl_listener *listener, void *data) {
460 struct sway_layer_surface *sway_layer_surface =
461 wl_container_of(listener, sway_layer_surface, new_subsurface);
462 struct wlr_subsurface *wlr_subsurface = data;
463 create_subsurface(wlr_subsurface, sway_layer_surface);
464}
465
466 235
467static struct sway_layer_surface *popup_get_layer( 236 if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) {
468 struct sway_layer_popup *popup) { 237 surface->mapped = layer_surface->surface->mapped;
469 while (popup->parent_type == LAYER_PARENT_POPUP) { 238 arrange_layers(surface->output);
470 popup = popup->parent_popup; 239 transaction_commit_dirty();
471 } 240 }
472 return popup->parent_layer;
473} 241}
474 242
475static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { 243static void handle_map(struct wl_listener *listener, void *data) {
476 struct wlr_xdg_popup *popup = layer_popup->wlr_popup; 244 struct sway_layer_surface *surface = wl_container_of(listener,
477 struct wlr_surface *surface = popup->base->surface; 245 surface, map);
478 int popup_sx = popup->geometry.x - popup->base->geometry.x; 246
479 int popup_sy = popup->geometry.y - popup->base->geometry.y; 247 struct wlr_layer_surface_v1 *layer_surface =
480 int ox = popup_sx, oy = popup_sy; 248 surface->scene->layer_surface;
481 struct sway_layer_surface *layer; 249
482 while (true) { 250 // focus on new surface
483 if (layer_popup->parent_type == LAYER_PARENT_POPUP) { 251 if (layer_surface->current.keyboard_interactive &&
484 layer_popup = layer_popup->parent_popup; 252 (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ||
485 ox += layer_popup->wlr_popup->geometry.x; 253 layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) {
486 oy += layer_popup->wlr_popup->geometry.y; 254 struct sway_seat *seat;
487 } else { 255 wl_list_for_each(seat, &server.input->seats, link) {
488 layer = layer_popup->parent_layer; 256 // but only if the currently focused layer has a lower precedence
489 ox += layer->geo.x; 257 if (!seat->focused_layer ||
490 oy += layer->geo.y; 258 seat->focused_layer->current.layer >= layer_surface->current.layer) {
491 break; 259 seat_set_focus_layer(seat, layer_surface);
260 }
492 } 261 }
262 arrange_layers(surface->output);
493 } 263 }
494 struct wlr_output *wlr_output = layer->layer_surface->output;
495 struct sway_output *output = wlr_output->data;
496 output_damage_surface(output, ox, oy, surface, whole);
497}
498 264
499static void popup_handle_map(struct wl_listener *listener, void *data) { 265 cursor_rebase_all();
500 struct sway_layer_popup *popup = wl_container_of(listener, popup, map);
501 struct sway_layer_surface *layer = popup_get_layer(popup);
502 struct wlr_output *wlr_output = layer->layer_surface->output;
503 wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output);
504 popup_damage(popup, true);
505} 266}
506 267
507static void popup_handle_unmap(struct wl_listener *listener, void *data) { 268static void handle_unmap(struct wl_listener *listener, void *data) {
508 struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); 269 struct sway_layer_surface *surface = wl_container_of(
509 popup_damage(popup, true); 270 listener, surface, unmap);
510} 271 struct sway_seat *seat;
272 wl_list_for_each(seat, &server.input->seats, link) {
273 if (seat->focused_layer == surface->layer_surface) {
274 seat_set_focus_layer(seat, NULL);
275 }
276 }
511 277
512static void popup_handle_commit(struct wl_listener *listener, void *data) { 278 cursor_rebase_all();
513 struct sway_layer_popup *popup = wl_container_of(listener, popup, commit);
514 popup_damage(popup, false);
515} 279}
516 280
517static void popup_handle_destroy(struct wl_listener *listener, void *data) { 281static void popup_handle_destroy(struct wl_listener *listener, void *data) {
518 struct sway_layer_popup *popup = 282 struct sway_layer_popup *popup =
519 wl_container_of(listener, popup, destroy); 283 wl_container_of(listener, popup, destroy);
520 284
521 wl_list_remove(&popup->map.link);
522 wl_list_remove(&popup->unmap.link);
523 wl_list_remove(&popup->destroy.link); 285 wl_list_remove(&popup->destroy.link);
286 wl_list_remove(&popup->new_popup.link);
524 wl_list_remove(&popup->commit.link); 287 wl_list_remove(&popup->commit.link);
525 free(popup); 288 free(popup);
526} 289}
527 290
528static void popup_unconstrain(struct sway_layer_popup *popup) { 291static void popup_unconstrain(struct sway_layer_popup *popup) {
529 struct sway_layer_surface *layer = popup_get_layer(popup);
530 struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; 292 struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
293 struct sway_output *output = popup->toplevel->output;
531 294
532 struct sway_output *output = layer->layer_surface->output->data; 295 // if a client tries to create a popup while we are in the process of destroying
296 // its output, don't crash.
297 if (!output) {
298 return;
299 }
300
301 int lx, ly;
302 wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly);
533 303
534 // the output box expressed in the coordinate system of the toplevel parent 304 // the output box expressed in the coordinate system of the toplevel parent
535 // of the popup 305 // of the popup
536 struct wlr_box output_toplevel_sx_box = { 306 struct wlr_box output_toplevel_sx_box = {
537 .x = -layer->geo.x, 307 .x = output->lx - lx,
538 .y = -layer->geo.y, 308 .y = output->ly - ly,
539 .width = output->width, 309 .width = output->width,
540 .height = output->height, 310 .height = output->height,
541 }; 311 };
@@ -543,32 +313,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) {
543 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 313 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
544} 314}
545 315
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
546static void popup_handle_new_popup(struct wl_listener *listener, void *data); 323static void popup_handle_new_popup(struct wl_listener *listener, void *data);
547 324
548static 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,
549 enum layer_parent parent_type, void *parent) { 326 struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) {
550 struct sway_layer_popup *popup = 327 struct sway_layer_popup *popup = calloc(1, sizeof(*popup));
551 calloc(1, sizeof(struct sway_layer_popup));
552 if (popup == NULL) { 328 if (popup == NULL) {
553 return NULL; 329 return NULL;
554 } 330 }
555 331
332 popup->toplevel = toplevel;
556 popup->wlr_popup = wlr_popup; 333 popup->wlr_popup = wlr_popup;
557 popup->parent_type = parent_type; 334 popup->scene = wlr_scene_xdg_surface_create(parent,
558 popup->parent_layer = parent; 335 wlr_popup->base);
336
337 if (!popup->scene) {
338 free(popup);
339 return NULL;
340 }
559 341
560 popup->map.notify = popup_handle_map;
561 wl_signal_add(&wlr_popup->base->events.map, &popup->map);
562 popup->unmap.notify = popup_handle_unmap;
563 wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
564 popup->destroy.notify = popup_handle_destroy; 342 popup->destroy.notify = popup_handle_destroy;
565 wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); 343 wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
566 popup->commit.notify = popup_handle_commit;
567 wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
568 popup->new_popup.notify = popup_handle_new_popup; 344 popup->new_popup.notify = popup_handle_new_popup;
569 wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); 345 wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
570 346 popup->commit.notify = popup_handle_commit;
571 popup_unconstrain(popup); 347 wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
572 348
573 return popup; 349 return popup;
574} 350}
@@ -577,19 +353,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
577 struct sway_layer_popup *sway_layer_popup = 353 struct sway_layer_popup *sway_layer_popup =
578 wl_container_of(listener, sway_layer_popup, new_popup); 354 wl_container_of(listener, sway_layer_popup, new_popup);
579 struct wlr_xdg_popup *wlr_popup = data; 355 struct wlr_xdg_popup *wlr_popup = data;
580 create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); 356 create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene);
581} 357}
582 358
583static void handle_new_popup(struct wl_listener *listener, void *data) { 359static void handle_new_popup(struct wl_listener *listener, void *data) {
584 struct sway_layer_surface *sway_layer_surface = 360 struct sway_layer_surface *sway_layer_surface =
585 wl_container_of(listener, sway_layer_surface, new_popup); 361 wl_container_of(listener, sway_layer_surface, new_popup);
586 struct wlr_xdg_popup *wlr_popup = data; 362 struct wlr_xdg_popup *wlr_popup = data;
587 create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); 363 create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups);
588}
589
590struct sway_layer_surface *layer_from_wlr_layer_surface_v1(
591 struct wlr_layer_surface_v1 *layer_surface) {
592 return layer_surface->data;
593} 364}
594 365
595void handle_layer_shell_surface(struct wl_listener *listener, void *data) { 366void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
@@ -597,14 +368,14 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
597 sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 368 sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32
598 " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", 369 " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",",
599 layer_surface->namespace, 370 layer_surface->namespace,
600 layer_surface->client_pending.layer, 371 layer_surface->pending.layer,
601 layer_surface->client_pending.anchor, 372 layer_surface->pending.anchor,
602 layer_surface->client_pending.desired_width, 373 layer_surface->pending.desired_width,
603 layer_surface->client_pending.desired_height, 374 layer_surface->pending.desired_height,
604 layer_surface->client_pending.margin.top, 375 layer_surface->pending.margin.top,
605 layer_surface->client_pending.margin.right, 376 layer_surface->pending.margin.right,
606 layer_surface->client_pending.margin.bottom, 377 layer_surface->pending.margin.bottom,
607 layer_surface->client_pending.margin.left); 378 layer_surface->pending.margin.left);
608 379
609 if (!layer_surface->output) { 380 if (!layer_surface->output) {
610 // Assign last active output 381 // Assign last active output
@@ -616,12 +387,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
616 output = ws->output; 387 output = ws->output;
617 } 388 }
618 } 389 }
619 if (!output || output == root->noop_output) { 390 if (!output || output == root->fallback_output) {
620 if (!root->outputs->length) { 391 if (!root->outputs->length) {
621 sway_log(SWAY_ERROR, 392 sway_log(SWAY_ERROR,
622 "no output to auto-assign layer surface '%s' to", 393 "no output to auto-assign layer surface '%s' to",
623 layer_surface->namespace); 394 layer_surface->namespace);
624 wlr_layer_surface_v1_close(layer_surface); 395 wlr_layer_surface_v1_destroy(layer_surface);
625 return; 396 return;
626 } 397 }
627 output = root->outputs->items[0]; 398 output = root->outputs->items[0];
@@ -629,42 +400,51 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
629 layer_surface->output = output->wlr_output; 400 layer_surface->output = output->wlr_output;
630 } 401 }
631 402
632 struct sway_layer_surface *sway_layer = 403 struct sway_output *output = layer_surface->output->data;
633 calloc(1, sizeof(struct sway_layer_surface)); 404
634 if (!sway_layer) { 405 enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer;
406 struct wlr_scene_tree *output_layer = sway_layer_get_scene(
407 output, layer_type);
408 struct wlr_scene_layer_surface_v1 *scene_surface =
409 wlr_scene_layer_surface_v1_create(output_layer, layer_surface);
410 if (!scene_surface) {
411 sway_log(SWAY_ERROR, "Could not allocate a layer_surface_v1");
635 return; 412 return;
636 } 413 }
637 414
638 sway_layer->surface_commit.notify = handle_surface_commit; 415 struct sway_layer_surface *surface =
639 wl_signal_add(&layer_surface->surface->events.commit, 416 sway_layer_surface_create(scene_surface);
640 &sway_layer->surface_commit); 417 if (!surface) {
641 418 wlr_layer_surface_v1_destroy(layer_surface);
642 sway_layer->destroy.notify = handle_destroy;
643 wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy);
644 sway_layer->map.notify = handle_map;
645 wl_signal_add(&layer_surface->events.map, &sway_layer->map);
646 sway_layer->unmap.notify = handle_unmap;
647 wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap);
648 sway_layer->new_popup.notify = handle_new_popup;
649 wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup);
650 sway_layer->new_subsurface.notify = handle_new_subsurface;
651 wl_signal_add(&layer_surface->surface->events.new_subsurface,
652 &sway_layer->new_subsurface);
653
654 sway_layer->layer_surface = layer_surface;
655 layer_surface->data = sway_layer;
656 419
657 struct sway_output *output = layer_surface->output->data; 420 sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface");
658 sway_layer->output_destroy.notify = handle_output_destroy; 421 return;
659 wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); 422 }
660 423
661 wl_list_insert(&output->layers[layer_surface->client_pending.layer], 424 if (!scene_descriptor_assign(&scene_surface->tree->node,
662 &sway_layer->link); 425 SWAY_SCENE_DESC_LAYER_SHELL, surface)) {
663 426 sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor");
664 // Temporarily set the layer's current state to client_pending 427 // destroying the layer_surface will also destroy its corresponding
665 // So that we can easily arrange it 428 // scene node
666 struct wlr_layer_surface_v1_state old_state = layer_surface->current; 429 wlr_layer_surface_v1_destroy(layer_surface);
667 layer_surface->current = layer_surface->client_pending; 430 return;
668 arrange_layers(output); 431 }
669 layer_surface->current = old_state; 432
433 surface->output = output;
434
435 surface->surface_commit.notify = handle_surface_commit;
436 wl_signal_add(&layer_surface->surface->events.commit,
437 &surface->surface_commit);
438 surface->map.notify = handle_map;
439 wl_signal_add(&layer_surface->surface->events.map, &surface->map);
440 surface->unmap.notify = handle_unmap;
441 wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap);
442 surface->new_popup.notify = handle_new_popup;
443 wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup);
444
445 surface->output_destroy.notify = handle_output_destroy;
446 wl_signal_add(&output->events.disable, &surface->output_destroy);
447
448 surface->node_destroy.notify = handle_node_destroy;
449 wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy);
670} 450}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 2f2ab4bc..70987feb 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -1,41 +1,61 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <strings.h> 3#include <strings.h>
5#include <time.h> 4#include <time.h>
6#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/config.h>
7#include <wlr/backend/headless.h>
8#include <wlr/render/swapchain.h>
7#include <wlr/render/wlr_renderer.h> 9#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_buffer.h> 10#include <wlr/types/wlr_buffer.h>
11#include <wlr/types/wlr_gamma_control_v1.h>
9#include <wlr/types/wlr_matrix.h> 12#include <wlr/types/wlr_matrix.h>
10#include <wlr/types/wlr_output_damage.h>
11#include <wlr/types/wlr_output_layout.h> 13#include <wlr/types/wlr_output_layout.h>
14#include <wlr/types/wlr_output_management_v1.h>
15#include <wlr/types/wlr_output_power_management_v1.h>
12#include <wlr/types/wlr_output.h> 16#include <wlr/types/wlr_output.h>
13#include <wlr/types/wlr_presentation_time.h> 17#include <wlr/types/wlr_presentation_time.h>
14#include <wlr/types/wlr_surface.h> 18#include <wlr/types/wlr_compositor.h>
15#include <wlr/util/region.h> 19#include <wlr/util/region.h>
20#include <wlr/util/transform.h>
16#include "config.h" 21#include "config.h"
17#include "log.h" 22#include "log.h"
18#include "sway/config.h" 23#include "sway/config.h"
19#include "sway/desktop/transaction.h" 24#include "sway/desktop/transaction.h"
20#include "sway/input/input-manager.h" 25#include "sway/input/input-manager.h"
21#include "sway/input/seat.h" 26#include "sway/input/seat.h"
27#include "sway/ipc-server.h"
22#include "sway/layers.h" 28#include "sway/layers.h"
23#include "sway/output.h" 29#include "sway/output.h"
30#include "sway/scene_descriptor.h"
24#include "sway/server.h" 31#include "sway/server.h"
25#include "sway/surface.h"
26#include "sway/tree/arrange.h" 32#include "sway/tree/arrange.h"
27#include "sway/tree/container.h" 33#include "sway/tree/container.h"
28#include "sway/tree/root.h" 34#include "sway/tree/root.h"
29#include "sway/tree/view.h" 35#include "sway/tree/view.h"
30#include "sway/tree/workspace.h" 36#include "sway/tree/workspace.h"
31 37
38#if WLR_HAS_DRM_BACKEND
39#include <wlr/backend/drm.h>
40#include <wlr/types/wlr_drm_lease_v1.h>
41#endif
42
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
32struct 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) {
33 for (int i = 0; i < root->outputs->length; ++i) { 56 for (int i = 0; i < root->outputs->length; ++i) {
34 struct sway_output *output = root->outputs->items[i]; 57 struct sway_output *output = root->outputs->items[i];
35 char identifier[128]; 58 if (output_match_name_or_id(output, name_or_id)) {
36 output_get_identifier(identifier, sizeof(identifier), output);
37 if (strcasecmp(identifier, name_or_id) == 0
38 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
39 return output; 59 return output;
40 } 60 }
41 } 61 }
@@ -45,513 +65,217 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) {
45struct 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) {
46 struct sway_output *output; 66 struct sway_output *output;
47 wl_list_for_each(output, &root->all_outputs, link) { 67 wl_list_for_each(output, &root->all_outputs, link) {
48 char identifier[128]; 68 if (output_match_name_or_id(output, name_or_id)) {
49 output_get_identifier(identifier, sizeof(identifier), output);
50 if (strcasecmp(identifier, name_or_id) == 0
51 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
52 return output; 69 return output;
53 } 70 }
54 } 71 }
55 return NULL; 72 return NULL;
56} 73}
57 74
58struct surface_iterator_data {
59 sway_surface_iterator_func_t user_iterator;
60 void *user_data;
61
62 struct sway_output *output;
63 struct sway_view *view;
64 double ox, oy;
65 int width, height;
66};
67
68static bool get_surface_box(struct surface_iterator_data *data,
69 struct wlr_surface *surface, int sx, int sy,
70 struct wlr_box *surface_box) {
71 struct sway_output *output = data->output;
72
73 if (!wlr_surface_has_buffer(surface)) {
74 return false;
75 }
76
77 int sw = surface->current.width;
78 int sh = surface->current.height;
79 75
80 struct wlr_box box = { 76struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
81 .x = floor(data->ox + sx), 77 struct sway_seat *seat = input_manager_current_seat();
82 .y = floor(data->oy + sy), 78 struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node);
83 .width = sw, 79 if (!focus) {
84 .height = sh, 80 if (!output->workspaces->length) {
85 }; 81 return NULL;
86 if (surface_box != NULL) { 82 }
87 memcpy(surface_box, &box, sizeof(struct wlr_box)); 83 return output->workspaces->items[0];
88 } 84 }
89 85 return focus->sway_workspace;
90 struct wlr_box output_box = {
91 .width = output->width,
92 .height = output->height,
93 };
94
95 struct wlr_box intersection;
96 return wlr_box_intersection(&intersection, &output_box, &box);
97} 86}
98 87
99static void output_for_each_surface_iterator(struct wlr_surface *surface, 88struct send_frame_done_data {
100 int sx, int sy, void *_data) { 89 struct timespec when;
101 struct surface_iterator_data *data = _data; 90 int msec_until_refresh;
102 91 struct sway_output *output;
103 struct wlr_box box; 92};
104 bool intersects = get_surface_box(data, surface, sx, sy, &box);
105 if (!intersects) {
106 return;
107 }
108 93
109 data->user_iterator(data->output, data->view, surface, &box, 94struct buffer_timer {
110 data->user_data); 95 struct wl_listener destroy;
111} 96 struct wl_event_source *frame_done_timer;
97};
112 98
113void output_surface_for_each_surface(struct sway_output *output, 99static int handle_buffer_timer(void *data) {
114 struct wlr_surface *surface, double ox, double oy, 100 struct wlr_scene_buffer *buffer = data;
115 sway_surface_iterator_func_t iterator, void *user_data) {
116 struct surface_iterator_data data = {
117 .user_iterator = iterator,
118 .user_data = user_data,
119 .output = output,
120 .view = NULL,
121 .ox = ox,
122 .oy = oy,
123 .width = surface->current.width,
124 .height = surface->current.height,
125 };
126
127 wlr_surface_for_each_surface(surface,
128 output_for_each_surface_iterator, &data);
129}
130 101
131void output_view_for_each_surface(struct sway_output *output, 102 struct timespec now;
132 struct sway_view *view, sway_surface_iterator_func_t iterator, 103 clock_gettime(CLOCK_MONOTONIC, &now);
133 void *user_data) { 104 wlr_scene_buffer_send_frame_done(buffer, &now);
134 struct surface_iterator_data data = { 105 return 0;
135 .user_iterator = iterator,
136 .user_data = user_data,
137 .output = output,
138 .view = view,
139 .ox = view->container->surface_x - output->lx
140 - view->geometry.x,
141 .oy = view->container->surface_y - output->ly
142 - view->geometry.y,
143 .width = view->container->current.content_width,
144 .height = view->container->current.content_height,
145 };
146
147 view_for_each_surface(view, output_for_each_surface_iterator, &data);
148} 106}
149 107
150void output_view_for_each_popup_surface(struct sway_output *output, 108static void handle_buffer_timer_destroy(struct wl_listener *listener,
151 struct sway_view *view, sway_surface_iterator_func_t iterator, 109 void *data) {
152 void *user_data) { 110 struct buffer_timer *timer = wl_container_of(listener, timer, destroy);
153 struct surface_iterator_data data = {
154 .user_iterator = iterator,
155 .user_data = user_data,
156 .output = output,
157 .view = view,
158 .ox = view->container->surface_x - output->lx
159 - view->geometry.x,
160 .oy = view->container->surface_y - output->ly
161 - view->geometry.y,
162 .width = view->container->current.content_width,
163 .height = view->container->current.content_height,
164 };
165
166 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data);
167}
168 111
169void output_layer_for_each_surface(struct sway_output *output, 112 wl_list_remove(&timer->destroy.link);
170 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 113 wl_event_source_remove(timer->frame_done_timer);
171 void *user_data) { 114 free(timer);
172 struct sway_layer_surface *layer_surface;
173 wl_list_for_each(layer_surface, layer_surfaces, link) {
174 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
175 layer_surface->layer_surface;
176 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
177 struct surface_iterator_data data = {
178 .user_iterator = iterator,
179 .user_data = user_data,
180 .output = output,
181 .view = NULL,
182 .ox = layer_surface->geo.x,
183 .oy = layer_surface->geo.y,
184 .width = surface->current.width,
185 .height = surface->current.height,
186 };
187 wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1,
188 output_for_each_surface_iterator, &data);
189 }
190} 115}
191 116
192void output_layer_for_each_toplevel_surface(struct sway_output *output, 117static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) {
193 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 118 struct buffer_timer *timer =
194 void *user_data) { 119 scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER);
195 struct sway_layer_surface *layer_surface; 120 if (timer) {
196 wl_list_for_each(layer_surface, layer_surfaces, link) { 121 return timer;
197 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
198 layer_surface->layer_surface;
199 output_surface_for_each_surface(output, wlr_layer_surface_v1->surface,
200 layer_surface->geo.x, layer_surface->geo.y, iterator,
201 user_data);
202 } 122 }
203}
204 123
205 124 timer = calloc(1, sizeof(struct buffer_timer));
206void output_layer_for_each_popup_surface(struct sway_output *output, 125 if (!timer) {
207 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 126 return NULL;
208 void *user_data) {
209 struct sway_layer_surface *layer_surface;
210 wl_list_for_each(layer_surface, layer_surfaces, link) {
211 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
212 layer_surface->layer_surface;
213 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
214 struct surface_iterator_data data = {
215 .user_iterator = iterator,
216 .user_data = user_data,
217 .output = output,
218 .view = NULL,
219 .ox = layer_surface->geo.x,
220 .oy = layer_surface->geo.y,
221 .width = surface->current.width,
222 .height = surface->current.height,
223 };
224 wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1,
225 output_for_each_surface_iterator, &data);
226 } 127 }
227}
228 128
229#if HAVE_XWAYLAND 129 timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop,
230void output_unmanaged_for_each_surface(struct sway_output *output, 130 handle_buffer_timer, buffer);
231 struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, 131 if (!timer->frame_done_timer) {
232 void *user_data) { 132 free(timer);
233 struct sway_xwayland_unmanaged *unmanaged_surface; 133 return NULL;
234 wl_list_for_each(unmanaged_surface, unmanaged, link) {
235 struct wlr_xwayland_surface *xsurface =
236 unmanaged_surface->wlr_xwayland_surface;
237 double ox = unmanaged_surface->lx - output->lx;
238 double oy = unmanaged_surface->ly - output->ly;
239
240 output_surface_for_each_surface(output, xsurface->surface, ox, oy,
241 iterator, user_data);
242 } 134 }
243}
244#endif
245 135
246void output_drag_icons_for_each_surface(struct sway_output *output, 136 scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer);
247 struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
248 void *user_data) {
249 struct sway_drag_icon *drag_icon;
250 wl_list_for_each(drag_icon, drag_icons, link) {
251 double ox = drag_icon->x - output->lx;
252 double oy = drag_icon->y - output->ly;
253
254 if (drag_icon->wlr_drag_icon->mapped) {
255 output_surface_for_each_surface(output,
256 drag_icon->wlr_drag_icon->surface, ox, oy,
257 iterator, user_data);
258 }
259 }
260}
261 137
262static void for_each_surface_container_iterator(struct sway_container *con, 138 timer->destroy.notify = handle_buffer_timer_destroy;
263 void *_data) { 139 wl_signal_add(&buffer->node.events.destroy, &timer->destroy);
264 if (!con->view || !view_is_visible(con->view)) {
265 return;
266 }
267 140
268 struct surface_iterator_data *data = _data; 141 return timer;
269 output_view_for_each_surface(data->output, con->view,
270 data->user_iterator, data->user_data);
271} 142}
272 143
273static void output_for_each_surface(struct sway_output *output, 144static void send_frame_done_iterator(struct wlr_scene_buffer *buffer,
274 sway_surface_iterator_func_t iterator, void *user_data) { 145 int x, int y, void *user_data) {
275 if (output_has_opaque_overlay_layer_surface(output)) { 146 struct send_frame_done_data *data = user_data;
276 goto overlay; 147 struct sway_output *output = data->output;
277 } 148 int view_max_render_time = 0;
278
279 struct surface_iterator_data data = {
280 .user_iterator = iterator,
281 .user_data = user_data,
282 .output = output,
283 .view = NULL,
284 };
285 149
286 struct sway_workspace *workspace = output_get_active_workspace(output); 150 if (buffer->primary_output != data->output->scene_output) {
287 struct sway_container *fullscreen_con = root->fullscreen_global; 151 return;
288 if (!fullscreen_con) {
289 if (!workspace) {
290 return;
291 }
292 fullscreen_con = workspace->current.fullscreen;
293 }
294 if (fullscreen_con) {
295 for_each_surface_container_iterator(fullscreen_con, &data);
296 container_for_each_child(fullscreen_con,
297 for_each_surface_container_iterator, &data);
298
299 // TODO: Show transient containers for fullscreen global
300 if (fullscreen_con == workspace->current.fullscreen) {
301 for (int i = 0; i < workspace->current.floating->length; ++i) {
302 struct sway_container *floater =
303 workspace->current.floating->items[i];
304 if (container_is_transient_for(floater, fullscreen_con)) {
305 for_each_surface_container_iterator(floater, &data);
306 }
307 }
308 }
309#if HAVE_XWAYLAND
310 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
311 iterator, user_data);
312#endif
313 } else {
314 output_layer_for_each_surface(output,
315 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
316 iterator, user_data);
317 output_layer_for_each_surface(output,
318 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
319 iterator, user_data);
320
321 workspace_for_each_container(workspace,
322 for_each_surface_container_iterator, &data);
323
324#if HAVE_XWAYLAND
325 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
326 iterator, user_data);
327#endif
328 output_layer_for_each_surface(output,
329 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
330 iterator, user_data);
331 } 152 }
332 153
333overlay: 154 struct wlr_scene_node *current = &buffer->node;
334 output_layer_for_each_surface(output, 155 while (true) {
335 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 156 struct sway_view *view = scene_descriptor_try_get(current,
336 iterator, user_data); 157 SWAY_SCENE_DESC_VIEW);
337 output_drag_icons_for_each_surface(output, &root->drag_icons, 158 if (view) {
338 iterator, user_data); 159 view_max_render_time = view->max_render_time;
339} 160 break;
340
341static int scale_length(int length, int offset, float scale) {
342 return round((offset + length) * scale) - round(offset * scale);
343}
344
345void scale_box(struct wlr_box *box, float scale) {
346 box->width = scale_length(box->width, box->x, scale);
347 box->height = scale_length(box->height, box->y, scale);
348 box->x = round(box->x * scale);
349 box->y = round(box->y * scale);
350}
351
352struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
353 struct sway_seat *seat = input_manager_current_seat();
354 struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node);
355 if (!focus) {
356 if (!output->workspaces->length) {
357 return NULL;
358 } 161 }
359 return output->workspaces->items[0];
360 }
361 return focus->sway_workspace;
362}
363 162
364bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { 163 if (!current->parent) {
365 struct sway_layer_surface *sway_layer_surface; 164 break;
366 wl_list_for_each(sway_layer_surface,
367 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) {
368 struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface;
369 pixman_box32_t output_box = {
370 .x2 = output->width,
371 .y2 = output->height,
372 };
373 pixman_region32_t surface_opaque_box;
374 pixman_region32_init(&surface_opaque_box);
375 pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region);
376 pixman_region32_translate(&surface_opaque_box,
377 sway_layer_surface->geo.x, sway_layer_surface->geo.y);
378 pixman_region_overlap_t contains =
379 pixman_region32_contains_rectangle(&surface_opaque_box, &output_box);
380 pixman_region32_fini(&surface_opaque_box);
381
382 if (contains == PIXMAN_REGION_IN) {
383 return true;
384 } 165 }
385 }
386 return false;
387}
388 166
389struct send_frame_done_data { 167 current = &current->parent->node;
390 struct timespec when;
391 int msec_until_refresh;
392};
393
394static void send_frame_done_iterator(struct sway_output *output,
395 struct sway_view *view, struct wlr_surface *surface,
396 struct wlr_box *box, void *user_data) {
397 int view_max_render_time = 0;
398 if (view != NULL) {
399 view_max_render_time = view->max_render_time;
400 } 168 }
401 169
402 struct send_frame_done_data *data = user_data;
403
404 int delay = data->msec_until_refresh - output->max_render_time 170 int delay = data->msec_until_refresh - output->max_render_time
405 - view_max_render_time; 171 - view_max_render_time;
406 172
407 if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { 173 struct buffer_timer *timer = NULL;
408 wlr_surface_send_frame_done(surface, &data->when);
409 } else {
410 struct sway_surface *sway_surface = surface->data;
411 wl_event_source_timer_update(sway_surface->frame_done_timer, delay);
412 }
413}
414
415static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) {
416 output_for_each_surface(output, send_frame_done_iterator, data);
417}
418
419static void count_surface_iterator(struct sway_output *output,
420 struct sway_view *view, struct wlr_surface *surface,
421 struct wlr_box *box, void *data) {
422 size_t *n = data;
423 (*n)++;
424}
425 174
426static bool scan_out_fullscreen_view(struct sway_output *output, 175 if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) {
427 struct sway_view *view) { 176 timer = buffer_timer_get_or_create(buffer);
428 struct wlr_output *wlr_output = output->wlr_output;
429 struct sway_workspace *workspace = output->current.active_workspace;
430 if (!sway_assert(workspace, "Expected an active workspace")) {
431 return false;
432 } 177 }
433 178
434 if (!wl_list_empty(&view->saved_buffers)) { 179 if (timer) {
435 return false; 180 wl_event_source_timer_update(timer->frame_done_timer, delay);
181 } else {
182 wlr_scene_buffer_send_frame_done(buffer, &data->when);
436 } 183 }
184}
437 185
438 for (int i = 0; i < workspace->current.floating->length; ++i) { 186static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output,
439 struct sway_container *floater = 187 struct wlr_scene_buffer *buffer) {
440 workspace->current.floating->items[i]; 188 // if we are scaling down, we should always choose linear
441 if (container_is_transient_for(floater, view->container)) { 189 if (buffer->dst_width > 0 && buffer->dst_height > 0 && (
442 return false; 190 buffer->dst_width < buffer->buffer_width ||
443 } 191 buffer->dst_height < buffer->buffer_height)) {
192 return WLR_SCALE_FILTER_BILINEAR;
444 } 193 }
445 194
446#if HAVE_XWAYLAND 195 switch (output->scale_filter) {
447 if (!wl_list_empty(&root->xwayland_unmanaged)) { 196 case SCALE_FILTER_LINEAR:
448 return false; 197 return WLR_SCALE_FILTER_BILINEAR;
198 case SCALE_FILTER_NEAREST:
199 return WLR_SCALE_FILTER_NEAREST;
200 default:
201 abort(); // unreachable
449 } 202 }
450#endif 203}
451 204
452 if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { 205static void output_configure_scene(struct sway_output *output,
453 return false; 206 struct wlr_scene_node *node, float opacity) {
454 } 207 if (!node->enabled) {
455 if (!wl_list_empty(&root->drag_icons)) { 208 return;
456 return false;
457 } 209 }
458 210
459 struct wlr_surface *surface = view->surface; 211 struct sway_container *con =
460 if (surface == NULL) { 212 scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER);
461 return false; 213 if (con) {
462 } 214 opacity = con->alpha;
463 size_t n_surfaces = 0;
464 output_view_for_each_surface(output, view,
465 count_surface_iterator, &n_surfaces);
466 if (n_surfaces != 1) {
467 return false;
468 } 215 }
469 216
470 if (surface->buffer == NULL) { 217 if (node->type == WLR_SCENE_NODE_BUFFER) {
471 return false; 218 struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
472 }
473 219
474 if ((float)surface->current.scale != wlr_output->scale || 220 // hack: don't call the scene setter because that will damage all outputs
475 surface->current.transform != wlr_output->transform) { 221 // We don't want to damage outputs that aren't our current output that
476 return false; 222 // we're configuring
477 } 223 buffer->filter_mode = get_scale_filter(output, buffer);
478 224
479 wlr_output_attach_buffer(wlr_output, &surface->buffer->base); 225 wlr_scene_buffer_set_opacity(buffer, opacity);
480 if (!wlr_output_test(wlr_output)) { 226 } else if (node->type == WLR_SCENE_NODE_TREE) {
481 return false; 227 struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);
228 struct wlr_scene_node *node;
229 wl_list_for_each(node, &tree->children, link) {
230 output_configure_scene(output, node, opacity);
231 }
482 } 232 }
483
484 wlr_presentation_surface_sampled_on_output(server.presentation, surface,
485 wlr_output);
486
487 return wlr_output_commit(wlr_output);
488} 233}
489 234
490static int output_repaint_timer_handler(void *data) { 235static int output_repaint_timer_handler(void *data) {
491 struct sway_output *output = data; 236 struct sway_output *output = data;
492 if (output->wlr_output == NULL) {
493 return 0;
494 }
495
496 output->wlr_output->frame_pending = false;
497 237
498 struct sway_workspace *workspace = output->current.active_workspace; 238 if (!output->enabled) {
499 if (workspace == NULL) {
500 return 0; 239 return 0;
501 } 240 }
502 241
503 struct sway_container *fullscreen_con = root->fullscreen_global; 242 output->wlr_output->frame_pending = false;
504 if (!fullscreen_con) {
505 fullscreen_con = workspace->current.fullscreen;
506 }
507 243
508 if (fullscreen_con && fullscreen_con->view) { 244 output_configure_scene(output, &root->root_scene->tree.node, 1.0f);
509 // Try to scan-out the fullscreen view
510 static bool last_scanned_out = false;
511 bool scanned_out =
512 scan_out_fullscreen_view(output, fullscreen_con->view);
513 245
514 if (scanned_out && !last_scanned_out) { 246 if (output->gamma_lut_changed) {
515 sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", 247 struct wlr_output_state pending;
516 output->wlr_output->name); 248 wlr_output_state_init(&pending);
249 if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) {
250 return 0;
517 } 251 }
518 if (last_scanned_out && !scanned_out) { 252
519 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", 253 output->gamma_lut_changed = false;
520 output->wlr_output->name); 254 struct wlr_gamma_control_v1 *gamma_control =
521 output_damage_whole(output); 255 wlr_gamma_control_manager_v1_get_control(
256 server.gamma_control_manager_v1, output->wlr_output);
257 if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) {
258 wlr_output_state_finish(&pending);
259 return 0;
522 } 260 }
523 last_scanned_out = scanned_out;
524 261
525 if (scanned_out) { 262 if (!wlr_output_commit_state(output->wlr_output, &pending)) {
263 wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
264 wlr_output_state_finish(&pending);
526 return 0; 265 return 0;
527 } 266 }
528 }
529 267
530 bool needs_frame; 268 wlr_output_state_finish(&pending);
531 pixman_region32_t damage;
532 pixman_region32_init(&damage);
533 if (!wlr_output_damage_attach_render(output->damage,
534 &needs_frame, &damage)) {
535 return 0; 269 return 0;
536 } 270 }
537 271
538 if (needs_frame) { 272 wlr_scene_output_commit(output->scene_output, NULL);
539 struct timespec now;
540 clock_gettime(CLOCK_MONOTONIC, &now);
541
542 output_render(output, &now, &damage);
543 } else {
544 wlr_output_rollback(output->wlr_output);
545 }
546
547 pixman_region32_fini(&damage);
548
549 return 0; 273 return 0;
550} 274}
551 275
552static void damage_handle_frame(struct wl_listener *listener, void *user_data) { 276static void handle_frame(struct wl_listener *listener, void *user_data) {
553 struct sway_output *output = 277 struct sway_output *output =
554 wl_container_of(listener, output, damage_frame); 278 wl_container_of(listener, output, frame);
555 if (!output->enabled || !output->wlr_output->enabled) { 279 if (!output->enabled || !output->wlr_output->enabled) {
556 return; 280 return;
557 } 281 }
@@ -562,9 +286,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
562 286
563 if (output->max_render_time != 0) { 287 if (output->max_render_time != 0) {
564 struct timespec now; 288 struct timespec now;
565 clockid_t presentation_clock 289 clock_gettime(CLOCK_MONOTONIC, &now);
566 = wlr_backend_get_presentation_clock(server.backend);
567 clock_gettime(presentation_clock, &now);
568 290
569 const long NSEC_IN_SECONDS = 1000000000; 291 const long NSEC_IN_SECONDS = 1000000000;
570 struct timespec predicted_refresh = output->last_presentation; 292 struct timespec predicted_refresh = output->last_presentation;
@@ -611,118 +333,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
611 struct send_frame_done_data data = {0}; 333 struct send_frame_done_data data = {0};
612 clock_gettime(CLOCK_MONOTONIC, &data.when); 334 clock_gettime(CLOCK_MONOTONIC, &data.when);
613 data.msec_until_refresh = msec_until_refresh; 335 data.msec_until_refresh = msec_until_refresh;
614 send_frame_done(output, &data); 336 data.output = output;
615} 337 wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data);
616
617void output_damage_whole(struct sway_output *output) {
618 // The output can exist with no wlr_output if it's just been disconnected
619 // and the transaction to evacuate it has't completed yet.
620 if (output && output->wlr_output && output->damage) {
621 wlr_output_damage_add_whole(output->damage);
622 }
623}
624
625static void damage_surface_iterator(struct sway_output *output,
626 struct sway_view *view, struct wlr_surface *surface,
627 struct wlr_box *_box, void *_data) {
628 bool *data = _data;
629 bool whole = *data;
630
631 struct wlr_box box = *_box;
632 scale_box(&box, output->wlr_output->scale);
633
634 if (pixman_region32_not_empty(&surface->buffer_damage)) {
635 pixman_region32_t damage;
636 pixman_region32_init(&damage);
637 wlr_surface_get_effective_damage(surface, &damage);
638 wlr_region_scale(&damage, &damage, output->wlr_output->scale);
639 if (ceil(output->wlr_output->scale) > surface->current.scale) {
640 // When scaling up a surface, it'll become blurry so we need to
641 // expand the damage region
642 wlr_region_expand(&damage, &damage,
643 ceil(output->wlr_output->scale) - surface->current.scale);
644 }
645 pixman_region32_translate(&damage, box.x, box.y);
646 wlr_output_damage_add(output->damage, &damage);
647 pixman_region32_fini(&damage);
648 }
649
650 if (whole) {
651 wlr_output_damage_add_box(output->damage, &box);
652 }
653
654 if (!wl_list_empty(&surface->current.frame_callback_list)) {
655 wlr_output_schedule_frame(output->wlr_output);
656 }
657}
658
659void output_damage_surface(struct sway_output *output, double ox, double oy,
660 struct wlr_surface *surface, bool whole) {
661 output_surface_for_each_surface(output, surface, ox, oy,
662 damage_surface_iterator, &whole);
663}
664
665void output_damage_from_view(struct sway_output *output,
666 struct sway_view *view) {
667 if (!view_is_visible(view)) {
668 return;
669 }
670 bool whole = false;
671 output_view_for_each_surface(output, view, damage_surface_iterator, &whole);
672}
673
674// Expecting an unscaled box in layout coordinates
675void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
676 struct wlr_box box;
677 memcpy(&box, _box, sizeof(struct wlr_box));
678 box.x -= output->lx;
679 box.y -= output->ly;
680 scale_box(&box, output->wlr_output->scale);
681 wlr_output_damage_add_box(output->damage, &box);
682}
683
684static void damage_child_views_iterator(struct sway_container *con,
685 void *data) {
686 if (!con->view || !view_is_visible(con->view)) {
687 return;
688 }
689 struct sway_output *output = data;
690 bool whole = true;
691 output_view_for_each_surface(output, con->view, damage_surface_iterator,
692 &whole);
693}
694
695void output_damage_whole_container(struct sway_output *output,
696 struct sway_container *con) {
697 // Pad the box by 1px, because the width is a double and might be a fraction
698 struct wlr_box box = {
699 .x = con->current.x - output->lx - 1,
700 .y = con->current.y - output->ly - 1,
701 .width = con->current.width + 2,
702 .height = con->current.height + 2,
703 };
704 scale_box(&box, output->wlr_output->scale);
705 wlr_output_damage_add_box(output->damage, &box);
706 // Damage subsurfaces as well, which may extend outside the box
707 if (con->view) {
708 damage_child_views_iterator(con, output);
709 } else {
710 container_for_each_child(con, damage_child_views_iterator, output);
711 }
712}
713
714static void damage_handle_destroy(struct wl_listener *listener, void *data) {
715 struct sway_output *output =
716 wl_container_of(listener, output, damage_destroy);
717 if (!output->enabled) {
718 return;
719 }
720 output_disable(output);
721
722 wl_list_remove(&output->damage_destroy.link);
723 wl_list_remove(&output->damage_frame.link);
724
725 transaction_commit_dirty();
726} 338}
727 339
728static void update_output_manager_config(struct sway_server *server) { 340static void update_output_manager_config(struct sway_server *server) {
@@ -731,73 +343,61 @@ static void update_output_manager_config(struct sway_server *server) {
731 343
732 struct sway_output *output; 344 struct sway_output *output;
733 wl_list_for_each(output, &root->all_outputs, link) { 345 wl_list_for_each(output, &root->all_outputs, link) {
734 if (output == root->noop_output) { 346 if (output == root->fallback_output) {
735 continue; 347 continue;
736 } 348 }
737 struct wlr_output_configuration_head_v1 *config_head = 349 struct wlr_output_configuration_head_v1 *config_head =
738 wlr_output_configuration_head_v1_create(config, output->wlr_output); 350 wlr_output_configuration_head_v1_create(config, output->wlr_output);
739 struct wlr_box *output_box = wlr_output_layout_get_box( 351 struct wlr_box output_box;
740 root->output_layout, output->wlr_output); 352 wlr_output_layout_get_box(root->output_layout,
741 // We mark the output enabled even if it is switched off by DPMS 353 output->wlr_output, &output_box);
742 config_head->state.enabled = output->current_mode != NULL && output->enabled; 354 // We mark the output enabled when it's switched off but not disabled
743 config_head->state.mode = output->current_mode; 355 config_head->state.enabled = !wlr_box_empty(&output_box);
744 if (output_box) { 356 config_head->state.x = output_box.x;
745 config_head->state.x = output_box->x; 357 config_head->state.y = output_box.y;
746 config_head->state.y = output_box->y;
747 }
748 } 358 }
749 359
750 wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); 360 wlr_output_manager_v1_set_configuration(server->output_manager_v1, config);
361
362 ipc_event_output();
751} 363}
752 364
753static void handle_destroy(struct wl_listener *listener, void *data) { 365static void begin_destroy(struct sway_output *output) {
754 struct sway_output *output = wl_container_of(listener, output, destroy);
755 struct sway_server *server = output->server; 366 struct sway_server *server = output->server;
756 wl_signal_emit(&output->events.destroy, output);
757 367
758 if (output->enabled) { 368 if (output->enabled) {
759 output_disable(output); 369 output_disable(output);
760 } 370 }
371
761 output_begin_destroy(output); 372 output_begin_destroy(output);
762 373
374 wl_list_remove(&output->link);
375
376 wl_list_remove(&output->layout_destroy.link);
763 wl_list_remove(&output->destroy.link); 377 wl_list_remove(&output->destroy.link);
764 wl_list_remove(&output->commit.link); 378 wl_list_remove(&output->commit.link);
765 wl_list_remove(&output->mode.link);
766 wl_list_remove(&output->present.link); 379 wl_list_remove(&output->present.link);
380 wl_list_remove(&output->frame.link);
381 wl_list_remove(&output->request_state.link);
382
383 wlr_scene_output_destroy(output->scene_output);
384 output->scene_output = NULL;
385 output->wlr_output->data = NULL;
386 output->wlr_output = NULL;
767 387
768 transaction_commit_dirty(); 388 transaction_commit_dirty();
769 389
770 update_output_manager_config(server); 390 update_output_manager_config(server);
771} 391}
772 392
773static void handle_mode(struct wl_listener *listener, void *data) { 393static void handle_destroy(struct wl_listener *listener, void *data) {
774 struct sway_output *output = wl_container_of(listener, output, mode); 394 struct sway_output *output = wl_container_of(listener, output, destroy);
775 if (!output->enabled && !output->enabling) { 395 begin_destroy(output);
776 struct output_config *oc = find_output_config(output);
777 if (output->wlr_output->current_mode != NULL &&
778 (!oc || oc->enabled)) {
779 // We want to enable this output, but it didn't work last time,
780 // possibly because we hadn't enough CRTCs. Try again now that the
781 // output has a mode.
782 sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, "
783 "trying to enable it", output->wlr_output->name);
784 apply_output_config(oc, output);
785 }
786 return;
787 }
788 if (!output->enabled) {
789 return;
790 }
791 arrange_layers(output);
792 arrange_output(output);
793 transaction_commit_dirty();
794
795 update_output_manager_config(output->server);
796} 396}
797 397
798static void update_textures(struct sway_container *con, void *data) { 398static void handle_layout_destroy(struct wl_listener *listener, void *data) {
799 container_update_title_textures(con); 399 struct sway_output *output = wl_container_of(listener, output, layout_destroy);
800 container_update_marks_textures(con); 400 begin_destroy(output);
801} 401}
802 402
803static void handle_commit(struct wl_listener *listener, void *data) { 403static void handle_commit(struct wl_listener *listener, void *data) {
@@ -808,24 +408,28 @@ static void handle_commit(struct wl_listener *listener, void *data) {
808 return; 408 return;
809 } 409 }
810 410
811 if (event->committed & WLR_OUTPUT_STATE_SCALE) { 411 if (event->state->committed & (
812 output_for_each_container(output, update_textures, NULL); 412 WLR_OUTPUT_STATE_MODE |
813 } 413 WLR_OUTPUT_STATE_TRANSFORM |
814 414 WLR_OUTPUT_STATE_SCALE)) {
815 if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) {
816 arrange_layers(output); 415 arrange_layers(output);
817 arrange_output(output); 416 arrange_output(output);
818 transaction_commit_dirty(); 417 transaction_commit_dirty();
819 418
820 update_output_manager_config(output->server); 419 update_output_manager_config(output->server);
821 } 420 }
421
422 // Next time the output is enabled, try to re-apply the gamma LUT
423 if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) {
424 output->gamma_lut_changed = true;
425 }
822} 426}
823 427
824static void handle_present(struct wl_listener *listener, void *data) { 428static void handle_present(struct wl_listener *listener, void *data) {
825 struct sway_output *output = wl_container_of(listener, output, present); 429 struct sway_output *output = wl_container_of(listener, output, present);
826 struct wlr_output_event_present *output_event = data; 430 struct wlr_output_event_present *output_event = data;
827 431
828 if (!output->enabled) { 432 if (!output->enabled || !output_event->presented) {
829 return; 433 return;
830 } 434 }
831 435
@@ -833,37 +437,91 @@ static void handle_present(struct wl_listener *listener, void *data) {
833 output->refresh_nsec = output_event->refresh; 437 output->refresh_nsec = output_event->refresh;
834} 438}
835 439
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
447static unsigned int last_headless_num = 0;
448
836void handle_new_output(struct wl_listener *listener, void *data) { 449void handle_new_output(struct wl_listener *listener, void *data) {
837 struct sway_server *server = wl_container_of(listener, server, new_output); 450 struct sway_server *server = wl_container_of(listener, server, new_output);
838 struct wlr_output *wlr_output = data; 451 struct wlr_output *wlr_output = data;
839 sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 452
453 if (wlr_output == root->fallback_output->wlr_output) {
454 return;
455 }
456
457 if (wlr_output_is_headless(wlr_output)) {
458 char name[64];
459 snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num);
460 wlr_output_set_name(wlr_output, name);
461 }
462
463 sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)",
464 wlr_output, wlr_output->name, wlr_output->non_desktop);
465
466 if (wlr_output->non_desktop) {
467 sway_log(SWAY_DEBUG, "Not configuring non-desktop output");
468 struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output);
469#if WLR_HAS_DRM_BACKEND
470 if (server->drm_lease_manager) {
471 wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager,
472 wlr_output);
473 }
474#endif
475 list_add(root->non_desktop_outputs, non_desktop);
476 return;
477 }
478
479 if (!wlr_output_init_render(wlr_output, server->allocator,
480 server->renderer)) {
481 sway_log(SWAY_ERROR, "Failed to init output render");
482 return;
483 }
484
485 // Create the scene output here so we're not accidentally creating one for
486 // the fallback output
487 struct wlr_scene_output *scene_output =
488 wlr_scene_output_create(root->root_scene, wlr_output);
489 if (!scene_output) {
490 sway_log(SWAY_ERROR, "Failed to create a scene output");
491 return;
492 }
840 493
841 struct sway_output *output = output_create(wlr_output); 494 struct sway_output *output = output_create(wlr_output);
842 if (!output) { 495 if (!output) {
496 sway_log(SWAY_ERROR, "Failed to create a sway output");
497 wlr_scene_output_destroy(scene_output);
843 return; 498 return;
844 } 499 }
500
845 output->server = server; 501 output->server = server;
846 output->damage = wlr_output_damage_create(wlr_output); 502 output->scene_output = scene_output;
847 503
504 wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy);
505 output->layout_destroy.notify = handle_layout_destroy;
848 wl_signal_add(&wlr_output->events.destroy, &output->destroy); 506 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
849 output->destroy.notify = handle_destroy; 507 output->destroy.notify = handle_destroy;
850 wl_signal_add(&wlr_output->events.commit, &output->commit); 508 wl_signal_add(&wlr_output->events.commit, &output->commit);
851 output->commit.notify = handle_commit; 509 output->commit.notify = handle_commit;
852 wl_signal_add(&wlr_output->events.mode, &output->mode);
853 output->mode.notify = handle_mode;
854 wl_signal_add(&wlr_output->events.present, &output->present); 510 wl_signal_add(&wlr_output->events.present, &output->present);
855 output->present.notify = handle_present; 511 output->present.notify = handle_present;
856 wl_signal_add(&output->damage->events.frame, &output->damage_frame); 512 wl_signal_add(&wlr_output->events.frame, &output->frame);
857 output->damage_frame.notify = damage_handle_frame; 513 output->frame.notify = handle_frame;
858 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); 514 wl_signal_add(&wlr_output->events.request_state, &output->request_state);
859 output->damage_destroy.notify = damage_handle_destroy; 515 output->request_state.notify = handle_request_state;
860 516
861 output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, 517 output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop,
862 output_repaint_timer_handler, output); 518 output_repaint_timer_handler, output);
863 519
864 struct output_config *oc = find_output_config(output); 520 if (server->session_lock.lock) {
865 apply_output_config(oc, output); 521 sway_session_lock_add_output(server->session_lock.lock, output);
866 free_output_config(oc); 522 }
523
524 apply_all_output_configs();
867 525
868 transaction_commit_dirty(); 526 transaction_commit_dirty();
869 527
@@ -877,62 +535,103 @@ void handle_output_layout_change(struct wl_listener *listener,
877 update_output_manager_config(server); 535 update_output_manager_config(server);
878} 536}
879 537
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
880static void output_manager_apply(struct sway_server *server, 581static void output_manager_apply(struct sway_server *server,
881 struct wlr_output_configuration_v1 *config, bool test_only) { 582 struct wlr_output_configuration_v1 *config, bool test_only) {
882 // TODO: perform atomic tests on the whole backend atomically 583 size_t configs_len = wl_list_length(&root->all_outputs);
883 584 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
884 struct wlr_output_configuration_head_v1 *config_head; 585 if (!configs) {
885 // First disable outputs we need to disable 586 return;
886 bool ok = true; 587 }
887 wl_list_for_each(config_head, &config->heads, link) { 588
888 struct wlr_output *wlr_output = config_head->state.output; 589 int config_idx = 0;
889 struct sway_output *output = wlr_output->data; 590 struct sway_output *sway_output;
890 if (!output->enabled || config_head->state.enabled) { 591 wl_list_for_each(sway_output, &root->all_outputs, link) {
592 if (sway_output == root->fallback_output) {
593 configs_len--;
891 continue; 594 continue;
892 } 595 }
893 struct output_config *oc = new_output_config(output->wlr_output->name);
894 oc->enabled = false;
895 596
896 if (test_only) { 597 struct matched_output_config *cfg = &configs[config_idx++];
897 ok &= test_output_config(oc, output); 598 cfg->output = sway_output;
898 } else {
899 oc = store_output_config(oc);
900 ok &= apply_output_config(oc, output);
901 }
902 }
903 599
904 // Then enable outputs that need to 600 struct wlr_output_configuration_head_v1 *config_head;
905 wl_list_for_each(config_head, &config->heads, link) { 601 wl_list_for_each(config_head, &config->heads, link) {
906 struct wlr_output *wlr_output = config_head->state.output; 602 if (config_head->state.output == sway_output->wlr_output) {
907 struct sway_output *output = wlr_output->data; 603 cfg->config = output_config_for_config_head(config_head, sway_output);
908 if (!config_head->state.enabled) { 604 break;
909 continue; 605 }
910 } 606 }
911 struct output_config *oc = new_output_config(output->wlr_output->name); 607 if (!cfg->config) {
912 oc->enabled = true; 608 cfg->config = find_output_config(sway_output);
913 if (config_head->state.mode != NULL) {
914 struct wlr_output_mode *mode = config_head->state.mode;
915 oc->width = mode->width;
916 oc->height = mode->height;
917 oc->refresh_rate = mode->refresh / 1000.f;
918 } else {
919 oc->width = config_head->state.custom_mode.width;
920 oc->height = config_head->state.custom_mode.height;
921 oc->refresh_rate =
922 config_head->state.custom_mode.refresh / 1000.f;
923 } 609 }
924 oc->x = config_head->state.x; 610 }
925 oc->y = config_head->state.y; 611
926 oc->transform = config_head->state.transform; 612 bool ok = apply_output_configs(configs, configs_len, test_only);
927 oc->scale = config_head->state.scale; 613 for (size_t idx = 0; idx < configs_len; idx++) {
614 struct matched_output_config *cfg = &configs[idx];
928 615
929 if (test_only) { 616 // Only store new configs for successful non-test commits. Old configs,
930 ok &= test_output_config(oc, output); 617 // test-only and failed commits just get freed.
618 bool store_config = false;
619 if (!test_only && ok) {
620 struct wlr_output_configuration_head_v1 *config_head;
621 wl_list_for_each(config_head, &config->heads, link) {
622 if (config_head->state.output == cfg->output->wlr_output) {
623 store_config = true;
624 break;
625 }
626 }
627 }
628 if (store_config) {
629 store_output_config(cfg->config);
931 } else { 630 } else {
932 oc = store_output_config(oc); 631 free_output_config(cfg->config);
933 ok &= apply_output_config(oc, output);
934 } 632 }
935 } 633 }
634 free(configs);
936 635
937 if (ok) { 636 if (ok) {
938 wlr_output_configuration_v1_send_succeeded(config); 637 wlr_output_configuration_v1_send_succeeded(config);
@@ -970,12 +669,12 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
970 struct output_config *oc = new_output_config(output->wlr_output->name); 669 struct output_config *oc = new_output_config(output->wlr_output->name);
971 switch (event->mode) { 670 switch (event->mode) {
972 case ZWLR_OUTPUT_POWER_V1_MODE_OFF: 671 case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
973 oc->dpms_state = DPMS_OFF; 672 oc->power = 0;
974 break; 673 break;
975 case ZWLR_OUTPUT_POWER_V1_MODE_ON: 674 case ZWLR_OUTPUT_POWER_V1_MODE_ON:
976 oc->dpms_state = DPMS_ON; 675 oc->power = 1;
977 break; 676 break;
978 } 677 }
979 oc = store_output_config(oc); 678 store_output_config(oc);
980 apply_output_config(oc, output); 679 apply_all_output_configs();
981} 680}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
deleted file mode 100644
index a5bd8a5f..00000000
--- a/sway/desktop/render.c
+++ /dev/null
@@ -1,1160 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <GLES2/gl2.h>
4#include <stdlib.h>
5#include <strings.h>
6#include <time.h>
7#include <wayland-server-core.h>
8#include <wlr/render/gles2.h>
9#include <wlr/render/wlr_renderer.h>
10#include <wlr/types/wlr_buffer.h>
11#include <wlr/types/wlr_matrix.h>
12#include <wlr/types/wlr_output_damage.h>
13#include <wlr/types/wlr_output_layout.h>
14#include <wlr/types/wlr_output.h>
15#include <wlr/types/wlr_surface.h>
16#include <wlr/util/region.h>
17#include "log.h"
18#include "config.h"
19#include "sway/config.h"
20#include "sway/input/input-manager.h"
21#include "sway/input/seat.h"
22#include "sway/layers.h"
23#include "sway/output.h"
24#include "sway/server.h"
25#include "sway/tree/arrange.h"
26#include "sway/tree/container.h"
27#include "sway/tree/root.h"
28#include "sway/tree/view.h"
29#include "sway/tree/workspace.h"
30
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_backend_get_renderer(wlr_output->backend);
56 assert(renderer);
57
58 struct wlr_box box = {
59 .x = rect->x1,
60 .y = rect->y1,
61 .width = rect->x2 - rect->x1,
62 .height = rect->y2 - rect->y1,
63 };
64
65 int ow, oh;
66 wlr_output_transformed_resolution(wlr_output, &ow, &oh);
67
68 enum wl_output_transform transform =
69 wlr_output_transform_invert(wlr_output->transform);
70 wlr_box_transform(&box, &box, transform, ow, oh);
71
72 wlr_renderer_scissor(renderer, &box);
73}
74
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 =
104 wlr_backend_get_renderer(wlr_output->backend);
105 struct sway_output *output = wlr_output->data;
106
107 pixman_region32_t damage;
108 pixman_region32_init(&damage);
109 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y,
110 dst_box->width, dst_box->height);
111 pixman_region32_intersect(&damage, &damage, output_damage);
112 bool damaged = pixman_region32_not_empty(&damage);
113 if (!damaged) {
114 goto damage_finish;
115 }
116
117 int nrects;
118 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
119 for (int i = 0; i < nrects; ++i) {
120 scissor_output(wlr_output, &rects[i]);
121 set_scale_filter(wlr_output, texture, output->scale_filter);
122 if (src_box != NULL) {
123 wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha);
124 } else {
125 wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
126 }
127 }
128
129damage_finish:
130 pixman_region32_fini(&damage);
131}
132
133static void render_surface_iterator(struct sway_output *output,
134 struct sway_view *view, struct wlr_surface *surface,
135 struct wlr_box *_box, void *_data) {
136 struct render_data *data = _data;
137 struct wlr_output *wlr_output = output->wlr_output;
138 pixman_region32_t *output_damage = data->damage;
139 float alpha = data->alpha;
140
141 struct wlr_texture *texture = wlr_surface_get_texture(surface);
142 if (!texture) {
143 return;
144 }
145
146 struct wlr_fbox src_box;
147 wlr_surface_get_buffer_source_box(surface, &src_box);
148
149 struct wlr_box proj_box = *_box;
150 scale_box(&proj_box, wlr_output->scale);
151
152 float matrix[9];
153 enum wl_output_transform transform =
154 wlr_output_transform_invert(surface->current.transform);
155 wlr_matrix_project_box(matrix, &proj_box, transform, 0.0,
156 wlr_output->transform_matrix);
157
158 struct wlr_box dst_box = *_box;
159 struct wlr_box *clip_box = data->clip_box;
160 if (clip_box != NULL) {
161 dst_box.width = fmin(dst_box.width, clip_box->width);
162 dst_box.height = fmin(dst_box.height, clip_box->height);
163 }
164 scale_box(&dst_box, wlr_output->scale);
165
166 render_texture(wlr_output, output_damage, texture,
167 &src_box, &dst_box, matrix, alpha);
168
169 wlr_presentation_surface_sampled_on_output(server.presentation, surface,
170 wlr_output);
171}
172
173static void render_layer_toplevel(struct sway_output *output,
174 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
175 struct render_data data = {
176 .damage = damage,
177 .alpha = 1.0f,
178 };
179 output_layer_for_each_toplevel_surface(output, layer_surfaces,
180 render_surface_iterator, &data);
181}
182
183static void render_layer_popups(struct sway_output *output,
184 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
185 struct render_data data = {
186 .damage = damage,
187 .alpha = 1.0f,
188 };
189 output_layer_for_each_popup_surface(output, layer_surfaces,
190 render_surface_iterator, &data);
191}
192
193#if HAVE_XWAYLAND
194static void render_unmanaged(struct sway_output *output,
195 pixman_region32_t *damage, struct wl_list *unmanaged) {
196 struct render_data data = {
197 .damage = damage,
198 .alpha = 1.0f,
199 };
200 output_unmanaged_for_each_surface(output, unmanaged,
201 render_surface_iterator, &data);
202}
203#endif
204
205static void render_drag_icons(struct sway_output *output,
206 pixman_region32_t *damage, struct wl_list *drag_icons) {
207 struct render_data data = {
208 .damage = damage,
209 .alpha = 1.0f,
210 };
211 output_drag_icons_for_each_surface(output, drag_icons,
212 render_surface_iterator, &data);
213}
214
215// _box.x and .y are expected to be layout-local
216// _box.width and .height are expected to be output-buffer-local
217void render_rect(struct sway_output *output,
218 pixman_region32_t *output_damage, const struct wlr_box *_box,
219 float color[static 4]) {
220 struct wlr_output *wlr_output = output->wlr_output;
221 struct wlr_renderer *renderer =
222 wlr_backend_get_renderer(wlr_output->backend);
223
224 struct wlr_box box;
225 memcpy(&box, _box, sizeof(struct wlr_box));
226 box.x -= output->lx * wlr_output->scale;
227 box.y -= output->ly * wlr_output->scale;
228
229 pixman_region32_t damage;
230 pixman_region32_init(&damage);
231 pixman_region32_union_rect(&damage, &damage, box.x, box.y,
232 box.width, box.height);
233 pixman_region32_intersect(&damage, &damage, output_damage);
234 bool damaged = pixman_region32_not_empty(&damage);
235 if (!damaged) {
236 goto damage_finish;
237 }
238
239 int nrects;
240 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
241 for (int i = 0; i < nrects; ++i) {
242 scissor_output(wlr_output, &rects[i]);
243 wlr_render_rect(renderer, &box, color,
244 wlr_output->transform_matrix);
245 }
246
247damage_finish:
248 pixman_region32_fini(&damage);
249}
250
251void premultiply_alpha(float color[4], float opacity) {
252 color[3] *= opacity;
253 color[0] *= color[3];
254 color[1] *= color[3];
255 color[2] *= color[3];
256}
257
258static void render_view_toplevels(struct sway_view *view,
259 struct sway_output *output, pixman_region32_t *damage, float alpha) {
260 struct render_data data = {
261 .damage = damage,
262 .alpha = alpha,
263 };
264 struct wlr_box clip_box;
265 if (!container_is_current_floating(view->container)) {
266 // As we pass the geometry offsets to the surface iterator, we will
267 // need to account for the offsets in the clip dimensions.
268 clip_box.width = view->container->current.content_width + view->geometry.x;
269 clip_box.height = view->container->current.content_height + view->geometry.y;
270 data.clip_box = &clip_box;
271 }
272 // Render all toplevels without descending into popups
273 double ox = view->container->surface_x -
274 output->lx - view->geometry.x;
275 double oy = view->container->surface_y -
276 output->ly - view->geometry.y;
277 output_surface_for_each_surface(output, view->surface, ox, oy,
278 render_surface_iterator, &data);
279}
280
281static void render_view_popups(struct sway_view *view,
282 struct sway_output *output, pixman_region32_t *damage, float alpha) {
283 struct render_data data = {
284 .damage = damage,
285 .alpha = alpha,
286 };
287 output_view_for_each_popup_surface(output, view,
288 render_surface_iterator, &data);
289}
290
291static void render_saved_view(struct sway_view *view,
292 struct sway_output *output, pixman_region32_t *damage, float alpha) {
293 struct wlr_output *wlr_output = output->wlr_output;
294
295 if (wl_list_empty(&view->saved_buffers)) {
296 return;
297 }
298
299 bool floating = container_is_current_floating(view->container);
300
301 struct sway_saved_buffer *saved_buf;
302 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
303 if (!saved_buf->buffer->texture) {
304 continue;
305 }
306
307 struct wlr_box proj_box = {
308 .x = saved_buf->x - view->saved_geometry.x - output->lx,
309 .y = saved_buf->y - view->saved_geometry.y - output->ly,
310 .width = saved_buf->width,
311 .height = saved_buf->height,
312 };
313
314 struct wlr_box output_box = {
315 .width = output->width,
316 .height = output->height,
317 };
318
319 struct wlr_box intersection;
320 bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box);
321 if (!intersects) {
322 continue;
323 }
324
325 struct wlr_box dst_box = proj_box;
326 scale_box(&proj_box, wlr_output->scale);
327
328 float matrix[9];
329 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
330 wlr_matrix_project_box(matrix, &proj_box, transform, 0,
331 wlr_output->transform_matrix);
332
333 if (!floating) {
334 dst_box.width = fmin(dst_box.width,
335 view->container->current.content_width -
336 (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x);
337 dst_box.height = fmin(dst_box.height,
338 view->container->current.content_height -
339 (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y);
340 }
341 scale_box(&dst_box, wlr_output->scale);
342
343 render_texture(wlr_output, damage, saved_buf->buffer->texture,
344 &saved_buf->source_box, &dst_box, matrix, alpha);
345 }
346
347 // FIXME: we should set the surface that this saved buffer originates from
348 // as sampled here.
349 // https://github.com/swaywm/sway/pull/4465#discussion_r321082059
350}
351
352/**
353 * Render a view's surface and left/bottom/right borders.
354 */
355static void render_view(struct sway_output *output, pixman_region32_t *damage,
356 struct sway_container *con, struct border_colors *colors) {
357 struct sway_view *view = con->view;
358 if (!wl_list_empty(&view->saved_buffers)) {
359 render_saved_view(view, output, damage, view->container->alpha);
360 } else if (view->surface) {
361 render_view_toplevels(view, output, damage, view->container->alpha);
362 }
363
364 if (con->current.border == B_NONE || con->current.border == B_CSD) {
365 return;
366 }
367
368 struct wlr_box box;
369 float output_scale = output->wlr_output->scale;
370 float color[4];
371 struct sway_container_state *state = &con->current;
372
373 if (state->border_left) {
374 memcpy(&color, colors->child_border, sizeof(float) * 4);
375 premultiply_alpha(color, con->alpha);
376 box.x = floor(state->x);
377 box.y = floor(state->content_y);
378 box.width = state->border_thickness;
379 box.height = state->content_height;
380 scale_box(&box, output_scale);
381 render_rect(output, damage, &box, color);
382 }
383
384 list_t *siblings = container_get_current_siblings(con);
385 enum sway_container_layout layout =
386 container_current_parent_layout(con);
387
388 if (state->border_right) {
389 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) {
390 memcpy(&color, colors->indicator, sizeof(float) * 4);
391 } else {
392 memcpy(&color, colors->child_border, sizeof(float) * 4);
393 }
394 premultiply_alpha(color, con->alpha);
395 box.x = floor(state->content_x + state->content_width);
396 box.y = floor(state->content_y);
397 box.width = state->border_thickness;
398 box.height = state->content_height;
399 scale_box(&box, output_scale);
400 render_rect(output, damage, &box, color);
401 }
402
403 if (state->border_bottom) {
404 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) {
405 memcpy(&color, colors->indicator, sizeof(float) * 4);
406 } else {
407 memcpy(&color, colors->child_border, sizeof(float) * 4);
408 }
409 premultiply_alpha(color, con->alpha);
410 box.x = floor(state->x);
411 box.y = floor(state->content_y + state->content_height);
412 box.width = state->width;
413 box.height = state->border_thickness;
414 scale_box(&box, output_scale);
415 render_rect(output, damage, &box, color);
416 }
417}
418
419/**
420 * Render a titlebar.
421 *
422 * Care must be taken not to render over the same pixel multiple times,
423 * otherwise the colors will be incorrect when using opacity.
424 *
425 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
426 * The left side is: 1px border, 2px padding, title
427 */
428static void render_titlebar(struct sway_output *output,
429 pixman_region32_t *output_damage, struct sway_container *con,
430 int x, int y, int width,
431 struct border_colors *colors, struct wlr_texture *title_texture,
432 struct wlr_texture *marks_texture) {
433 struct wlr_box box;
434 float color[4];
435 float output_scale = output->wlr_output->scale;
436 double output_x = output->lx;
437 double output_y = output->ly;
438 int titlebar_border_thickness = config->titlebar_border_thickness;
439 int titlebar_h_padding = config->titlebar_h_padding;
440 int titlebar_v_padding = config->titlebar_v_padding;
441 enum alignment title_align = config->title_align;
442
443 // Single pixel bar above title
444 memcpy(&color, colors->border, sizeof(float) * 4);
445 premultiply_alpha(color, con->alpha);
446 box.x = x;
447 box.y = y;
448 box.width = width;
449 box.height = titlebar_border_thickness;
450 scale_box(&box, output_scale);
451 render_rect(output, output_damage, &box, color);
452
453 // Single pixel bar below title
454 box.x = x;
455 box.y = y + container_titlebar_height() - titlebar_border_thickness;
456 box.width = width;
457 box.height = titlebar_border_thickness;
458 scale_box(&box, output_scale);
459 render_rect(output, output_damage, &box, color);
460
461 // Single pixel left edge
462 box.x = x;
463 box.y = y + titlebar_border_thickness;
464 box.width = titlebar_border_thickness;
465 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
466 scale_box(&box, output_scale);
467 render_rect(output, output_damage, &box, color);
468
469 // Single pixel right edge
470 box.x = x + width - titlebar_border_thickness;
471 box.y = y + titlebar_border_thickness;
472 box.width = titlebar_border_thickness;
473 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
474 scale_box(&box, output_scale);
475 render_rect(output, output_damage, &box, color);
476
477 int inner_x = x - output_x + titlebar_h_padding;
478 int bg_y = y + titlebar_border_thickness;
479 size_t inner_width = width - titlebar_h_padding * 2;
480
481 // output-buffer local
482 int ob_inner_x = round(inner_x * output_scale);
483 int ob_inner_width = scale_length(inner_width, inner_x, output_scale);
484 int ob_bg_height = scale_length(
485 (titlebar_v_padding - titlebar_border_thickness) * 2 +
486 config->font_height, bg_y, output_scale);
487
488 // Marks
489 int ob_marks_x = 0; // output-buffer-local
490 int ob_marks_width = 0; // output-buffer-local
491 if (config->show_marks && marks_texture) {
492 struct wlr_box texture_box = {
493 .width = marks_texture->width,
494 .height = marks_texture->height,
495 };
496 ob_marks_width = texture_box.width;
497
498 // The marks texture might be shorter than the config->font_height, in
499 // which case we need to pad it as evenly as possible above and below.
500 int ob_padding_total = ob_bg_height - texture_box.height;
501 int ob_padding_above = floor(ob_padding_total / 2.0);
502 int ob_padding_below = ceil(ob_padding_total / 2.0);
503
504 // Render texture. If the title is on the right, the marks will be on
505 // the left. Otherwise, they will be on the right.
506 if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) {
507 texture_box.x = ob_inner_x;
508 } else {
509 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
510 }
511 ob_marks_x = texture_box.x;
512
513 texture_box.y = round((bg_y - output_y) * output_scale) +
514 ob_padding_above;
515
516 float matrix[9];
517 wlr_matrix_project_box(matrix, &texture_box,
518 WL_OUTPUT_TRANSFORM_NORMAL,
519 0.0, output->wlr_output->transform_matrix);
520
521 if (ob_inner_width < texture_box.width) {
522 texture_box.width = ob_inner_width;
523 }
524 render_texture(output->wlr_output, output_damage, marks_texture,
525 NULL, &texture_box, matrix, con->alpha);
526
527 // Padding above
528 memcpy(&color, colors->background, sizeof(float) * 4);
529 premultiply_alpha(color, con->alpha);
530 box.x = texture_box.x + round(output_x * output_scale);
531 box.y = round((y + titlebar_border_thickness) * output_scale);
532 box.width = texture_box.width;
533 box.height = ob_padding_above;
534 render_rect(output, output_damage, &box, color);
535
536 // Padding below
537 box.y += ob_padding_above + texture_box.height;
538 box.height = ob_padding_below;
539 render_rect(output, output_damage, &box, color);
540 }
541
542 // Title text
543 int ob_title_x = 0; // output-buffer-local
544 int ob_title_width = 0; // output-buffer-local
545 if (title_texture) {
546 struct wlr_box texture_box = {
547 .width = title_texture->width,
548 .height = title_texture->height,
549 };
550
551 // The effective output may be NULL when con is not on any output.
552 // This can happen because we render all children of containers,
553 // even those that are out of the bounds of any output.
554 struct sway_output *effective = container_get_effective_output(con);
555 float title_scale = effective ? effective->wlr_output->scale : output_scale;
556 texture_box.width = texture_box.width * output_scale / title_scale;
557 texture_box.height = texture_box.height * output_scale / title_scale;
558 ob_title_width = texture_box.width;
559
560 // The title texture might be shorter than the config->font_height,
561 // in which case we need to pad it above and below.
562 int ob_padding_above = round((config->font_baseline -
563 con->title_baseline + titlebar_v_padding -
564 titlebar_border_thickness) * output_scale);
565 int ob_padding_below = ob_bg_height - ob_padding_above -
566 texture_box.height;
567
568 // Render texture
569 if (texture_box.width > ob_inner_width - ob_marks_width) {
570 texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width)
571 ? ob_marks_x + ob_marks_width : ob_inner_x;
572 } else if (title_align == ALIGN_LEFT) {
573 texture_box.x = ob_inner_x;
574 } else if (title_align == ALIGN_CENTER) {
575 // If there are marks visible, center between the edge and marks.
576 // Otherwise, center in the inner area.
577 if (ob_marks_width) {
578 texture_box.x = (ob_inner_x + ob_marks_x) / 2
579 - texture_box.width / 2;
580 } else {
581 texture_box.x = ob_inner_x + ob_inner_width / 2
582 - texture_box.width / 2;
583 }
584 } else {
585 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
586 }
587 ob_title_x = texture_box.x;
588
589 texture_box.y =
590 round((bg_y - output_y) * output_scale) + ob_padding_above;
591
592 float matrix[9];
593 wlr_matrix_project_box(matrix, &texture_box,
594 WL_OUTPUT_TRANSFORM_NORMAL,
595 0.0, output->wlr_output->transform_matrix);
596
597 if (ob_inner_width - ob_marks_width < texture_box.width) {
598 texture_box.width = ob_inner_width - ob_marks_width;
599 }
600
601 render_texture(output->wlr_output, output_damage, title_texture,
602 NULL, &texture_box, matrix, con->alpha);
603
604 // Padding above
605 memcpy(&color, colors->background, sizeof(float) * 4);
606 premultiply_alpha(color, con->alpha);
607 box.x = texture_box.x + round(output_x * output_scale);
608 box.y = round((y + titlebar_border_thickness) * output_scale);
609 box.width = texture_box.width;
610 box.height = ob_padding_above;
611 render_rect(output, output_damage, &box, color);
612
613 // Padding below
614 box.y += ob_padding_above + texture_box.height;
615 box.height = ob_padding_below;
616 render_rect(output, output_damage, &box, color);
617 }
618
619 // Determine the left + right extends of the textures (output-buffer local)
620 int ob_left_x, ob_left_width, ob_right_x, ob_right_width;
621 if (ob_title_width == 0 && ob_marks_width == 0) {
622 ob_left_x = ob_inner_x;
623 ob_left_width = 0;
624 ob_right_x = ob_inner_x;
625 ob_right_width = 0;
626 } else if (ob_title_x < ob_marks_x) {
627 ob_left_x = ob_title_x;
628 ob_left_width = ob_title_width;
629 ob_right_x = ob_marks_x;
630 ob_right_width = ob_marks_width;
631 } else {
632 ob_left_x = ob_marks_x;
633 ob_left_width = ob_marks_width;
634 ob_right_x = ob_title_x;
635 ob_right_width = ob_title_width;
636 }
637 if (ob_left_x < ob_inner_x) {
638 ob_left_x = ob_inner_x;
639 } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) {
640 ob_right_x = ob_left_x;
641 ob_right_width = ob_left_width;
642 }
643
644 // Filler between title and marks
645 box.width = ob_right_x - ob_left_x - ob_left_width;
646 if (box.width > 0) {
647 box.x = ob_left_x + ob_left_width + round(output_x * output_scale);
648 box.y = round(bg_y * output_scale);
649 box.height = ob_bg_height;
650 render_rect(output, output_damage, &box, color);
651 }
652
653 // Padding on left side
654 box.x = x + titlebar_border_thickness;
655 box.y = y + titlebar_border_thickness;
656 box.width = titlebar_h_padding - titlebar_border_thickness;
657 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
658 config->font_height;
659 scale_box(&box, output_scale);
660 int left_x = ob_left_x + round(output_x * output_scale);
661 if (box.x + box.width < left_x) {
662 box.width += left_x - box.x - box.width;
663 }
664 render_rect(output, output_damage, &box, color);
665
666 // Padding on right side
667 box.x = x + width - titlebar_h_padding;
668 box.y = y + titlebar_border_thickness;
669 box.width = titlebar_h_padding - titlebar_border_thickness;
670 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
671 config->font_height;
672 scale_box(&box, output_scale);
673 int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale);
674 if (right_rx < box.x) {
675 box.width += box.x - right_rx;
676 box.x = right_rx;
677 }
678 render_rect(output, output_damage, &box, color);
679}
680
681/**
682 * Render the top border line for a view using "border pixel".
683 */
684static void render_top_border(struct sway_output *output,
685 pixman_region32_t *output_damage, struct sway_container *con,
686 struct border_colors *colors) {
687 struct sway_container_state *state = &con->current;
688 if (!state->border_top) {
689 return;
690 }
691 struct wlr_box box;
692 float color[4];
693 float output_scale = output->wlr_output->scale;
694
695 // Child border - top edge
696 memcpy(&color, colors->child_border, sizeof(float) * 4);
697 premultiply_alpha(color, con->alpha);
698 box.x = floor(state->x);
699 box.y = floor(state->y);
700 box.width = state->width;
701 box.height = state->border_thickness;
702 scale_box(&box, output_scale);
703 render_rect(output, output_damage, &box, color);
704}
705
706struct parent_data {
707 enum sway_container_layout layout;
708 struct wlr_box box;
709 list_t *children;
710 bool focused;
711 struct sway_container *active_child;
712};
713
714static void render_container(struct sway_output *output,
715 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
716
717/**
718 * Render a container's children using a L_HORIZ or L_VERT layout.
719 *
720 * Wrap child views in borders and leave child containers borderless because
721 * they'll apply their own borders to their children.
722 */
723static void render_containers_linear(struct sway_output *output,
724 pixman_region32_t *damage, struct parent_data *parent) {
725 for (int i = 0; i < parent->children->length; ++i) {
726 struct sway_container *child = parent->children->items[i];
727
728 if (child->view) {
729 struct sway_view *view = child->view;
730 struct border_colors *colors;
731 struct wlr_texture *title_texture;
732 struct wlr_texture *marks_texture;
733 struct sway_container_state *state = &child->current;
734
735 if (view_is_urgent(view)) {
736 colors = &config->border_colors.urgent;
737 title_texture = child->title_urgent;
738 marks_texture = child->marks_urgent;
739 } else if (state->focused || parent->focused) {
740 colors = &config->border_colors.focused;
741 title_texture = child->title_focused;
742 marks_texture = child->marks_focused;
743 } else if (child == parent->active_child) {
744 colors = &config->border_colors.focused_inactive;
745 title_texture = child->title_focused_inactive;
746 marks_texture = child->marks_focused_inactive;
747 } else {
748 colors = &config->border_colors.unfocused;
749 title_texture = child->title_unfocused;
750 marks_texture = child->marks_unfocused;
751 }
752
753 if (state->border == B_NORMAL) {
754 render_titlebar(output, damage, child, floor(state->x),
755 floor(state->y), state->width, colors,
756 title_texture, marks_texture);
757 } else if (state->border == B_PIXEL) {
758 render_top_border(output, damage, child, colors);
759 }
760 render_view(output, damage, child, colors);
761 } else {
762 render_container(output, damage, child,
763 parent->focused || child->current.focused);
764 }
765 }
766}
767
768/**
769 * Render a container's children using the L_TABBED layout.
770 */
771static void render_containers_tabbed(struct sway_output *output,
772 pixman_region32_t *damage, struct parent_data *parent) {
773 if (!parent->children->length) {
774 return;
775 }
776 struct sway_container *current = parent->active_child;
777 struct border_colors *current_colors = &config->border_colors.unfocused;
778 int tab_width = parent->box.width / parent->children->length;
779
780 // Render tabs
781 for (int i = 0; i < parent->children->length; ++i) {
782 struct sway_container *child = parent->children->items[i];
783 struct sway_view *view = child->view;
784 struct sway_container_state *cstate = &child->current;
785 struct border_colors *colors;
786 struct wlr_texture *title_texture;
787 struct wlr_texture *marks_texture;
788 bool urgent = view ?
789 view_is_urgent(view) : container_has_urgent_child(child);
790
791 if (urgent) {
792 colors = &config->border_colors.urgent;
793 title_texture = child->title_urgent;
794 marks_texture = child->marks_urgent;
795 } else if (cstate->focused || parent->focused) {
796 colors = &config->border_colors.focused;
797 title_texture = child->title_focused;
798 marks_texture = child->marks_focused;
799 } else if (child == parent->active_child) {
800 colors = &config->border_colors.focused_inactive;
801 title_texture = child->title_focused_inactive;
802 marks_texture = child->marks_focused_inactive;
803 } else {
804 colors = &config->border_colors.unfocused;
805 title_texture = child->title_unfocused;
806 marks_texture = child->marks_unfocused;
807 }
808
809 int x = floor(cstate->x + tab_width * i);
810
811 // Make last tab use the remaining width of the parent
812 if (i == parent->children->length - 1) {
813 tab_width = parent->box.width - tab_width * i;
814 }
815
816 render_titlebar(output, damage, child, x, parent->box.y, tab_width,
817 colors, title_texture, marks_texture);
818
819 if (child == current) {
820 current_colors = colors;
821 }
822 }
823
824 // Render surface and left/right/bottom borders
825 if (current->view) {
826 render_view(output, damage, current, current_colors);
827 } else {
828 render_container(output, damage, current,
829 parent->focused || current->current.focused);
830 }
831}
832
833/**
834 * Render a container's children using the L_STACKED layout.
835 */
836static void render_containers_stacked(struct sway_output *output,
837 pixman_region32_t *damage, struct parent_data *parent) {
838 if (!parent->children->length) {
839 return;
840 }
841 struct sway_container *current = parent->active_child;
842 struct border_colors *current_colors = &config->border_colors.unfocused;
843 size_t titlebar_height = container_titlebar_height();
844
845 // Render titles
846 for (int i = 0; i < parent->children->length; ++i) {
847 struct sway_container *child = parent->children->items[i];
848 struct sway_view *view = child->view;
849 struct sway_container_state *cstate = &child->current;
850 struct border_colors *colors;
851 struct wlr_texture *title_texture;
852 struct wlr_texture *marks_texture;
853 bool urgent = view ?
854 view_is_urgent(view) : container_has_urgent_child(child);
855
856 if (urgent) {
857 colors = &config->border_colors.urgent;
858 title_texture = child->title_urgent;
859 marks_texture = child->marks_urgent;
860 } else if (cstate->focused || parent->focused) {
861 colors = &config->border_colors.focused;
862 title_texture = child->title_focused;
863 marks_texture = child->marks_focused;
864 } else if (child == parent->active_child) {
865 colors = &config->border_colors.focused_inactive;
866 title_texture = child->title_focused_inactive;
867 marks_texture = child->marks_focused_inactive;
868 } else {
869 colors = &config->border_colors.unfocused;
870 title_texture = child->title_unfocused;
871 marks_texture = child->marks_unfocused;
872 }
873
874 int y = parent->box.y + titlebar_height * i;
875 render_titlebar(output, damage, child, parent->box.x, y,
876 parent->box.width, colors, title_texture, marks_texture);
877
878 if (child == current) {
879 current_colors = colors;
880 }
881 }
882
883 // Render surface and left/right/bottom borders
884 if (current->view) {
885 render_view(output, damage, current, current_colors);
886 } else {
887 render_container(output, damage, current,
888 parent->focused || current->current.focused);
889 }
890}
891
892static void render_containers(struct sway_output *output,
893 pixman_region32_t *damage, struct parent_data *parent) {
894 if (config->hide_lone_tab && parent->children->length == 1) {
895 struct sway_container *child = parent->children->items[0];
896 if (child->view) {
897 render_containers_linear(output,damage, parent);
898 return;
899 }
900 }
901
902 switch (parent->layout) {
903 case L_NONE:
904 case L_HORIZ:
905 case L_VERT:
906 render_containers_linear(output, damage, parent);
907 break;
908 case L_STACKED:
909 render_containers_stacked(output, damage, parent);
910 break;
911 case L_TABBED:
912 render_containers_tabbed(output, damage, parent);
913 break;
914 }
915}
916
917static void render_container(struct sway_output *output,
918 pixman_region32_t *damage, struct sway_container *con, bool focused) {
919 struct parent_data data = {
920 .layout = con->current.layout,
921 .box = {
922 .x = floor(con->current.x),
923 .y = floor(con->current.y),
924 .width = con->current.width,
925 .height = con->current.height,
926 },
927 .children = con->current.children,
928 .focused = focused,
929 .active_child = con->current.focused_inactive_child,
930 };
931 render_containers(output, damage, &data);
932}
933
934static void render_workspace(struct sway_output *output,
935 pixman_region32_t *damage, struct sway_workspace *ws, bool focused) {
936 struct parent_data data = {
937 .layout = ws->current.layout,
938 .box = {
939 .x = floor(ws->current.x),
940 .y = floor(ws->current.y),
941 .width = ws->current.width,
942 .height = ws->current.height,
943 },
944 .children = ws->current.tiling,
945 .focused = focused,
946 .active_child = ws->current.focused_inactive_child,
947 };
948 render_containers(output, damage, &data);
949}
950
951static void render_floating_container(struct sway_output *soutput,
952 pixman_region32_t *damage, struct sway_container *con) {
953 if (con->view) {
954 struct sway_view *view = con->view;
955 struct border_colors *colors;
956 struct wlr_texture *title_texture;
957 struct wlr_texture *marks_texture;
958
959 if (view_is_urgent(view)) {
960 colors = &config->border_colors.urgent;
961 title_texture = con->title_urgent;
962 marks_texture = con->marks_urgent;
963 } else if (con->current.focused) {
964 colors = &config->border_colors.focused;
965 title_texture = con->title_focused;
966 marks_texture = con->marks_focused;
967 } else {
968 colors = &config->border_colors.unfocused;
969 title_texture = con->title_unfocused;
970 marks_texture = con->marks_unfocused;
971 }
972
973 if (con->current.border == B_NORMAL) {
974 render_titlebar(soutput, damage, con, floor(con->current.x),
975 floor(con->current.y), con->current.width, colors,
976 title_texture, marks_texture);
977 } else if (con->current.border == B_PIXEL) {
978 render_top_border(soutput, damage, con, colors);
979 }
980 render_view(soutput, damage, con, colors);
981 } else {
982 render_container(soutput, damage, con, con->current.focused);
983 }
984}
985
986static void render_floating(struct sway_output *soutput,
987 pixman_region32_t *damage) {
988 for (int i = 0; i < root->outputs->length; ++i) {
989 struct sway_output *output = root->outputs->items[i];
990 for (int j = 0; j < output->current.workspaces->length; ++j) {
991 struct sway_workspace *ws = output->current.workspaces->items[j];
992 if (!workspace_is_visible(ws)) {
993 continue;
994 }
995 for (int k = 0; k < ws->current.floating->length; ++k) {
996 struct sway_container *floater = ws->current.floating->items[k];
997 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
998 continue;
999 }
1000 render_floating_container(soutput, damage, floater);
1001 }
1002 }
1003 }
1004}
1005
1006static void render_seatops(struct sway_output *output,
1007 pixman_region32_t *damage) {
1008 struct sway_seat *seat;
1009 wl_list_for_each(seat, &server.input->seats, link) {
1010 seatop_render(seat, output, damage);
1011 }
1012}
1013
1014void output_render(struct sway_output *output, struct timespec *when,
1015 pixman_region32_t *damage) {
1016 struct wlr_output *wlr_output = output->wlr_output;
1017
1018 struct wlr_renderer *renderer =
1019 wlr_backend_get_renderer(wlr_output->backend);
1020 if (!sway_assert(renderer != NULL,
1021 "expected the output backend to have a renderer")) {
1022 return;
1023 }
1024
1025 struct sway_workspace *workspace = output->current.active_workspace;
1026 if (workspace == NULL) {
1027 return;
1028 }
1029
1030 struct sway_container *fullscreen_con = root->fullscreen_global;
1031 if (!fullscreen_con) {
1032 fullscreen_con = workspace->current.fullscreen;
1033 }
1034
1035 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
1036
1037 if (!pixman_region32_not_empty(damage)) {
1038 // Output isn't damaged but needs buffer swap
1039 goto renderer_end;
1040 }
1041
1042 if (debug.damage == DAMAGE_HIGHLIGHT) {
1043 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
1044 } else if (debug.damage == DAMAGE_RERENDER) {
1045 int width, height;
1046 wlr_output_transformed_resolution(wlr_output, &width, &height);
1047 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1048 }
1049
1050 if (output_has_opaque_overlay_layer_surface(output)) {
1051 goto render_overlay;
1052 }
1053
1054 if (fullscreen_con) {
1055 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1056
1057 int nrects;
1058 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1059 for (int i = 0; i < nrects; ++i) {
1060 scissor_output(wlr_output, &rects[i]);
1061 wlr_renderer_clear(renderer, clear_color);
1062 }
1063
1064 if (fullscreen_con->view) {
1065 if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) {
1066 render_saved_view(fullscreen_con->view, output, damage, 1.0f);
1067 } else if (fullscreen_con->view->surface) {
1068 render_view_toplevels(fullscreen_con->view,
1069 output, damage, 1.0f);
1070 }
1071 } else {
1072 render_container(output, damage, fullscreen_con,
1073 fullscreen_con->current.focused);
1074 }
1075
1076 for (int i = 0; i < workspace->current.floating->length; ++i) {
1077 struct sway_container *floater =
1078 workspace->current.floating->items[i];
1079 if (container_is_transient_for(floater, fullscreen_con)) {
1080 render_floating_container(output, damage, floater);
1081 }
1082 }
1083#if HAVE_XWAYLAND
1084 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1085#endif
1086 } else {
1087 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
1088
1089 int nrects;
1090 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1091 for (int i = 0; i < nrects; ++i) {
1092 scissor_output(wlr_output, &rects[i]);
1093 wlr_renderer_clear(renderer, clear_color);
1094 }
1095
1096 render_layer_toplevel(output, damage,
1097 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1098 render_layer_toplevel(output, damage,
1099 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1100
1101 render_workspace(output, damage, workspace, workspace->current.focused);
1102 render_floating(output, damage);
1103#if HAVE_XWAYLAND
1104 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1105#endif
1106 render_layer_toplevel(output, damage,
1107 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1108
1109 render_layer_popups(output, damage,
1110 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1111 render_layer_popups(output, damage,
1112 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1113 render_layer_popups(output, damage,
1114 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1115 }
1116
1117 render_seatops(output, damage);
1118
1119 struct sway_seat *seat = input_manager_current_seat();
1120 struct sway_container *focus = seat_get_focused_container(seat);
1121 if (focus && focus->view) {
1122 render_view_popups(focus->view, output, damage, focus->alpha);
1123 }
1124
1125render_overlay:
1126 render_layer_toplevel(output, damage,
1127 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1128 render_layer_popups(output, damage,
1129 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1130 render_drag_icons(output, damage, &root->drag_icons);
1131
1132renderer_end:
1133 wlr_renderer_scissor(renderer, NULL);
1134 wlr_output_render_software_cursors(wlr_output, damage);
1135 wlr_renderer_end(renderer);
1136
1137 int width, height;
1138 wlr_output_transformed_resolution(wlr_output, &width, &height);
1139
1140 pixman_region32_t frame_damage;
1141 pixman_region32_init(&frame_damage);
1142
1143 enum wl_output_transform transform =
1144 wlr_output_transform_invert(wlr_output->transform);
1145 wlr_region_transform(&frame_damage, &output->damage->current,
1146 transform, width, height);
1147
1148 if (debug.damage == DAMAGE_HIGHLIGHT) {
1149 pixman_region32_union_rect(&frame_damage, &frame_damage,
1150 0, 0, wlr_output->width, wlr_output->height);
1151 }
1152
1153 wlr_output_set_damage(wlr_output, &frame_damage);
1154 pixman_region32_fini(&frame_damage);
1155
1156 if (!wlr_output_commit(wlr_output)) {
1157 return;
1158 }
1159 output->last_frame = *when;
1160}
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c
deleted file mode 100644
index 767b2045..00000000
--- a/sway/desktop/surface.c
+++ /dev/null
@@ -1,46 +0,0 @@
1#define _POSIX_C_SOURCE 200112L
2#include <stdlib.h>
3#include <time.h>
4#include <wlr/types/wlr_surface.h>
5#include "sway/server.h"
6#include "sway/surface.h"
7
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 b1f3fb32..042141ab 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -1,11 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
5#include <time.h> 4#include <time.h>
6#include <wlr/types/wlr_buffer.h> 5#include <wlr/types/wlr_buffer.h>
7#include "sway/config.h" 6#include "sway/config.h"
8#include "sway/desktop.h" 7#include "sway/scene_descriptor.h"
9#include "sway/desktop/idle_inhibit_v1.h" 8#include "sway/desktop/idle_inhibit_v1.h"
10#include "sway/desktop/transaction.h" 9#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 10#include "sway/input/cursor.h"
@@ -214,39 +213,20 @@ static void transaction_add_node(struct sway_transaction *transaction,
214 213
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
@@ -402,7 +790,7 @@ static void transaction_commit(struct sway_transaction *transaction) {
402 struct sway_transaction_instruction *instruction = 790 struct sway_transaction_instruction *instruction =
403 transaction->instructions->items[i]; 791 transaction->instructions->items[i];
404 struct sway_node *node = instruction->node; 792 struct sway_node *node = instruction->node;
405 bool hidden = node_is_view(node) && 793 bool hidden = node_is_view(node) && !node->destroying &&
406 !view_is_visible(node->sway_container->view); 794 !view_is_visible(node->sway_container->view);
407 if (should_configure(node, instruction)) { 795 if (should_configure(node, instruction)) {
408 instruction->serial = view_configure(node->sway_container->view, 796 instruction->serial = view_configure(node->sway_container->view,
@@ -415,21 +803,11 @@ static void transaction_commit(struct sway_transaction *transaction) {
415 ++transaction->num_waiting; 803 ++transaction->num_waiting;
416 } 804 }
417 805
418 // From here on we are rendering a saved buffer of the view, which 806 view_send_frame_done(node->sway_container->view);
419 // means we can send a frame done event to make the client redraw it
420 // as soon as possible. Additionally, this is required if a view is
421 // mapping and its default geometry doesn't intersect an output.
422 struct timespec now;
423 clock_gettime(CLOCK_MONOTONIC, &now);
424 wlr_surface_send_frame_done(
425 node->sway_container->view->surface, &now);
426 } 807 }
427 if (!hidden && node_is_view(node) && 808 if (!hidden && node_is_view(node) &&
428 wl_list_empty(&node->sway_container->view->saved_buffers)) { 809 !node->sway_container->view->saved_surface_tree) {
429 view_save_buffer(node->sway_container->view); 810 view_save_buffer(node->sway_container->view);
430 memcpy(&node->sway_container->view->saved_geometry,
431 &node->sway_container->view->geometry,
432 sizeof(struct wlr_box));
433 } 811 }
434 node->instruction = instruction; 812 node->instruction = instruction;
435 } 813 }
@@ -499,16 +877,18 @@ static void set_instruction_ready(
499 transaction_progress(); 877 transaction_progress();
500} 878}
501 879
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 d34654fd..7c417891 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 199309L
2#include <float.h> 1#include <float.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -7,7 +6,7 @@
7#include <wlr/util/edges.h> 6#include <wlr/util/edges.h>
8#include "log.h" 7#include "log.h"
9#include "sway/decoration.h" 8#include "sway/decoration.h"
10#include "sway/desktop.h" 9#include "sway/scene_descriptor.h"
11#include "sway/desktop/transaction.h" 10#include "sway/desktop/transaction.h"
12#include "sway/input/cursor.h" 11#include "sway/input/cursor.h"
13#include "sway/input/input-manager.h" 12#include "sway/input/input-manager.h"
@@ -19,64 +18,45 @@
19#include "sway/tree/workspace.h" 18#include "sway/tree/workspace.h"
20#include "sway/xdg_decoration.h" 19#include "sway/xdg_decoration.h"
21 20
22static const struct sway_view_child_impl popup_impl;
23
24static void popup_get_root_coords(struct sway_view_child *child,
25 int *root_sx, int *root_sy) {
26 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
27 struct wlr_xdg_surface *surface = popup->wlr_xdg_surface;
28
29 int x_offset = -child->view->geometry.x - surface->geometry.x;
30 int y_offset = -child->view->geometry.y - surface->geometry.y;
31
32 wlr_xdg_popup_get_toplevel_coords(surface->popup,
33 x_offset + surface->popup->geometry.x,
34 y_offset + surface->popup->geometry.y,
35 root_sx, root_sy);
36}
37
38static void popup_destroy(struct sway_view_child *child) {
39 if (!sway_assert(child->impl == &popup_impl,
40 "Expected an xdg_shell popup")) {
41 return;
42 }
43 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
44 wl_list_remove(&popup->new_popup.link);
45 wl_list_remove(&popup->destroy.link);
46 free(popup);
47}
48
49static const struct sway_view_child_impl popup_impl = {
50 .get_root_coords = popup_get_root_coords,
51 .destroy = popup_destroy,
52};
53
54static struct sway_xdg_popup *popup_create( 21static struct sway_xdg_popup *popup_create(
55 struct wlr_xdg_popup *wlr_popup, struct sway_view *view); 22 struct wlr_xdg_popup *wlr_popup, struct sway_view *view,
23 struct wlr_scene_tree *parent);
56 24
57static void popup_handle_new_popup(struct wl_listener *listener, void *data) { 25static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
58 struct sway_xdg_popup *popup = 26 struct sway_xdg_popup *popup =
59 wl_container_of(listener, popup, new_popup); 27 wl_container_of(listener, popup, new_popup);
60 struct wlr_xdg_popup *wlr_popup = data; 28 struct wlr_xdg_popup *wlr_popup = data;
61 popup_create(wlr_popup, popup->child.view); 29 popup_create(wlr_popup, popup->view, popup->xdg_surface_tree);
62} 30}
63 31
64static void popup_handle_destroy(struct wl_listener *listener, void *data) { 32static void popup_handle_destroy(struct wl_listener *listener, void *data) {
65 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); 33 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy);
66 view_child_destroy(&popup->child); 34
35 wl_list_remove(&popup->new_popup.link);
36 wl_list_remove(&popup->destroy.link);
37 wl_list_remove(&popup->surface_commit.link);
38 wl_list_remove(&popup->reposition.link);
39 wlr_scene_node_destroy(&popup->scene_tree->node);
40 free(popup);
67} 41}
68 42
69static void popup_unconstrain(struct sway_xdg_popup *popup) { 43static void popup_unconstrain(struct sway_xdg_popup *popup) {
70 struct sway_view *view = popup->child.view; 44 struct sway_view *view = popup->view;
71 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; 45 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
46
47 struct sway_workspace *workspace = view->container->pending.workspace;
48 if (!workspace) {
49 // is null if in the scratchpad
50 return;
51 }
72 52
73 struct sway_output *output = view->container->pending.workspace->output; 53 struct sway_output *output = workspace->output;
74 54
75 // the output box expressed in the coordinate system of the toplevel parent 55 // the output box expressed in the coordinate system of the toplevel parent
76 // of the popup 56 // of the popup
77 struct wlr_box output_toplevel_sx_box = { 57 struct wlr_box output_toplevel_sx_box = {
78 .x = output->lx - view->container->pending.content_x, 58 .x = output->lx - view->container->pending.content_x + view->geometry.x,
79 .y = output->ly - view->container->pending.content_y, 59 .y = output->ly - view->container->pending.content_y + view->geometry.y,
80 .width = output->width, 60 .width = output->width,
81 .height = output->height, 61 .height = output->height,
82 }; 62 };
@@ -84,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
84 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 64 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
85} 65}
86 66
87static struct sway_xdg_popup *popup_create( 67static void popup_handle_surface_commit(struct wl_listener *listener, void *data) {
88 struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { 68 struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit);
69 if (popup->wlr_xdg_popup->base->initial_commit) {
70 popup_unconstrain(popup);
71 }
72}
73
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) {
89 struct wlr_xdg_surface *xdg_surface = wlr_popup->base; 81 struct wlr_xdg_surface *xdg_surface = wlr_popup->base;
90 82
91 struct sway_xdg_popup *popup = 83 struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup));
92 calloc(1, sizeof(struct sway_xdg_popup)); 84 if (!popup) {
93 if (popup == NULL) { 85 return NULL;
86 }
87
88 popup->wlr_xdg_popup = wlr_popup;
89 popup->view = view;
90
91 popup->scene_tree = wlr_scene_tree_create(parent);
92 if (!popup->scene_tree) {
93 free(popup);
94 return NULL;
95 }
96
97 popup->xdg_surface_tree = wlr_scene_xdg_surface_create(
98 popup->scene_tree, xdg_surface);
99 if (!popup->xdg_surface_tree) {
100 wlr_scene_node_destroy(&popup->scene_tree->node);
101 free(popup);
94 return NULL; 102 return NULL;
95 } 103 }
96 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
97 popup->wlr_xdg_surface = xdg_surface;
98 104
105 popup->desc.relative = &view->content_tree->node;
106 popup->desc.view = view;
107
108 if (!scene_descriptor_assign(&popup->scene_tree->node,
109 SWAY_SCENE_DESC_POPUP, &popup->desc)) {
110 sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor");
111 wlr_scene_node_destroy(&popup->scene_tree->node);
112 free(popup);
113 return NULL;
114 }
115
116 popup->wlr_xdg_popup = xdg_surface->popup;
117 struct sway_xdg_shell_view *shell_view =
118 wl_container_of(view, shell_view, view);
119 xdg_surface->data = shell_view;
120
121 wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit);
122 popup->surface_commit.notify = popup_handle_surface_commit;
99 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 123 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
100 popup->new_popup.notify = popup_handle_new_popup; 124 popup->new_popup.notify = popup_handle_new_popup;
101 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); 125 wl_signal_add(&wlr_popup->events.reposition, &popup->reposition);
126 popup->reposition.notify = popup_handle_reposition;
127 wl_signal_add(&wlr_popup->events.destroy, &popup->destroy);
102 popup->destroy.notify = popup_handle_destroy; 128 popup->destroy.notify = popup_handle_destroy;
103 129
104 wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map);
105 wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap);
106
107 popup_unconstrain(popup);
108
109 return popup; 130 return popup;
110} 131}
111 132
112
113static struct sway_xdg_shell_view *xdg_shell_view_from_view( 133static struct sway_xdg_shell_view *xdg_shell_view_from_view(
114 struct sway_view *view) { 134 struct sway_view *view) {
115 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, 135 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL,
@@ -122,7 +142,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view(
122static void get_constraints(struct sway_view *view, double *min_width, 142static void get_constraints(struct sway_view *view, double *min_width,
123 double *max_width, double *min_height, double *max_height) { 143 double *max_width, double *min_height, double *max_height) {
124 struct wlr_xdg_toplevel_state *state = 144 struct wlr_xdg_toplevel_state *state =
125 &view->wlr_xdg_surface->toplevel->current; 145 &view->wlr_xdg_toplevel->current;
126 *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; 146 *min_width = state->min_width > 0 ? state->min_width : DBL_MIN;
127 *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; 147 *max_width = state->max_width > 0 ? state->max_width : DBL_MAX;
128 *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; 148 *min_height = state->min_height > 0 ? state->min_height : DBL_MIN;
@@ -136,9 +156,9 @@ static const char *get_string_prop(struct sway_view *view,
136 } 156 }
137 switch (prop) { 157 switch (prop) {
138 case VIEW_PROP_TITLE: 158 case VIEW_PROP_TITLE:
139 return view->wlr_xdg_surface->toplevel->title; 159 return view->wlr_xdg_toplevel->title;
140 case VIEW_PROP_APP_ID: 160 case VIEW_PROP_APP_ID:
141 return view->wlr_xdg_surface->toplevel->app_id; 161 return view->wlr_xdg_toplevel->app_id;
142 default: 162 default:
143 return NULL; 163 return NULL;
144 } 164 }
@@ -151,50 +171,52 @@ static uint32_t configure(struct sway_view *view, double lx, double ly,
151 if (xdg_shell_view == NULL) { 171 if (xdg_shell_view == NULL) {
152 return 0; 172 return 0;
153 } 173 }
154 return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); 174 return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel,
175 width, height);
155} 176}
156 177
157static void set_activated(struct sway_view *view, bool activated) { 178static void set_activated(struct sway_view *view, bool activated) {
158 if (xdg_shell_view_from_view(view) == NULL) { 179 if (xdg_shell_view_from_view(view) == NULL) {
159 return; 180 return;
160 } 181 }
161 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 182 wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated);
162 if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
163 wlr_xdg_toplevel_set_activated(surface, activated);
164 }
165} 183}
166 184
167static void set_tiled(struct sway_view *view, bool tiled) { 185static void set_tiled(struct sway_view *view, bool tiled) {
168 if (xdg_shell_view_from_view(view) == NULL) { 186 if (xdg_shell_view_from_view(view) == NULL) {
169 return; 187 return;
170 } 188 }
171 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 189 if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >=
172 enum wlr_edges edges = WLR_EDGE_NONE; 190 XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) {
173 if (tiled) { 191 enum wlr_edges edges = WLR_EDGE_NONE;
174 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | 192 if (tiled) {
175 WLR_EDGE_BOTTOM; 193 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP |
194 WLR_EDGE_BOTTOM;
195 }
196 wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
197 } else {
198 // The version is too low for the tiled state; configure as maximized instead
199 // to stop the client from drawing decorations outside of the toplevel geometry.
200 wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled);
176 } 201 }
177 wlr_xdg_toplevel_set_tiled(surface, edges);
178} 202}
179 203
180static void set_fullscreen(struct sway_view *view, bool fullscreen) { 204static void set_fullscreen(struct sway_view *view, bool fullscreen) {
181 if (xdg_shell_view_from_view(view) == NULL) { 205 if (xdg_shell_view_from_view(view) == NULL) {
182 return; 206 return;
183 } 207 }
184 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 208 wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen);
185 wlr_xdg_toplevel_set_fullscreen(surface, fullscreen);
186} 209}
187 210
188static void set_resizing(struct sway_view *view, bool resizing) { 211static void set_resizing(struct sway_view *view, bool resizing) {
189 if (xdg_shell_view_from_view(view) == NULL) { 212 if (xdg_shell_view_from_view(view) == NULL) {
190 return; 213 return;
191 } 214 }
192 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 215 wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing);
193 wlr_xdg_toplevel_set_resizing(surface, resizing);
194} 216}
195 217
196static bool wants_floating(struct sway_view *view) { 218static bool wants_floating(struct sway_view *view) {
197 struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; 219 struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
198 struct wlr_xdg_toplevel_state *state = &toplevel->current; 220 struct wlr_xdg_toplevel_state *state = &toplevel->current;
199 return (state->min_width != 0 && state->min_height != 0 221 return (state->min_width != 0 && state->min_height != 0
200 && (state->min_width == state->max_width 222 && (state->min_width == state->max_width
@@ -202,35 +224,17 @@ static bool wants_floating(struct sway_view *view) {
202 || toplevel->parent; 224 || toplevel->parent;
203} 225}
204 226
205static void for_each_surface(struct sway_view *view,
206 wlr_surface_iterator_func_t iterator, void *user_data) {
207 if (xdg_shell_view_from_view(view) == NULL) {
208 return;
209 }
210 wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator,
211 user_data);
212}
213
214static void for_each_popup_surface(struct sway_view *view,
215 wlr_surface_iterator_func_t iterator, void *user_data) {
216 if (xdg_shell_view_from_view(view) == NULL) {
217 return;
218 }
219 wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator,
220 user_data);
221}
222
223static bool is_transient_for(struct sway_view *child, 227static bool is_transient_for(struct sway_view *child,
224 struct sway_view *ancestor) { 228 struct sway_view *ancestor) {
225 if (xdg_shell_view_from_view(child) == NULL) { 229 if (xdg_shell_view_from_view(child) == NULL) {
226 return false; 230 return false;
227 } 231 }
228 struct wlr_xdg_surface *surface = child->wlr_xdg_surface; 232 struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel;
229 while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { 233 while (toplevel) {
230 if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { 234 if (toplevel->parent == ancestor->wlr_xdg_toplevel) {
231 return true; 235 return true;
232 } 236 }
233 surface = surface->toplevel->parent; 237 toplevel = toplevel->parent;
234 } 238 }
235 return false; 239 return false;
236} 240}
@@ -239,17 +243,13 @@ static void _close(struct sway_view *view) {
239 if (xdg_shell_view_from_view(view) == NULL) { 243 if (xdg_shell_view_from_view(view) == NULL) {
240 return; 244 return;
241 } 245 }
242 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 246 wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel);
243 if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL
244 && surface->toplevel) {
245 wlr_xdg_toplevel_send_close(surface);
246 }
247} 247}
248 248
249static void close_popups(struct sway_view *view) { 249static void close_popups(struct sway_view *view) {
250 struct wlr_xdg_popup *popup, *tmp; 250 struct wlr_xdg_popup *popup, *tmp;
251 wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { 251 wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) {
252 wlr_xdg_popup_destroy(popup->base); 252 wlr_xdg_popup_destroy(popup);
253 } 253 }
254} 254}
255 255
@@ -271,8 +271,6 @@ static const struct sway_view_impl view_impl = {
271 .set_fullscreen = set_fullscreen, 271 .set_fullscreen = set_fullscreen,
272 .set_resizing = set_resizing, 272 .set_resizing = set_resizing,
273 .wants_floating = wants_floating, 273 .wants_floating = wants_floating,
274 .for_each_surface = for_each_surface,
275 .for_each_popup_surface = for_each_popup_surface,
276 .is_transient_for = is_transient_for, 274 .is_transient_for = is_transient_for,
277 .close = _close, 275 .close = _close,
278 .close_popups = close_popups, 276 .close_popups = close_popups,
@@ -283,7 +281,21 @@ static void handle_commit(struct wl_listener *listener, void *data) {
283 struct sway_xdg_shell_view *xdg_shell_view = 281 struct sway_xdg_shell_view *xdg_shell_view =
284 wl_container_of(listener, xdg_shell_view, commit); 282 wl_container_of(listener, xdg_shell_view, commit);
285 struct sway_view *view = &xdg_shell_view->view; 283 struct sway_view *view = &xdg_shell_view->view;
286 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 284 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base;
285
286 if (xdg_surface->initial_commit) {
287 if (view->xdg_decoration != NULL) {
288 set_xdg_decoration_mode(view->xdg_decoration);
289 }
290 // XXX: https://github.com/swaywm/sway/issues/2176
291 wlr_xdg_surface_schedule_configure(xdg_surface);
292 // TODO: wlr_xdg_toplevel_set_bounds()
293 return;
294 }
295
296 if (!xdg_surface->surface->mapped) {
297 return;
298 }
287 299
288 struct wlr_box new_geo; 300 struct wlr_box new_geo;
289 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); 301 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
@@ -296,23 +308,32 @@ static void handle_commit(struct wl_listener *listener, void *data) {
296 // The client changed its surface size in this commit. For floating 308 // The client changed its surface size in this commit. For floating
297 // containers, we resize the container to match. For tiling containers, 309 // containers, we resize the container to match. For tiling containers,
298 // we only recenter the surface. 310 // we only recenter the surface.
299 desktop_damage_view(view);
300 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 311 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
301 if (container_is_floating(view->container)) { 312 if (container_is_floating(view->container)) {
302 view_update_size(view); 313 view_update_size(view);
314 // Only set the toplevel size the current container actually has a size.
315 if (view->container->current.width) {
316 wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width,
317 view->geometry.height);
318 }
303 transaction_commit_dirty_client(); 319 transaction_commit_dirty_client();
304 } else {
305 view_center_surface(view);
306 } 320 }
307 desktop_damage_view(view); 321
322 view_center_and_clip_surface(view);
308 } 323 }
309 324
310 if (view->container->node.instruction) { 325 if (view->container->node.instruction) {
311 transaction_notify_view_ready_by_serial(view, 326 bool successful = transaction_notify_view_ready_by_serial(view,
312 xdg_surface->configure_serial); 327 xdg_surface->current.configure_serial);
328
329 // If we saved the view and this commit isn't what we're looking for
330 // that means the user will never actually see the buffers submitted to
331 // us here. Just send frame done events to these surfaces so they can
332 // commit another time for us.
333 if (view->saved_surface_tree && !successful) {
334 view_send_frame_done(view);
335 }
313 } 336 }
314
315 view_damage_from(view);
316} 337}
317 338
318static void handle_set_title(struct wl_listener *listener, void *data) { 339static void handle_set_title(struct wl_listener *listener, void *data) {
@@ -327,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) {
327 struct sway_xdg_shell_view *xdg_shell_view = 348 struct sway_xdg_shell_view *xdg_shell_view =
328 wl_container_of(listener, xdg_shell_view, set_app_id); 349 wl_container_of(listener, xdg_shell_view, set_app_id);
329 struct sway_view *view = &xdg_shell_view->view; 350 struct sway_view *view = &xdg_shell_view->view;
351 view_update_app_id(view);
330 view_execute_criteria(view); 352 view_execute_criteria(view);
331} 353}
332 354
@@ -334,29 +356,39 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
334 struct sway_xdg_shell_view *xdg_shell_view = 356 struct sway_xdg_shell_view *xdg_shell_view =
335 wl_container_of(listener, xdg_shell_view, new_popup); 357 wl_container_of(listener, xdg_shell_view, new_popup);
336 struct wlr_xdg_popup *wlr_popup = data; 358 struct wlr_xdg_popup *wlr_popup = data;
337 popup_create(wlr_popup, &xdg_shell_view->view); 359
360 struct sway_xdg_popup *popup = popup_create(wlr_popup,
361 &xdg_shell_view->view, root->layers.popup);
362 if (!popup) {
363 return;
364 }
365
366 int lx, ly;
367 wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly);
368 wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly);
369}
370
371static void handle_request_maximize(struct wl_listener *listener, void *data) {
372 struct sway_xdg_shell_view *xdg_shell_view =
373 wl_container_of(listener, xdg_shell_view, request_maximize);
374 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
375 wlr_xdg_surface_schedule_configure(toplevel->base);
338} 376}
339 377
340static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 378static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
341 struct sway_xdg_shell_view *xdg_shell_view = 379 struct sway_xdg_shell_view *xdg_shell_view =
342 wl_container_of(listener, xdg_shell_view, request_fullscreen); 380 wl_container_of(listener, xdg_shell_view, request_fullscreen);
343 struct wlr_xdg_toplevel_set_fullscreen_event *e = data; 381 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
344 struct wlr_xdg_surface *xdg_surface =
345 xdg_shell_view->view.wlr_xdg_surface;
346 struct sway_view *view = &xdg_shell_view->view; 382 struct sway_view *view = &xdg_shell_view->view;
347 383
348 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, 384 if (!toplevel->base->surface->mapped) {
349 "xdg_shell requested fullscreen of surface with role %i",
350 xdg_surface->role)) {
351 return;
352 }
353 if (!xdg_surface->mapped) {
354 return; 385 return;
355 } 386 }
356 387
357 struct sway_container *container = view->container; 388 struct sway_container *container = view->container;
358 if (e->fullscreen && e->output && e->output->data) { 389 struct wlr_xdg_toplevel_requested *req = &toplevel->requested;
359 struct sway_output *output = e->output->data; 390 if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) {
391 struct sway_output *output = req->fullscreen_output->data;
360 struct sway_workspace *ws = output_get_active_workspace(output); 392 struct sway_workspace *ws = output_get_active_workspace(output);
361 if (ws && !container_is_scratchpad_hidden(container) && 393 if (ws && !container_is_scratchpad_hidden(container) &&
362 container->pending.workspace != ws) { 394 container->pending.workspace != ws) {
@@ -368,22 +400,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
368 } 400 }
369 } 401 }
370 402
371 container_set_fullscreen(container, e->fullscreen); 403 container_set_fullscreen(container, req->fullscreen);
372 404
373 arrange_root(); 405 arrange_root();
374 transaction_commit_dirty(); 406 transaction_commit_dirty();
375} 407}
376 408
377static void handle_request_maximize(struct wl_listener *listener, void *data) {
378 struct wlr_xdg_surface *surface = data;
379 wlr_xdg_surface_schedule_configure(surface);
380}
381
382static void handle_request_move(struct wl_listener *listener, void *data) { 409static void handle_request_move(struct wl_listener *listener, void *data) {
383 struct sway_xdg_shell_view *xdg_shell_view = 410 struct sway_xdg_shell_view *xdg_shell_view =
384 wl_container_of(listener, xdg_shell_view, request_move); 411 wl_container_of(listener, xdg_shell_view, request_move);
385 struct sway_view *view = &xdg_shell_view->view; 412 struct sway_view *view = &xdg_shell_view->view;
386 if (!container_is_floating(view->container)) { 413 if (!container_is_floating(view->container) ||
414 view->container->pending.fullscreen_mode) {
387 return; 415 return;
388 } 416 }
389 struct wlr_xdg_toplevel_move_event *e = data; 417 struct wlr_xdg_toplevel_move_event *e = data;
@@ -418,10 +446,9 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
418 446
419 view_unmap(view); 447 view_unmap(view);
420 448
421 wl_list_remove(&xdg_shell_view->commit.link);
422 wl_list_remove(&xdg_shell_view->new_popup.link); 449 wl_list_remove(&xdg_shell_view->new_popup.link);
423 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
424 wl_list_remove(&xdg_shell_view->request_maximize.link); 450 wl_list_remove(&xdg_shell_view->request_maximize.link);
451 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
425 wl_list_remove(&xdg_shell_view->request_move.link); 452 wl_list_remove(&xdg_shell_view->request_move.link);
426 wl_list_remove(&xdg_shell_view->request_resize.link); 453 wl_list_remove(&xdg_shell_view->request_resize.link);
427 wl_list_remove(&xdg_shell_view->set_title.link); 454 wl_list_remove(&xdg_shell_view->set_title.link);
@@ -432,62 +459,61 @@ static void handle_map(struct wl_listener *listener, void *data) {
432 struct sway_xdg_shell_view *xdg_shell_view = 459 struct sway_xdg_shell_view *xdg_shell_view =
433 wl_container_of(listener, xdg_shell_view, map); 460 wl_container_of(listener, xdg_shell_view, map);
434 struct sway_view *view = &xdg_shell_view->view; 461 struct sway_view *view = &xdg_shell_view->view;
435 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 462 struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
436 463
437 view->natural_width = view->wlr_xdg_surface->geometry.width; 464 view->natural_width = toplevel->base->current.geometry.width;
438 view->natural_height = view->wlr_xdg_surface->geometry.height; 465 view->natural_height = toplevel->base->current.geometry.height;
439 if (!view->natural_width && !view->natural_height) { 466 if (!view->natural_width && !view->natural_height) {
440 view->natural_width = view->wlr_xdg_surface->surface->current.width; 467 view->natural_width = toplevel->base->surface->current.width;
441 view->natural_height = view->wlr_xdg_surface->surface->current.height; 468 view->natural_height = toplevel->base->surface->current.height;
442 } 469 }
443 470
444 bool csd = false; 471 bool csd = false;
445 472
446 if (!view->xdg_decoration) { 473 if (view->xdg_decoration) {
474 enum wlr_xdg_toplevel_decoration_v1_mode mode =
475 view->xdg_decoration->wlr_xdg_decoration->requested_mode;
476 csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
477 } else {
447 struct sway_server_decoration *deco = 478 struct sway_server_decoration *deco =
448 decoration_from_surface(xdg_surface->surface); 479 decoration_from_surface(toplevel->base->surface);
449 csd = !deco || deco->wlr_server_decoration->mode == 480 csd = !deco || deco->wlr_server_decoration->mode ==
450 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; 481 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
451
452 } 482 }
453 483
454 view_map(view, view->wlr_xdg_surface->surface, 484 view_map(view, toplevel->base->surface,
455 xdg_surface->toplevel->client_pending.fullscreen, 485 toplevel->requested.fullscreen,
456 xdg_surface->toplevel->client_pending.fullscreen_output, 486 toplevel->requested.fullscreen_output,
457 csd); 487 csd);
458 488
459 transaction_commit_dirty(); 489 transaction_commit_dirty();
460 490
461 xdg_shell_view->commit.notify = handle_commit;
462 wl_signal_add(&xdg_surface->surface->events.commit,
463 &xdg_shell_view->commit);
464
465 xdg_shell_view->new_popup.notify = handle_new_popup; 491 xdg_shell_view->new_popup.notify = handle_new_popup;
466 wl_signal_add(&xdg_surface->events.new_popup, 492 wl_signal_add(&toplevel->base->events.new_popup,
467 &xdg_shell_view->new_popup); 493 &xdg_shell_view->new_popup);
468 494
469 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
470 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
471 &xdg_shell_view->request_fullscreen);
472
473 xdg_shell_view->request_maximize.notify = handle_request_maximize; 495 xdg_shell_view->request_maximize.notify = handle_request_maximize;
474 wl_signal_add(&xdg_surface->toplevel->events.request_maximize, 496 wl_signal_add(&toplevel->events.request_maximize,
475 &xdg_shell_view->request_maximize); 497 &xdg_shell_view->request_maximize);
476 498
499 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
500 wl_signal_add(&toplevel->events.request_fullscreen,
501 &xdg_shell_view->request_fullscreen);
502
477 xdg_shell_view->request_move.notify = handle_request_move; 503 xdg_shell_view->request_move.notify = handle_request_move;
478 wl_signal_add(&xdg_surface->toplevel->events.request_move, 504 wl_signal_add(&toplevel->events.request_move,
479 &xdg_shell_view->request_move); 505 &xdg_shell_view->request_move);
480 506
481 xdg_shell_view->request_resize.notify = handle_request_resize; 507 xdg_shell_view->request_resize.notify = handle_request_resize;
482 wl_signal_add(&xdg_surface->toplevel->events.request_resize, 508 wl_signal_add(&toplevel->events.request_resize,
483 &xdg_shell_view->request_resize); 509 &xdg_shell_view->request_resize);
484 510
485 xdg_shell_view->set_title.notify = handle_set_title; 511 xdg_shell_view->set_title.notify = handle_set_title;
486 wl_signal_add(&xdg_surface->toplevel->events.set_title, 512 wl_signal_add(&toplevel->events.set_title,
487 &xdg_shell_view->set_title); 513 &xdg_shell_view->set_title);
488 514
489 xdg_shell_view->set_app_id.notify = handle_set_app_id; 515 xdg_shell_view->set_app_id.notify = handle_set_app_id;
490 wl_signal_add(&xdg_surface->toplevel->events.set_app_id, 516 wl_signal_add(&toplevel->events.set_app_id,
491 &xdg_shell_view->set_app_id); 517 &xdg_shell_view->set_app_id);
492} 518}
493 519
@@ -501,7 +527,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
501 wl_list_remove(&xdg_shell_view->destroy.link); 527 wl_list_remove(&xdg_shell_view->destroy.link);
502 wl_list_remove(&xdg_shell_view->map.link); 528 wl_list_remove(&xdg_shell_view->map.link);
503 wl_list_remove(&xdg_shell_view->unmap.link); 529 wl_list_remove(&xdg_shell_view->unmap.link);
504 view->wlr_xdg_surface = NULL; 530 wl_list_remove(&xdg_shell_view->commit.link);
531 view->wlr_xdg_toplevel = NULL;
505 if (view->xdg_decoration) { 532 if (view->xdg_decoration) {
506 view->xdg_decoration->view = NULL; 533 view->xdg_decoration->view = NULL;
507 } 534 }
@@ -513,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface(
513 return xdg_surface->data; 540 return xdg_surface->data;
514} 541}
515 542
516void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { 543void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) {
517 struct wlr_xdg_surface *xdg_surface = data; 544 struct wlr_xdg_toplevel *xdg_toplevel = data;
518
519 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
520 sway_log(SWAY_DEBUG, "New xdg_shell popup");
521 return;
522 }
523 545
524 sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", 546 sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",
525 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); 547 xdg_toplevel->title, xdg_toplevel->app_id);
526 wlr_xdg_surface_ping(xdg_surface); 548 wlr_xdg_surface_ping(xdg_toplevel->base);
527 549
528 struct sway_xdg_shell_view *xdg_shell_view = 550 struct sway_xdg_shell_view *xdg_shell_view =
529 calloc(1, sizeof(struct sway_xdg_shell_view)); 551 calloc(1, sizeof(struct sway_xdg_shell_view));
@@ -531,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
531 return; 553 return;
532 } 554 }
533 555
534 view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); 556 if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) {
535 xdg_shell_view->view.wlr_xdg_surface = xdg_surface; 557 free(xdg_shell_view);
558 return;
559 }
560 xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel;
536 561
537 xdg_shell_view->map.notify = handle_map; 562 xdg_shell_view->map.notify = handle_map;
538 wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); 563 wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map);
539 564
540 xdg_shell_view->unmap.notify = handle_unmap; 565 xdg_shell_view->unmap.notify = handle_unmap;
541 wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); 566 wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap);
567
568 xdg_shell_view->commit.notify = handle_commit;
569 wl_signal_add(&xdg_toplevel->base->surface->events.commit,
570 &xdg_shell_view->commit);
542 571
543 xdg_shell_view->destroy.notify = handle_destroy; 572 xdg_shell_view->destroy.notify = handle_destroy;
544 wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); 573 wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy);
574
575 wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base);
576
577 xdg_toplevel->base->data = xdg_shell_view;
545 578
546 xdg_surface->data = xdg_shell_view; 579 wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel,
580 XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
547} 581}
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 66cb3b02..270cf08f 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -1,20 +1,23 @@
1#define _POSIX_C_SOURCE 199309L
2#include <float.h> 1#include <float.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
5#include <wayland-server-core.h> 4#include <wayland-server-core.h>
6#include <wlr/types/wlr_output_layout.h> 5#include <wlr/types/wlr_output_layout.h>
7#include <wlr/types/wlr_output.h> 6#include <wlr/types/wlr_output.h>
7#include <wlr/types/wlr_xdg_activation_v1.h>
8#include <wlr/types/wlr_scene.h>
8#include <wlr/xwayland.h> 9#include <wlr/xwayland.h>
10#include <xcb/xcb_icccm.h>
9#include "log.h" 11#include "log.h"
10#include "sway/desktop.h"
11#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
12#include "sway/input/cursor.h" 13#include "sway/input/cursor.h"
13#include "sway/input/input-manager.h" 14#include "sway/input/input-manager.h"
14#include "sway/input/seat.h" 15#include "sway/input/seat.h"
15#include "sway/output.h" 16#include "sway/output.h"
17#include "sway/scene_descriptor.h"
16#include "sway/tree/arrange.h" 18#include "sway/tree/arrange.h"
17#include "sway/tree/container.h" 19#include "sway/tree/container.h"
20#include "sway/server.h"
18#include "sway/tree/view.h" 21#include "sway/tree/view.h"
19#include "sway/tree/workspace.h" 22#include "sway/tree/workspace.h"
20 23
@@ -42,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener,
42 ev->width, ev->height); 45 ev->width, ev->height);
43} 46}
44 47
45static void unmanaged_handle_commit(struct wl_listener *listener, void *data) {
46 struct sway_xwayland_unmanaged *surface =
47 wl_container_of(listener, surface, commit);
48 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
49
50 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
51 false);
52}
53
54static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { 48static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) {
55 struct sway_xwayland_unmanaged *surface = 49 struct sway_xwayland_unmanaged *surface =
56 wl_container_of(listener, surface, set_geometry); 50 wl_container_of(listener, surface, set_geometry);
57 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 51 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
58 52
59 if (xsurface->x != surface->lx || xsurface->y != surface->ly) { 53 wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y);
60 // Surface has moved
61 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
62 true);
63 surface->lx = xsurface->x;
64 surface->ly = xsurface->y;
65 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
66 true);
67 }
68} 54}
69 55
70static void unmanaged_handle_map(struct wl_listener *listener, void *data) { 56static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
@@ -72,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
72 wl_container_of(listener, surface, map); 58 wl_container_of(listener, surface, map);
73 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 59 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
74 60
75 wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); 61 surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged,
76 62 xsurface->surface);
77 wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry);
78 surface->set_geometry.notify = unmanaged_handle_set_geometry;
79 63
80 wl_signal_add(&xsurface->surface->events.commit, &surface->commit); 64 if (surface->surface_scene) {
81 surface->commit.notify = unmanaged_handle_commit; 65 scene_descriptor_assign(&surface->surface_scene->buffer->node,
66 SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface);
67 wlr_scene_node_set_position(&surface->surface_scene->buffer->node,
68 xsurface->x, xsurface->y);
82 69
83 surface->lx = xsurface->x; 70 wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry);
84 surface->ly = xsurface->y; 71 surface->set_geometry.notify = unmanaged_handle_set_geometry;
85 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); 72 }
86 73
87 if (wlr_xwayland_or_surface_wants_focus(xsurface)) { 74 if (wlr_xwayland_or_surface_wants_focus(xsurface)) {
88 struct sway_seat *seat = input_manager_current_seat(); 75 struct sway_seat *seat = input_manager_current_seat();
@@ -96,10 +83,13 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
96 struct sway_xwayland_unmanaged *surface = 83 struct sway_xwayland_unmanaged *surface =
97 wl_container_of(listener, surface, unmap); 84 wl_container_of(listener, surface, unmap);
98 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 85 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
99 desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); 86
100 wl_list_remove(&surface->link); 87 if (surface->surface_scene) {
101 wl_list_remove(&surface->set_geometry.link); 88 wl_list_remove(&surface->set_geometry.link);
102 wl_list_remove(&surface->commit.link); 89
90 wlr_scene_node_destroy(&surface->surface_scene->buffer->node);
91 surface->surface_scene = NULL;
92 }
103 93
104 struct sway_seat *seat = input_manager_current_seat(); 94 struct sway_seat *seat = input_manager_current_seat();
105 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { 95 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) {
@@ -121,18 +111,53 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
121 } 111 }
122} 112}
123 113
114static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) {
115 struct sway_xwayland_unmanaged *surface =
116 wl_container_of(listener, surface, request_activate);
117 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
118 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
119 return;
120 }
121 struct sway_seat *seat = input_manager_current_seat();
122 struct sway_container *focus = seat_get_focused_container(seat);
123 if (focus && focus->view && focus->view->pid != xsurface->pid) {
124 return;
125 }
126
127 seat_set_focus_surface(seat, xsurface->surface, false);
128}
129
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
124static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { 147static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
125 struct sway_xwayland_unmanaged *surface = 148 struct sway_xwayland_unmanaged *surface =
126 wl_container_of(listener, surface, destroy); 149 wl_container_of(listener, surface, destroy);
127 wl_list_remove(&surface->request_configure.link); 150 wl_list_remove(&surface->request_configure.link);
128 wl_list_remove(&surface->map.link); 151 wl_list_remove(&surface->associate.link);
129 wl_list_remove(&surface->unmap.link); 152 wl_list_remove(&surface->dissociate.link);
130 wl_list_remove(&surface->destroy.link); 153 wl_list_remove(&surface->destroy.link);
131 wl_list_remove(&surface->override_redirect.link); 154 wl_list_remove(&surface->override_redirect.link);
155 wl_list_remove(&surface->request_activate.link);
132 free(surface); 156 free(surface);
133} 157}
134 158
135static 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);
136 161
137struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); 162struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface);
138 163
@@ -141,14 +166,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi
141 wl_container_of(listener, surface, override_redirect); 166 wl_container_of(listener, surface, override_redirect);
142 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 167 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
143 168
144 bool mapped = xsurface->mapped; 169 bool associated = xsurface->surface != NULL;
170 bool mapped = associated && xsurface->surface->mapped;
145 if (mapped) { 171 if (mapped) {
146 unmanaged_handle_unmap(&surface->unmap, NULL); 172 unmanaged_handle_unmap(&surface->unmap, NULL);
147 } 173 }
174 if (associated) {
175 unmanaged_handle_dissociate(&surface->dissociate, NULL);
176 }
148 177
149 unmanaged_handle_destroy(&surface->destroy, NULL); 178 unmanaged_handle_destroy(&surface->destroy, NULL);
150 xsurface->data = NULL; 179 xsurface->data = NULL;
180
151 struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); 181 struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface);
182 if (associated) {
183 handle_associate(&xwayland_view->associate, NULL);
184 }
152 if (mapped) { 185 if (mapped) {
153 handle_map(&xwayland_view->map, xsurface); 186 handle_map(&xwayland_view->map, xsurface);
154 } 187 }
@@ -168,14 +201,16 @@ static struct sway_xwayland_unmanaged *create_unmanaged(
168 wl_signal_add(&xsurface->events.request_configure, 201 wl_signal_add(&xsurface->events.request_configure,
169 &surface->request_configure); 202 &surface->request_configure);
170 surface->request_configure.notify = unmanaged_handle_request_configure; 203 surface->request_configure.notify = unmanaged_handle_request_configure;
171 wl_signal_add(&xsurface->events.map, &surface->map); 204 wl_signal_add(&xsurface->events.associate, &surface->associate);
172 surface->map.notify = unmanaged_handle_map; 205 surface->associate.notify = unmanaged_handle_associate;
173 wl_signal_add(&xsurface->events.unmap, &surface->unmap); 206 wl_signal_add(&xsurface->events.dissociate, &surface->dissociate);
174 surface->unmap.notify = unmanaged_handle_unmap; 207 surface->dissociate.notify = unmanaged_handle_dissociate;
175 wl_signal_add(&xsurface->events.destroy, &surface->destroy); 208 wl_signal_add(&xsurface->events.destroy, &surface->destroy);
176 surface->destroy.notify = unmanaged_handle_destroy; 209 surface->destroy.notify = unmanaged_handle_destroy;
177 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); 210 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect);
178 surface->override_redirect.notify = unmanaged_handle_override_redirect; 211 surface->override_redirect.notify = unmanaged_handle_override_redirect;
212 wl_signal_add(&xsurface->events.request_activate, &surface->request_activate);
213 surface->request_activate.notify = unmanaged_handle_request_activate;
179 214
180 return surface; 215 return surface;
181} 216}
@@ -254,6 +289,7 @@ static void set_activated(struct sway_view *view, bool activated) {
254 } 289 }
255 290
256 wlr_xwayland_surface_activate(surface, activated); 291 wlr_xwayland_surface_activate(surface, activated);
292 wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE);
257} 293}
258 294
259static void set_tiled(struct sway_view *view, bool tiled) { 295static void set_tiled(struct sway_view *view, bool tiled) {
@@ -293,7 +329,7 @@ static bool wants_floating(struct sway_view *view) {
293 } 329 }
294 } 330 }
295 331
296 struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; 332 xcb_size_hints_t *size_hints = surface->size_hints;
297 if (size_hints != NULL && 333 if (size_hints != NULL &&
298 size_hints->min_width > 0 && size_hints->min_height > 0 && 334 size_hints->min_width > 0 && size_hints->min_height > 0 &&
299 (size_hints->max_width == size_hints->min_width || 335 (size_hints->max_width == size_hints->min_width ||
@@ -347,7 +383,7 @@ static void destroy(struct sway_view *view) {
347static void get_constraints(struct sway_view *view, double *min_width, 383static void get_constraints(struct sway_view *view, double *min_width,
348 double *max_width, double *min_height, double *max_height) { 384 double *max_width, double *min_height, double *max_height) {
349 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; 385 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
350 struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; 386 xcb_size_hints_t *size_hints = surface->size_hints;
351 387
352 if (size_hints == NULL) { 388 if (size_hints == NULL) {
353 *min_width = DBL_MIN; 389 *min_width = DBL_MIN;
@@ -377,17 +413,6 @@ static const struct sway_view_impl view_impl = {
377 .destroy = destroy, 413 .destroy = destroy,
378}; 414};
379 415
380static void get_geometry(struct sway_view *view, struct wlr_box *box) {
381 box->x = box->y = 0;
382 if (view->surface) {
383 box->width = view->surface->current.width;
384 box->height = view->surface->current.height;
385 } else {
386 box->width = 0;
387 box->height = 0;
388 }
389}
390
391static void handle_commit(struct wl_listener *listener, void *data) { 416static void handle_commit(struct wl_listener *listener, void *data) {
392 struct sway_xwayland_view *xwayland_view = 417 struct sway_xwayland_view *xwayland_view =
393 wl_container_of(listener, xwayland_view, commit); 418 wl_container_of(listener, xwayland_view, commit);
@@ -395,34 +420,38 @@ static void handle_commit(struct wl_listener *listener, void *data) {
395 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 420 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
396 struct wlr_surface_state *state = &xsurface->surface->current; 421 struct wlr_surface_state *state = &xsurface->surface->current;
397 422
398 struct wlr_box new_geo; 423 struct wlr_box new_geo = {0};
399 get_geometry(view, &new_geo); 424 new_geo.width = state->width;
425 new_geo.height = state->height;
426
400 bool new_size = new_geo.width != view->geometry.width || 427 bool new_size = new_geo.width != view->geometry.width ||
401 new_geo.height != view->geometry.height || 428 new_geo.height != view->geometry.height;
402 new_geo.x != view->geometry.x ||
403 new_geo.y != view->geometry.y;
404 429
405 if (new_size) { 430 if (new_size) {
406 // The client changed its surface size in this commit. For floating 431 // The client changed its surface size in this commit. For floating
407 // containers, we resize the container to match. For tiling containers, 432 // containers, we resize the container to match. For tiling containers,
408 // we only recenter the surface. 433 // we only recenter the surface.
409 desktop_damage_view(view);
410 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 434 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
411 if (container_is_floating(view->container)) { 435 if (container_is_floating(view->container)) {
412 view_update_size(view); 436 view_update_size(view);
413 transaction_commit_dirty_client(); 437 transaction_commit_dirty_client();
414 } else {
415 view_center_surface(view);
416 } 438 }
417 desktop_damage_view(view); 439
440 view_center_and_clip_surface(view);
418 } 441 }
419 442
420 if (view->container->node.instruction) { 443 if (view->container->node.instruction) {
421 transaction_notify_view_ready_by_geometry(view, 444 bool successful = transaction_notify_view_ready_by_geometry(view,
422 xsurface->x, xsurface->y, state->width, state->height); 445 xsurface->x, xsurface->y, state->width, state->height);
423 }
424 446
425 view_damage_from(view); 447 // If we saved the view and this commit isn't what we're looking for
448 // that means the user will never actually see the buffers submitted to
449 // us here. Just send frame done events to these surfaces so they can
450 // commit another time for us.
451 if (view->saved_surface_tree && !successful) {
452 view_send_frame_done(view);
453 }
454 }
426} 455}
427 456
428static void handle_destroy(struct wl_listener *listener, void *data) { 457static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -435,6 +464,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
435 wl_list_remove(&xwayland_view->commit.link); 464 wl_list_remove(&xwayland_view->commit.link);
436 } 465 }
437 466
467 xwayland_view->view.wlr_xwayland_surface = NULL;
468
438 wl_list_remove(&xwayland_view->destroy.link); 469 wl_list_remove(&xwayland_view->destroy.link);
439 wl_list_remove(&xwayland_view->request_configure.link); 470 wl_list_remove(&xwayland_view->request_configure.link);
440 wl_list_remove(&xwayland_view->request_fullscreen.link); 471 wl_list_remove(&xwayland_view->request_fullscreen.link);
@@ -445,11 +476,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
445 wl_list_remove(&xwayland_view->set_title.link); 476 wl_list_remove(&xwayland_view->set_title.link);
446 wl_list_remove(&xwayland_view->set_class.link); 477 wl_list_remove(&xwayland_view->set_class.link);
447 wl_list_remove(&xwayland_view->set_role.link); 478 wl_list_remove(&xwayland_view->set_role.link);
479 wl_list_remove(&xwayland_view->set_startup_id.link);
448 wl_list_remove(&xwayland_view->set_window_type.link); 480 wl_list_remove(&xwayland_view->set_window_type.link);
449 wl_list_remove(&xwayland_view->set_hints.link); 481 wl_list_remove(&xwayland_view->set_hints.link);
450 wl_list_remove(&xwayland_view->set_decorations.link); 482 wl_list_remove(&xwayland_view->set_decorations.link);
451 wl_list_remove(&xwayland_view->map.link); 483 wl_list_remove(&xwayland_view->associate.link);
452 wl_list_remove(&xwayland_view->unmap.link); 484 wl_list_remove(&xwayland_view->dissociate.link);
453 wl_list_remove(&xwayland_view->override_redirect.link); 485 wl_list_remove(&xwayland_view->override_redirect.link);
454 view_begin_destroy(&xwayland_view->view); 486 view_begin_destroy(&xwayland_view->view);
455} 487}
@@ -463,16 +495,28 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
463 return; 495 return;
464 } 496 }
465 497
498 wl_list_remove(&xwayland_view->commit.link);
499 wl_list_remove(&xwayland_view->surface_tree_destroy.link);
500
501 if (xwayland_view->surface_tree) {
502 wlr_scene_node_destroy(&xwayland_view->surface_tree->node);
503 xwayland_view->surface_tree = NULL;
504 }
505
466 view_unmap(view); 506 view_unmap(view);
507}
467 508
468 wl_list_remove(&xwayland_view->commit.link); 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;
469} 513}
470 514
471static void handle_map(struct wl_listener *listener, void *data) { 515static void handle_map(struct wl_listener *listener, void *data) {
472 struct sway_xwayland_view *xwayland_view = 516 struct sway_xwayland_view *xwayland_view =
473 wl_container_of(listener, xwayland_view, map); 517 wl_container_of(listener, xwayland_view, map);
474 struct wlr_xwayland_surface *xsurface = data;
475 struct sway_view *view = &xwayland_view->view; 518 struct sway_view *view = &xwayland_view->view;
519 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
476 520
477 view->natural_width = xsurface->width; 521 view->natural_width = xsurface->width;
478 view->natural_height = xsurface->height; 522 view->natural_height = xsurface->height;
@@ -485,23 +529,42 @@ static void handle_map(struct wl_listener *listener, void *data) {
485 // Put it back into the tree 529 // Put it back into the tree
486 view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); 530 view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false);
487 531
532 xwayland_view->surface_tree = wlr_scene_subsurface_tree_create(
533 xwayland_view->view.content_tree, xsurface->surface);
534
535 if (xwayland_view->surface_tree) {
536 xwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy;
537 wl_signal_add(&xwayland_view->surface_tree->node.events.destroy,
538 &xwayland_view->surface_tree_destroy);
539 }
540
488 transaction_commit_dirty(); 541 transaction_commit_dirty();
489} 542}
490 543
544static void handle_dissociate(struct wl_listener *listener, void *data);
545
491static void handle_override_redirect(struct wl_listener *listener, void *data) { 546static void handle_override_redirect(struct wl_listener *listener, void *data) {
492 struct sway_xwayland_view *xwayland_view = 547 struct sway_xwayland_view *xwayland_view =
493 wl_container_of(listener, xwayland_view, override_redirect); 548 wl_container_of(listener, xwayland_view, override_redirect);
494 struct wlr_xwayland_surface *xsurface = data;
495 struct sway_view *view = &xwayland_view->view; 549 struct sway_view *view = &xwayland_view->view;
550 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
496 551
497 bool mapped = xsurface->mapped; 552 bool associated = xsurface->surface != NULL;
553 bool mapped = associated && xsurface->surface->mapped;
498 if (mapped) { 554 if (mapped) {
499 handle_unmap(&xwayland_view->unmap, NULL); 555 handle_unmap(&xwayland_view->unmap, NULL);
500 } 556 }
557 if (associated) {
558 handle_dissociate(&xwayland_view->dissociate, NULL);
559 }
501 560
502 handle_destroy(&xwayland_view->destroy, view); 561 handle_destroy(&xwayland_view->destroy, view);
503 xsurface->data = NULL; 562 xsurface->data = NULL;
563
504 struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); 564 struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface);
565 if (associated) {
566 unmanaged_handle_associate(&unmanaged->associate, NULL);
567 }
505 if (mapped) { 568 if (mapped) {
506 unmanaged_handle_map(&unmanaged->map, xsurface); 569 unmanaged_handle_map(&unmanaged->map, xsurface);
507 } 570 }
@@ -513,7 +576,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
513 struct wlr_xwayland_surface_configure_event *ev = data; 576 struct wlr_xwayland_surface_configure_event *ev = data;
514 struct sway_view *view = &xwayland_view->view; 577 struct sway_view *view = &xwayland_view->view;
515 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 578 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
516 if (!xsurface->mapped) { 579 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
517 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, 580 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
518 ev->width, ev->height); 581 ev->width, ev->height);
519 return; 582 return;
@@ -542,7 +605,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
542 wl_container_of(listener, xwayland_view, request_fullscreen); 605 wl_container_of(listener, xwayland_view, request_fullscreen);
543 struct sway_view *view = &xwayland_view->view; 606 struct sway_view *view = &xwayland_view->view;
544 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 607 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
545 if (!xsurface->mapped) { 608 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
546 return; 609 return;
547 } 610 }
548 container_set_fullscreen(view->container, xsurface->fullscreen); 611 container_set_fullscreen(view->container, xsurface->fullscreen);
@@ -556,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) {
556 wl_container_of(listener, xwayland_view, request_minimize); 619 wl_container_of(listener, xwayland_view, request_minimize);
557 struct sway_view *view = &xwayland_view->view; 620 struct sway_view *view = &xwayland_view->view;
558 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 621 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
559 if (!xsurface->mapped) { 622 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
560 return; 623 return;
561 } 624 }
562 625
@@ -571,10 +634,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
571 wl_container_of(listener, xwayland_view, request_move); 634 wl_container_of(listener, xwayland_view, request_move);
572 struct sway_view *view = &xwayland_view->view; 635 struct sway_view *view = &xwayland_view->view;
573 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 636 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
574 if (!xsurface->mapped) { 637 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
575 return; 638 return;
576 } 639 }
577 if (!container_is_floating(view->container)) { 640 if (!container_is_floating(view->container) ||
641 view->container->pending.fullscreen_mode) {
578 return; 642 return;
579 } 643 }
580 struct sway_seat *seat = input_manager_current_seat(); 644 struct sway_seat *seat = input_manager_current_seat();
@@ -586,7 +650,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
586 wl_container_of(listener, xwayland_view, request_resize); 650 wl_container_of(listener, xwayland_view, request_resize);
587 struct sway_view *view = &xwayland_view->view; 651 struct sway_view *view = &xwayland_view->view;
588 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 652 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
589 if (!xsurface->mapped) { 653 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
590 return; 654 return;
591 } 655 }
592 if (!container_is_floating(view->container)) { 656 if (!container_is_floating(view->container)) {
@@ -602,10 +666,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) {
602 wl_container_of(listener, xwayland_view, request_activate); 666 wl_container_of(listener, xwayland_view, request_activate);
603 struct sway_view *view = &xwayland_view->view; 667 struct sway_view *view = &xwayland_view->view;
604 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 668 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
605 if (!xsurface->mapped) { 669 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
606 return; 670 return;
607 } 671 }
608 view_request_activate(view); 672 view_request_activate(view, NULL);
609 673
610 transaction_commit_dirty(); 674 transaction_commit_dirty();
611} 675}
@@ -615,7 +679,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) {
615 wl_container_of(listener, xwayland_view, set_title); 679 wl_container_of(listener, xwayland_view, set_title);
616 struct sway_view *view = &xwayland_view->view; 680 struct sway_view *view = &xwayland_view->view;
617 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 681 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
618 if (!xsurface->mapped) { 682 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
619 return; 683 return;
620 } 684 }
621 view_update_title(view, false); 685 view_update_title(view, false);
@@ -627,7 +691,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) {
627 wl_container_of(listener, xwayland_view, set_class); 691 wl_container_of(listener, xwayland_view, set_class);
628 struct sway_view *view = &xwayland_view->view; 692 struct sway_view *view = &xwayland_view->view;
629 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 693 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
630 if (!xsurface->mapped) { 694 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
631 return; 695 return;
632 } 696 }
633 view_execute_criteria(view); 697 view_execute_criteria(view);
@@ -638,18 +702,43 @@ static void handle_set_role(struct wl_listener *listener, void *data) {
638 wl_container_of(listener, xwayland_view, set_role); 702 wl_container_of(listener, xwayland_view, set_role);
639 struct sway_view *view = &xwayland_view->view; 703 struct sway_view *view = &xwayland_view->view;
640 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 704 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
641 if (!xsurface->mapped) { 705 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
642 return; 706 return;
643 } 707 }
644 view_execute_criteria(view); 708 view_execute_criteria(view);
645} 709}
646 710
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
647static void handle_set_window_type(struct wl_listener *listener, void *data) { 736static void handle_set_window_type(struct wl_listener *listener, void *data) {
648 struct sway_xwayland_view *xwayland_view = 737 struct sway_xwayland_view *xwayland_view =
649 wl_container_of(listener, xwayland_view, set_window_type); 738 wl_container_of(listener, xwayland_view, set_window_type);
650 struct sway_view *view = &xwayland_view->view; 739 struct sway_view *view = &xwayland_view->view;
651 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 740 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
652 if (!xsurface->mapped) { 741 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
653 return; 742 return;
654 } 743 }
655 view_execute_criteria(view); 744 view_execute_criteria(view);
@@ -660,20 +749,39 @@ static void handle_set_hints(struct wl_listener *listener, void *data) {
660 wl_container_of(listener, xwayland_view, set_hints); 749 wl_container_of(listener, xwayland_view, set_hints);
661 struct sway_view *view = &xwayland_view->view; 750 struct sway_view *view = &xwayland_view->view;
662 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 751 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
663 if (!xsurface->mapped) { 752 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
664 return; 753 return;
665 } 754 }
666 if (!xsurface->hints_urgency && view->urgent_timer) { 755 const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints);
756 if (!hints_urgency && view->urgent_timer) {
667 // The view is in the timeout period. We'll ignore the request to 757 // The view is in the timeout period. We'll ignore the request to
668 // unset urgency so that the view remains urgent until the timer clears 758 // unset urgency so that the view remains urgent until the timer clears
669 // it. 759 // it.
670 return; 760 return;
671 } 761 }
672 if (view->allow_request_urgent) { 762 if (view->allow_request_urgent) {
673 view_set_urgent(view, (bool)xsurface->hints_urgency); 763 view_set_urgent(view, hints_urgency);
674 } 764 }
675} 765}
676 766
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
677struct sway_view *view_from_wlr_xwayland_surface( 785struct sway_view *view_from_wlr_xwayland_surface(
678 struct wlr_xwayland_surface *xsurface) { 786 struct wlr_xwayland_surface *xsurface) {
679 return xsurface->data; 787 return xsurface->data;
@@ -689,7 +797,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
689 return NULL; 797 return NULL;
690 } 798 }
691 799
692 view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); 800 if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) {
801 free(xwayland_view);
802 return NULL;
803 }
693 xwayland_view->view.wlr_xwayland_surface = xsurface; 804 xwayland_view->view.wlr_xwayland_surface = xsurface;
694 805
695 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); 806 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
@@ -728,6 +839,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
728 wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); 839 wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role);
729 xwayland_view->set_role.notify = handle_set_role; 840 xwayland_view->set_role.notify = handle_set_role;
730 841
842 wl_signal_add(&xsurface->events.set_startup_id,
843 &xwayland_view->set_startup_id);
844 xwayland_view->set_startup_id.notify = handle_set_startup_id;
845
731 wl_signal_add(&xsurface->events.set_window_type, 846 wl_signal_add(&xsurface->events.set_window_type,
732 &xwayland_view->set_window_type); 847 &xwayland_view->set_window_type);
733 xwayland_view->set_window_type.notify = handle_set_window_type; 848 xwayland_view->set_window_type.notify = handle_set_window_type;
@@ -739,11 +854,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
739 &xwayland_view->set_decorations); 854 &xwayland_view->set_decorations);
740 xwayland_view->set_decorations.notify = handle_set_decorations; 855 xwayland_view->set_decorations.notify = handle_set_decorations;
741 856
742 wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); 857 wl_signal_add(&xsurface->events.associate, &xwayland_view->associate);
743 xwayland_view->unmap.notify = handle_unmap; 858 xwayland_view->associate.notify = handle_associate;
744 859
745 wl_signal_add(&xsurface->events.map, &xwayland_view->map); 860 wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate);
746 xwayland_view->map.notify = handle_map; 861 xwayland_view->dissociate.notify = handle_dissociate;
747 862
748 wl_signal_add(&xsurface->events.set_override_redirect, 863 wl_signal_add(&xsurface->events.set_override_redirect,
749 &xwayland_view->override_redirect); 864 &xwayland_view->override_redirect);
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 2fe5b202..3d04826c 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <math.h> 2#include <math.h>
4#include <libevdev/libevdev.h> 3#include <libevdev/libevdev.h>
@@ -7,8 +6,9 @@
7#include <time.h> 6#include <time.h>
8#include <strings.h> 7#include <strings.h>
9#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
10#include <wlr/types/wlr_idle.h> 9#include <wlr/types/wlr_cursor_shape_v1.h>
11#include <wlr/types/wlr_pointer.h> 10#include <wlr/types/wlr_pointer.h>
11#include <wlr/types/wlr_relative_pointer_v1.h>
12#include <wlr/types/wlr_touch.h> 12#include <wlr/types/wlr_touch.h>
13#include <wlr/types/wlr_tablet_v2.h> 13#include <wlr/types/wlr_tablet_v2.h>
14#include <wlr/types/wlr_tablet_pad.h> 14#include <wlr/types/wlr_tablet_pad.h>
@@ -19,12 +19,12 @@
19#include "log.h" 19#include "log.h"
20#include "util.h" 20#include "util.h"
21#include "sway/commands.h" 21#include "sway/commands.h"
22#include "sway/desktop.h"
23#include "sway/input/cursor.h" 22#include "sway/input/cursor.h"
24#include "sway/input/keyboard.h" 23#include "sway/input/keyboard.h"
25#include "sway/input/tablet.h" 24#include "sway/input/tablet.h"
26#include "sway/layers.h" 25#include "sway/layers.h"
27#include "sway/output.h" 26#include "sway/output.h"
27#include "sway/scene_descriptor.h"
28#include "sway/tree/container.h" 28#include "sway/tree/container.h"
29#include "sway/tree/root.h" 29#include "sway/tree/root.h"
30#include "sway/tree/view.h" 30#include "sway/tree/view.h"
@@ -37,45 +37,6 @@ static uint32_t get_current_time_msec(void) {
37 return now.tv_sec * 1000 + now.tv_nsec / 1000000; 37 return now.tv_sec * 1000 + now.tv_nsec / 1000000;
38} 38}
39 39
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,101 @@ 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 // check for unmanaged views first 47 struct wlr_scene_node *scene_node = NULL;
87#if HAVE_XWAYLAND 48
88 struct wl_list *unmanaged = &root->xwayland_unmanaged; 49 struct wlr_scene_node *node;
89 struct sway_xwayland_unmanaged *unmanaged_surface; 50 wl_list_for_each_reverse(node, &root->layer_tree->children, link) {
90 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { 51 struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node);
91 struct wlr_xwayland_surface *xsurface = 52
92 unmanaged_surface->wlr_xwayland_surface; 53 bool non_interactive = scene_descriptor_try_get(&layer->node,
93 54 SWAY_SCENE_DESC_NON_INTERACTIVE);
94 double _sx = lx - unmanaged_surface->lx; 55 if (non_interactive) {
95 double _sy = ly - unmanaged_surface->ly; 56 continue;
96 if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { 57 }
97 *surface = xsurface->surface; 58
98 *sx = _sx; 59 scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy);
99 *sy = _sy; 60 if (scene_node) {
100 return NULL; 61 break;
101 } 62 }
102 } 63 }
64
65 if (scene_node) {
66 // determine what wlr_surface we clicked on
67 if (scene_node->type == WLR_SCENE_NODE_BUFFER) {
68 struct wlr_scene_buffer *scene_buffer =
69 wlr_scene_buffer_from_node(scene_node);
70 struct wlr_scene_surface *scene_surface =
71 wlr_scene_surface_try_from_buffer(scene_buffer);
72
73 if (scene_surface) {
74 *surface = scene_surface->surface;
75 }
76 }
77
78 // determine what container we clicked on
79 struct wlr_scene_node *current = scene_node;
80 while (true) {
81 struct sway_container *con = scene_descriptor_try_get(current,
82 SWAY_SCENE_DESC_CONTAINER);
83
84 if (!con) {
85 struct sway_view *view = scene_descriptor_try_get(current,
86 SWAY_SCENE_DESC_VIEW);
87 if (view) {
88 con = view->container;
89 }
90 }
91
92 if (!con) {
93 struct sway_popup_desc *popup =
94 scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP);
95 if (popup && popup->view) {
96 con = popup->view->container;
97 }
98 }
99
100 if (con && (!con->view || con->view->surface)) {
101 return &con->node;
102 }
103
104 if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) {
105 // We don't want to feed through the current workspace on
106 // layer shells
107 return NULL;
108 }
109
110#if HAVE_XWAYLAND
111 if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) {
112 return NULL;
113 }
103#endif 114#endif
104 // find the output the cursor is on 115
116 if (!current->parent) {
117 break;
118 }
119
120 current = &current->parent->node;
121 }
122 }
123
124 // if we aren't on a container, determine what workspace we are on
105 struct wlr_output *wlr_output = wlr_output_layout_output_at( 125 struct wlr_output *wlr_output = wlr_output_layout_output_at(
106 root->output_layout, lx, ly); 126 root->output_layout, lx, ly);
107 if (wlr_output == NULL) { 127 if (wlr_output == NULL) {
108 return NULL; 128 return NULL;
109 } 129 }
130
110 struct sway_output *output = wlr_output->data; 131 struct sway_output *output = wlr_output->data;
111 if (!output || !output->enabled) { 132 if (!output || !output->enabled) {
112 // output is being destroyed or is being enabled 133 // output is being destroyed or is being enabled
113 return NULL; 134 return NULL;
114 } 135 }
115 double ox = lx, oy = ly;
116 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
117
118 if (root->fullscreen_global) {
119 // Try fullscreen container
120 struct sway_container *con = tiling_container_at(
121 &root->fullscreen_global->node, lx, ly, surface, sx, sy);
122 if (con) {
123 return &con->node;
124 }
125 return NULL;
126 }
127 136
128 // find the focused workspace on the output for this seat
129 struct sway_workspace *ws = output_get_active_workspace(output); 137 struct sway_workspace *ws = output_get_active_workspace(output);
130 if (!ws) { 138 if (!ws) {
131 return NULL; 139 return NULL;
132 } 140 }
133 141
134 if ((*surface = layer_surface_at(output,
135 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
136 ox, oy, sx, sy))) {
137 return NULL;
138 }
139 if (ws->fullscreen) {
140 // Try transient containers
141 for (int i = 0; i < ws->floating->length; ++i) {
142 struct sway_container *floater = ws->floating->items[i];
143 if (container_is_transient_for(floater, ws->fullscreen)) {
144 struct sway_container *con = tiling_container_at(
145 &floater->node, lx, ly, surface, sx, sy);
146 if (con) {
147 return &con->node;
148 }
149 }
150 }
151 // Try fullscreen container
152 struct sway_container *con =
153 tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy);
154 if (con) {
155 return &con->node;
156 }
157 return NULL;
158 }
159 if ((*surface = layer_surface_popup_at(output,
160 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
161 ox, oy, sx, sy))) {
162 return NULL;
163 }
164 if ((*surface = layer_surface_popup_at(output,
165 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
166 ox, oy, sx, sy))) {
167 return NULL;
168 }
169 if ((*surface = layer_surface_popup_at(output,
170 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
171 ox, oy, sx, sy))) {
172 return NULL;
173 }
174 if ((*surface = layer_surface_at(output,
175 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
176 ox, oy, sx, sy))) {
177 return NULL;
178 }
179
180 struct sway_container *c;
181 if ((c = container_at(ws, lx, ly, surface, sx, sy))) {
182 return &c->node;
183 }
184
185 if ((*surface = layer_surface_at(output,
186 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
187 ox, oy, sx, sy))) {
188 return NULL;
189 }
190 if ((*surface = layer_surface_at(output,
191 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
192 ox, oy, sx, sy))) {
193 return NULL;
194 }
195
196 return &ws->node; 142 return &ws->node;
197} 143}
198 144
@@ -218,7 +164,7 @@ void cursor_update_image(struct sway_cursor *cursor,
218 // Try a node's resize edge 164 // Try a node's resize edge
219 enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor); 165 enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor);
220 if (edge == WLR_EDGE_NONE) { 166 if (edge == WLR_EDGE_NONE) {
221 cursor_set_image(cursor, "left_ptr", NULL); 167 cursor_set_image(cursor, "default", NULL);
222 } else if (container_is_floating(node->sway_container)) { 168 } else if (container_is_floating(node->sway_container)) {
223 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); 169 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL);
224 } else { 170 } else {
@@ -229,12 +175,12 @@ void cursor_update_image(struct sway_cursor *cursor,
229 } 175 }
230 } 176 }
231 } else { 177 } else {
232 cursor_set_image(cursor, "left_ptr", NULL); 178 cursor_set_image(cursor, "default", NULL);
233 } 179 }
234} 180}
235 181
236static void cursor_hide(struct sway_cursor *cursor) { 182static void cursor_hide(struct sway_cursor *cursor) {
237 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); 183 wlr_cursor_unset_image(cursor->cursor);
238 cursor->hidden = true; 184 cursor->hidden = true;
239 wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); 185 wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat);
240} 186}
@@ -297,7 +243,7 @@ static enum sway_input_idle_source idle_source_from_device(
297 return IDLE_SOURCE_POINTER; 243 return IDLE_SOURCE_POINTER;
298 case WLR_INPUT_DEVICE_TOUCH: 244 case WLR_INPUT_DEVICE_TOUCH:
299 return IDLE_SOURCE_TOUCH; 245 return IDLE_SOURCE_TOUCH;
300 case WLR_INPUT_DEVICE_TABLET_TOOL: 246 case WLR_INPUT_DEVICE_TABLET:
301 return IDLE_SOURCE_TABLET_TOOL; 247 return IDLE_SOURCE_TABLET_TOOL;
302 case WLR_INPUT_DEVICE_TABLET_PAD: 248 case WLR_INPUT_DEVICE_TABLET_PAD:
303 return IDLE_SOURCE_TABLET_PAD; 249 return IDLE_SOURCE_TABLET_PAD;
@@ -346,7 +292,7 @@ void cursor_unhide(struct sway_cursor *cursor) {
346 wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); 292 wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor));
347} 293}
348 294
349static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, 295void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
350 struct wlr_input_device *device, double dx, double dy, 296 struct wlr_input_device *device, double dx, double dy,
351 double dx_unaccel, double dy_unaccel) { 297 double dx_unaccel, double dy_unaccel) {
352 wlr_relative_pointer_manager_v1_send_relative_motion( 298 wlr_relative_pointer_manager_v1_send_relative_motion(
@@ -383,33 +329,34 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
383static void handle_pointer_motion_relative( 329static void handle_pointer_motion_relative(
384 struct wl_listener *listener, void *data) { 330 struct wl_listener *listener, void *data) {
385 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); 331 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
386 struct wlr_event_pointer_motion *e = data; 332 struct wlr_pointer_motion_event *e = data;
387 cursor_handle_activity_from_device(cursor, e->device); 333 cursor_handle_activity_from_device(cursor, &e->pointer->base);
388 334
389 pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, 335 pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x,
390 e->unaccel_dx, e->unaccel_dy); 336 e->delta_y, e->unaccel_dx, e->unaccel_dy);
391} 337}
392 338
393static void handle_pointer_motion_absolute( 339static void handle_pointer_motion_absolute(
394 struct wl_listener *listener, void *data) { 340 struct wl_listener *listener, void *data) {
395 struct sway_cursor *cursor = 341 struct sway_cursor *cursor =
396 wl_container_of(listener, cursor, motion_absolute); 342 wl_container_of(listener, cursor, motion_absolute);
397 struct wlr_event_pointer_motion_absolute *event = data; 343 struct wlr_pointer_motion_absolute_event *event = data;
398 cursor_handle_activity_from_device(cursor, event->device); 344 cursor_handle_activity_from_device(cursor, &event->pointer->base);
399 345
400 double lx, ly; 346 double lx, ly;
401 wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, 347 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base,
402 event->x, event->y, &lx, &ly); 348 event->x, event->y, &lx, &ly);
403 349
404 double dx = lx - cursor->cursor->x; 350 double dx = lx - cursor->cursor->x;
405 double dy = ly - cursor->cursor->y; 351 double dy = ly - cursor->cursor->y;
406 352
407 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 353 pointer_motion(cursor, event->time_msec, &event->pointer->base, dx, dy,
354 dx, dy);
408} 355}
409 356
410void dispatch_cursor_button(struct sway_cursor *cursor, 357void dispatch_cursor_button(struct sway_cursor *cursor,
411 struct wlr_input_device *device, uint32_t time_msec, uint32_t button, 358 struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
412 enum wlr_button_state state) { 359 enum wl_pointer_button_state state) {
413 if (time_msec == 0) { 360 if (time_msec == 0) {
414 time_msec = get_current_time_msec(); 361 time_msec = get_current_time_msec();
415 } 362 }
@@ -419,9 +366,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
419 366
420static void handle_pointer_button(struct wl_listener *listener, void *data) { 367static void handle_pointer_button(struct wl_listener *listener, void *data) {
421 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 368 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
422 struct wlr_event_pointer_button *event = data; 369 struct wlr_pointer_button_event *event = data;
423 370
424 if (event->state == WLR_BUTTON_PRESSED) { 371 if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) {
425 cursor->pressed_button_count++; 372 cursor->pressed_button_count++;
426 } else { 373 } else {
427 if (cursor->pressed_button_count > 0) { 374 if (cursor->pressed_button_count > 0) {
@@ -431,20 +378,20 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
431 } 378 }
432 } 379 }
433 380
434 cursor_handle_activity_from_device(cursor, event->device); 381 cursor_handle_activity_from_device(cursor, &event->pointer->base);
435 dispatch_cursor_button(cursor, event->device, 382 dispatch_cursor_button(cursor, &event->pointer->base,
436 event->time_msec, event->button, event->state); 383 event->time_msec, event->button, event->state);
437} 384}
438 385
439void dispatch_cursor_axis(struct sway_cursor *cursor, 386void dispatch_cursor_axis(struct sway_cursor *cursor,
440 struct wlr_event_pointer_axis *event) { 387 struct wlr_pointer_axis_event *event) {
441 seatop_pointer_axis(cursor->seat, event); 388 seatop_pointer_axis(cursor->seat, event);
442} 389}
443 390
444static void handle_pointer_axis(struct wl_listener *listener, void *data) { 391static void handle_pointer_axis(struct wl_listener *listener, void *data) {
445 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); 392 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
446 struct wlr_event_pointer_axis *event = data; 393 struct wlr_pointer_axis_event *event = data;
447 cursor_handle_activity_from_device(cursor, event->device); 394 cursor_handle_activity_from_device(cursor, &event->pointer->base);
448 dispatch_cursor_axis(cursor, event); 395 dispatch_cursor_axis(cursor, event);
449} 396}
450 397
@@ -455,93 +402,76 @@ static void handle_pointer_frame(struct wl_listener *listener, void *data) {
455 402
456static void handle_touch_down(struct wl_listener *listener, void *data) { 403static void handle_touch_down(struct wl_listener *listener, void *data) {
457 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); 404 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);
458 struct wlr_event_touch_down *event = data; 405 struct wlr_touch_down_event *event = data;
459 cursor_handle_activity_from_device(cursor, event->device); 406 cursor_handle_activity_from_device(cursor, &event->touch->base);
460 cursor_hide(cursor); 407 cursor_hide(cursor);
461 408
462 struct sway_seat *seat = cursor->seat; 409 struct sway_seat *seat = cursor->seat;
463 struct wlr_seat *wlr_seat = seat->wlr_seat;
464 struct wlr_surface *surface = NULL;
465 410
466 double lx, ly; 411 double lx, ly;
467 wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, 412 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
468 event->x, event->y, &lx, &ly); 413 event->x, event->y, &lx, &ly);
469 double sx, sy;
470 struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy);
471 414
472 seat->touch_id = event->touch_id; 415 seat->touch_id = event->touch_id;
473 seat->touch_x = lx; 416 seat->touch_x = lx;
474 seat->touch_y = ly; 417 seat->touch_y = ly;
475 418
476 if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { 419 seatop_touch_down(seat, event, lx, ly);
477 if (seat_is_input_allowed(seat, surface)) { 420}
478 wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, 421
479 event->touch_id, sx, sy); 422static void handle_touch_up(struct wl_listener *listener, void *data) {
423 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);
424 struct wlr_touch_up_event *event = data;
425 cursor_handle_activity_from_device(cursor, &event->touch->base);
480 426
481 if (focused_node) { 427 struct sway_seat *seat = cursor->seat;
482 seat_set_focus(seat, focused_node); 428
483 } 429 if (cursor->simulating_pointer_from_touch) {
430 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
431 cursor->pointer_touch_up = true;
432 dispatch_cursor_button(cursor, &event->touch->base,
433 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
484 } 434 }
485 } else if (!cursor->simulating_pointer_from_touch && 435 } else {
486 (!surface || seat_is_input_allowed(seat, surface))) { 436 seatop_touch_up(seat, event);
487 // Fallback to cursor simulation.
488 // The pointer_touch_id state is needed, so drags are not aborted when over
489 // a surface supporting touch and multi touch events don't interfere.
490 cursor->simulating_pointer_from_touch = true;
491 cursor->pointer_touch_id = seat->touch_id;
492 double dx, dy;
493 dx = lx - cursor->cursor->x;
494 dy = ly - cursor->cursor->y;
495 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
496 dispatch_cursor_button(cursor, event->device, event->time_msec,
497 BTN_LEFT, WLR_BUTTON_PRESSED);
498 } 437 }
499} 438}
500 439
501static void handle_touch_up(struct wl_listener *listener, void *data) { 440static void handle_touch_cancel(struct wl_listener *listener, void *data) {
502 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); 441 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel);
503 struct wlr_event_touch_up *event = data; 442 struct wlr_touch_cancel_event *event = data;
504 cursor_handle_activity_from_device(cursor, event->device); 443 cursor_handle_activity_from_device(cursor, &event->touch->base);
505 444
506 struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; 445 struct sway_seat *seat = cursor->seat;
507 446
508 if (cursor->simulating_pointer_from_touch) { 447 if (cursor->simulating_pointer_from_touch) {
509 if (cursor->pointer_touch_id == cursor->seat->touch_id) { 448 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
510 cursor->pointer_touch_up = true; 449 cursor->pointer_touch_up = true;
511 dispatch_cursor_button(cursor, event->device, event->time_msec, 450 dispatch_cursor_button(cursor, &event->touch->base,
512 BTN_LEFT, WLR_BUTTON_RELEASED); 451 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
513 } 452 }
514 } else { 453 } else {
515 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); 454 seatop_touch_cancel(seat, event);
516 } 455 }
517} 456}
518 457
519static void handle_touch_motion(struct wl_listener *listener, void *data) { 458static void handle_touch_motion(struct wl_listener *listener, void *data) {
520 struct sway_cursor *cursor = 459 struct sway_cursor *cursor =
521 wl_container_of(listener, cursor, touch_motion); 460 wl_container_of(listener, cursor, touch_motion);
522 struct wlr_event_touch_motion *event = data; 461 struct wlr_touch_motion_event *event = data;
523 cursor_handle_activity_from_device(cursor, event->device); 462 cursor_handle_activity_from_device(cursor, &event->touch->base);
524 463
525 struct sway_seat *seat = cursor->seat; 464 struct sway_seat *seat = cursor->seat;
526 struct wlr_seat *wlr_seat = seat->wlr_seat;
527 struct wlr_surface *surface = NULL;
528 465
529 double lx, ly; 466 double lx, ly;
530 wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, 467 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
531 event->x, event->y, &lx, &ly); 468 event->x, event->y, &lx, &ly);
532 double sx, sy;
533 node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy);
534 469
535 if (seat->touch_id == event->touch_id) { 470 if (seat->touch_id == event->touch_id) {
536 seat->touch_x = lx; 471 seat->touch_x = lx;
537 seat->touch_y = ly; 472 seat->touch_y = ly;
538 473
539 struct sway_drag_icon *drag_icon; 474 drag_icons_update_position(seat);
540 wl_list_for_each(drag_icon, &root->drag_icons, link) {
541 if (drag_icon->seat == seat) {
542 drag_icon_update_position(drag_icon);
543 }
544 }
545 } 475 }
546 476
547 if (cursor->simulating_pointer_from_touch) { 477 if (cursor->simulating_pointer_from_touch) {
@@ -549,11 +479,11 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
549 double dx, dy; 479 double dx, dy;
550 dx = lx - cursor->cursor->x; 480 dx = lx - cursor->cursor->x;
551 dy = ly - cursor->cursor->y; 481 dy = ly - cursor->cursor->y;
552 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 482 pointer_motion(cursor, event->time_msec, &event->touch->base,
483 dx, dy, dx, dy);
553 } 484 }
554 } else if (surface) { 485 } else {
555 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, 486 seatop_touch_motion(seat, event, lx, ly);
556 event->touch_id, sx, sy);
557 } 487 }
558} 488}
559 489
@@ -588,14 +518,15 @@ static void apply_mapping_from_region(struct wlr_input_device *device,
588 double x1 = region->x1, x2 = region->x2; 518 double x1 = region->x1, x2 = region->x2;
589 double y1 = region->y1, y2 = region->y2; 519 double y1 = region->y1, y2 = region->y2;
590 520
591 if (region->mm) { 521 if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) {
592 if (device->width_mm == 0 || device->height_mm == 0) { 522 struct wlr_tablet *tablet = wlr_tablet_from_input_device(device);
523 if (tablet->width_mm == 0 || tablet->height_mm == 0) {
593 return; 524 return;
594 } 525 }
595 x1 /= device->width_mm; 526 x1 /= tablet->width_mm;
596 x2 /= device->width_mm; 527 x2 /= tablet->width_mm;
597 y1 /= device->height_mm; 528 y1 /= tablet->height_mm;
598 y2 /= device->height_mm; 529 y2 /= tablet->height_mm;
599 } 530 }
600 531
601 *x = apply_mapping_from_coord(x1, x2, *x); 532 *x = apply_mapping_from_coord(x1, x2, *x);
@@ -657,8 +588,8 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor,
657 588
658static void handle_tool_axis(struct wl_listener *listener, void *data) { 589static void handle_tool_axis(struct wl_listener *listener, void *data) {
659 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); 590 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
660 struct wlr_event_tablet_tool_axis *event = data; 591 struct wlr_tablet_tool_axis_event *event = data;
661 cursor_handle_activity_from_device(cursor, event->device); 592 cursor_handle_activity_from_device(cursor, &event->tablet->base);
662 593
663 struct sway_tablet_tool *sway_tool = event->tool->data; 594 struct sway_tablet_tool *sway_tool = event->tool->data;
664 if (!sway_tool) { 595 if (!sway_tool) {
@@ -713,8 +644,8 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
713 644
714static void handle_tool_tip(struct wl_listener *listener, void *data) { 645static void handle_tool_tip(struct wl_listener *listener, void *data) {
715 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); 646 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
716 struct wlr_event_tablet_tool_tip *event = data; 647 struct wlr_tablet_tool_tip_event *event = data;
717 cursor_handle_activity_from_device(cursor, event->device); 648 cursor_handle_activity_from_device(cursor, &event->tablet->base);
718 649
719 struct sway_tablet_tool *sway_tool = event->tool->data; 650 struct sway_tablet_tool *sway_tool = event->tool->data;
720 struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; 651 struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2;
@@ -729,8 +660,8 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
729 if (cursor->simulating_pointer_from_tool_tip && 660 if (cursor->simulating_pointer_from_tool_tip &&
730 event->state == WLR_TABLET_TOOL_TIP_UP) { 661 event->state == WLR_TABLET_TOOL_TIP_UP) {
731 cursor->simulating_pointer_from_tool_tip = false; 662 cursor->simulating_pointer_from_tool_tip = false;
732 dispatch_cursor_button(cursor, event->device, event->time_msec, 663 dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec,
733 BTN_LEFT, WLR_BUTTON_RELEASED); 664 BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
734 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 665 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
735 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { 666 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) {
736 // If we started holding the tool tip down on a surface that accepts 667 // If we started holding the tool tip down on a surface that accepts
@@ -741,8 +672,8 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
741 WLR_TABLET_TOOL_TIP_UP); 672 WLR_TABLET_TOOL_TIP_UP);
742 } else { 673 } else {
743 cursor->simulating_pointer_from_tool_tip = true; 674 cursor->simulating_pointer_from_tool_tip = true;
744 dispatch_cursor_button(cursor, event->device, event->time_msec, 675 dispatch_cursor_button(cursor, &event->tablet->base,
745 BTN_LEFT, WLR_BUTTON_PRESSED); 676 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
746 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 677 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
747 } 678 }
748 } else { 679 } else {
@@ -764,12 +695,13 @@ static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor,
764static void handle_tool_proximity(struct wl_listener *listener, void *data) { 695static void handle_tool_proximity(struct wl_listener *listener, void *data) {
765 struct sway_cursor *cursor = 696 struct sway_cursor *cursor =
766 wl_container_of(listener, cursor, tool_proximity); 697 wl_container_of(listener, cursor, tool_proximity);
767 struct wlr_event_tablet_tool_proximity *event = data; 698 struct wlr_tablet_tool_proximity_event *event = data;
768 cursor_handle_activity_from_device(cursor, event->device); 699 cursor_handle_activity_from_device(cursor, &event->tablet->base);
769 700
770 struct wlr_tablet_tool *tool = event->tool; 701 struct wlr_tablet_tool *tool = event->tool;
771 if (!tool->data) { 702 if (!tool->data) {
772 struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); 703 struct sway_tablet *tablet = get_tablet_for_device(cursor,
704 &event->tablet->base);
773 if (!tablet) { 705 if (!tablet) {
774 sway_log(SWAY_ERROR, "no tablet for tablet tool"); 706 sway_log(SWAY_ERROR, "no tablet for tablet tool");
775 return; 707 return;
@@ -794,8 +726,8 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) {
794 726
795static void handle_tool_button(struct wl_listener *listener, void *data) { 727static void handle_tool_button(struct wl_listener *listener, void *data) {
796 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); 728 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
797 struct wlr_event_tablet_tool_button *event = data; 729 struct wlr_tablet_tool_button_event *event = data;
798 cursor_handle_activity_from_device(cursor, event->device); 730 cursor_handle_activity_from_device(cursor, &event->tablet->base);
799 731
800 struct sway_tablet_tool *sway_tool = event->tool->data; 732 struct sway_tablet_tool *sway_tool = event->tool->data;
801 if (!sway_tool) { 733 if (!sway_tool) {
@@ -810,31 +742,71 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
810 node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, 742 node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y,
811 &surface, &sx, &sy); 743 &surface, &sx, &sy);
812 744
813 if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { 745 // TODO: floating resize should support graphics tablet events
746 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat);
747 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
748 bool mod_pressed = modifiers & config->floating_mod;
749
750 bool surface_supports_tablet_events =
751 surface && wlr_surface_accepts_tablet_v2(tablet_v2, surface);
752
753 // Simulate pointer when:
754 // 1. The modifier key is pressed, OR
755 // 2. The surface under the cursor does not support tablet events.
756 bool should_simulate_pointer = mod_pressed || !surface_supports_tablet_events;
757
758 // Similar to tool tip, we need to selectively simulate mouse events, but we
759 // want to make sure that it is always consistent. Because all tool buttons
760 // currently map to BTN_RIGHT, we need to keep count of how many tool
761 // buttons are currently pressed down so we can send consistent events.
762 //
763 // The logic follows:
764 // - If we are already simulating the pointer, we should continue to do so
765 // until at least no tool button is held down.
766 // - If we should simulate the pointer and no tool button is currently held
767 // down, begin simulating the pointer.
768 // - If neither of the above are true, send the tablet events.
769 if ((cursor->tool_buttons > 0 && cursor->simulating_pointer_from_tool_button)
770 || (cursor->tool_buttons == 0 && should_simulate_pointer)) {
771 cursor->simulating_pointer_from_tool_button = true;
772
814 // TODO: the user may want to configure which tool buttons are mapped to 773 // TODO: the user may want to configure which tool buttons are mapped to
815 // which simulated pointer buttons 774 // which simulated pointer buttons
816 switch (event->state) { 775 switch (event->state) {
817 case WLR_BUTTON_PRESSED: 776 case WLR_BUTTON_PRESSED:
818 if (cursor->tool_buttons == 0) { 777 if (cursor->tool_buttons == 0) {
819 dispatch_cursor_button(cursor, event->device, 778 dispatch_cursor_button(cursor, &event->tablet->base,
820 event->time_msec, BTN_RIGHT, event->state); 779 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED);
821 } 780 }
822 cursor->tool_buttons++;
823 break; 781 break;
824 case WLR_BUTTON_RELEASED: 782 case WLR_BUTTON_RELEASED:
825 if (cursor->tool_buttons == 1) { 783 if (cursor->tool_buttons <= 1) {
826 dispatch_cursor_button(cursor, event->device, 784 dispatch_cursor_button(cursor, &event->tablet->base,
827 event->time_msec, BTN_RIGHT, event->state); 785 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED);
828 } 786 }
829 cursor->tool_buttons--;
830 break; 787 break;
831 } 788 }
832 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 789 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
833 return; 790 } else {
791 cursor->simulating_pointer_from_tool_button = false;
792
793 wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool,
794 event->button, (enum zwp_tablet_pad_v2_button_state)event->state);
834 } 795 }
835 796
836 wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, 797 // Update tool button count.
837 event->button, (enum zwp_tablet_pad_v2_button_state)event->state); 798 switch (event->state) {
799 case WLR_BUTTON_PRESSED:
800 cursor->tool_buttons++;
801 break;
802 case WLR_BUTTON_RELEASED:
803 if (cursor->tool_buttons == 0) {
804 sway_log(SWAY_ERROR, "inconsistent tablet tool button events");
805 } else {
806 cursor->tool_buttons--;
807 }
808 break;
809 }
838} 810}
839 811
840static void check_constraint_region(struct sway_cursor *cursor) { 812static void check_constraint_region(struct sway_cursor *cursor) {
@@ -920,59 +892,68 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener,
920 event->hotspot_y, focused_client); 892 event->hotspot_y, focused_client);
921} 893}
922 894
895static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {
896 struct sway_cursor *cursor = wl_container_of(
897 listener, cursor, hold_begin);
898 struct wlr_pointer_hold_begin_event *event = data;
899 cursor_handle_activity_from_device(cursor, &event->pointer->base);
900 seatop_hold_begin(cursor->seat, event);
901}
902
903static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
904 struct sway_cursor *cursor = wl_container_of(
905 listener, cursor, hold_end);
906 struct wlr_pointer_hold_end_event *event = data;
907 cursor_handle_activity_from_device(cursor, &event->pointer->base);
908 seatop_hold_end(cursor->seat, event);
909}
910
923static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { 911static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) {
924 struct sway_cursor *cursor = wl_container_of( 912 struct sway_cursor *cursor = wl_container_of(
925 listener, cursor, pinch_begin); 913 listener, cursor, pinch_begin);
926 struct wlr_event_pointer_pinch_begin *event = data; 914 struct wlr_pointer_pinch_begin_event *event = data;
927 wlr_pointer_gestures_v1_send_pinch_begin( 915 cursor_handle_activity_from_device(cursor, &event->pointer->base);
928 cursor->pointer_gestures, cursor->seat->wlr_seat, 916 seatop_pinch_begin(cursor->seat, event);
929 event->time_msec, event->fingers);
930} 917}
931 918
932static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { 919static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) {
933 struct sway_cursor *cursor = wl_container_of( 920 struct sway_cursor *cursor = wl_container_of(
934 listener, cursor, pinch_update); 921 listener, cursor, pinch_update);
935 struct wlr_event_pointer_pinch_update *event = data; 922 struct wlr_pointer_pinch_update_event *event = data;
936 wlr_pointer_gestures_v1_send_pinch_update( 923 cursor_handle_activity_from_device(cursor, &event->pointer->base);
937 cursor->pointer_gestures, cursor->seat->wlr_seat, 924 seatop_pinch_update(cursor->seat, event);
938 event->time_msec, event->dx, event->dy,
939 event->scale, event->rotation);
940} 925}
941 926
942static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { 927static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
943 struct sway_cursor *cursor = wl_container_of( 928 struct sway_cursor *cursor = wl_container_of(
944 listener, cursor, pinch_end); 929 listener, cursor, pinch_end);
945 struct wlr_event_pointer_pinch_end *event = data; 930 struct wlr_pointer_pinch_end_event *event = data;
946 wlr_pointer_gestures_v1_send_pinch_end( 931 cursor_handle_activity_from_device(cursor, &event->pointer->base);
947 cursor->pointer_gestures, cursor->seat->wlr_seat, 932 seatop_pinch_end(cursor->seat, event);
948 event->time_msec, event->cancelled);
949} 933}
950 934
951static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { 935static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) {
952 struct sway_cursor *cursor = wl_container_of( 936 struct sway_cursor *cursor = wl_container_of(
953 listener, cursor, swipe_begin); 937 listener, cursor, swipe_begin);
954 struct wlr_event_pointer_swipe_begin *event = data; 938 struct wlr_pointer_swipe_begin_event *event = data;
955 wlr_pointer_gestures_v1_send_swipe_begin( 939 cursor_handle_activity_from_device(cursor, &event->pointer->base);
956 cursor->pointer_gestures, cursor->seat->wlr_seat, 940 seatop_swipe_begin(cursor->seat, event);
957 event->time_msec, event->fingers);
958} 941}
959 942
960static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { 943static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) {
961 struct sway_cursor *cursor = wl_container_of( 944 struct sway_cursor *cursor = wl_container_of(
962 listener, cursor, swipe_update); 945 listener, cursor, swipe_update);
963 struct wlr_event_pointer_swipe_update *event = data; 946 struct wlr_pointer_swipe_update_event *event = data;
964 wlr_pointer_gestures_v1_send_swipe_update( 947 cursor_handle_activity_from_device(cursor, &event->pointer->base);
965 cursor->pointer_gestures, cursor->seat->wlr_seat, 948 seatop_swipe_update(cursor->seat, event);
966 event->time_msec, event->dx, event->dy);
967} 949}
968 950
969static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { 951static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
970 struct sway_cursor *cursor = wl_container_of( 952 struct sway_cursor *cursor = wl_container_of(
971 listener, cursor, swipe_end); 953 listener, cursor, swipe_end);
972 struct wlr_event_pointer_swipe_end *event = data; 954 struct wlr_pointer_swipe_end_event *event = data;
973 wlr_pointer_gestures_v1_send_swipe_end( 955 cursor_handle_activity_from_device(cursor, &event->pointer->base);
974 cursor->pointer_gestures, cursor->seat->wlr_seat, 956 seatop_swipe_end(cursor->seat, event);
975 event->time_msec, event->cancelled);
976} 957}
977 958
978static void handle_image_surface_destroy(struct wl_listener *listener, 959static void handle_image_surface_destroy(struct wl_listener *listener,
@@ -1011,10 +992,9 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image,
1011 } 992 }
1012 993
1013 if (!image) { 994 if (!image) {
1014 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); 995 wlr_cursor_unset_image(cursor->cursor);
1015 } else if (!current_image || strcmp(current_image, image) != 0) { 996 } else if (!current_image || strcmp(current_image, image) != 0) {
1016 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, 997 wlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image);
1017 cursor->cursor);
1018 } 998 }
1019} 999}
1020 1000
@@ -1046,6 +1026,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1046 wl_event_source_remove(cursor->hide_source); 1026 wl_event_source_remove(cursor->hide_source);
1047 1027
1048 wl_list_remove(&cursor->image_surface_destroy.link); 1028 wl_list_remove(&cursor->image_surface_destroy.link);
1029 wl_list_remove(&cursor->hold_begin.link);
1030 wl_list_remove(&cursor->hold_end.link);
1049 wl_list_remove(&cursor->pinch_begin.link); 1031 wl_list_remove(&cursor->pinch_begin.link);
1050 wl_list_remove(&cursor->pinch_update.link); 1032 wl_list_remove(&cursor->pinch_update.link);
1051 wl_list_remove(&cursor->pinch_end.link); 1033 wl_list_remove(&cursor->pinch_end.link);
@@ -1059,6 +1041,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1059 wl_list_remove(&cursor->frame.link); 1041 wl_list_remove(&cursor->frame.link);
1060 wl_list_remove(&cursor->touch_down.link); 1042 wl_list_remove(&cursor->touch_down.link);
1061 wl_list_remove(&cursor->touch_up.link); 1043 wl_list_remove(&cursor->touch_up.link);
1044 wl_list_remove(&cursor->touch_cancel.link);
1062 wl_list_remove(&cursor->touch_motion.link); 1045 wl_list_remove(&cursor->touch_motion.link);
1063 wl_list_remove(&cursor->touch_frame.link); 1046 wl_list_remove(&cursor->touch_frame.link);
1064 wl_list_remove(&cursor->tool_axis.link); 1047 wl_list_remove(&cursor->tool_axis.link);
@@ -1095,19 +1078,24 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1095 wl_list_init(&cursor->image_surface_destroy.link); 1078 wl_list_init(&cursor->image_surface_destroy.link);
1096 cursor->image_surface_destroy.notify = handle_image_surface_destroy; 1079 cursor->image_surface_destroy.notify = handle_image_surface_destroy;
1097 1080
1098 cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); 1081 wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
1099 cursor->pinch_begin.notify = handle_pointer_pinch_begin; 1082 cursor->hold_begin.notify = handle_pointer_hold_begin;
1083 wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
1084 cursor->hold_end.notify = handle_pointer_hold_end;
1085
1100 wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); 1086 wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin);
1101 cursor->pinch_update.notify = handle_pointer_pinch_update; 1087 cursor->pinch_begin.notify = handle_pointer_pinch_begin;
1102 wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); 1088 wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update);
1103 cursor->pinch_end.notify = handle_pointer_pinch_end; 1089 cursor->pinch_update.notify = handle_pointer_pinch_update;
1104 wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); 1090 wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end);
1105 cursor->swipe_begin.notify = handle_pointer_swipe_begin; 1091 cursor->pinch_end.notify = handle_pointer_pinch_end;
1092
1106 wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); 1093 wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin);
1107 cursor->swipe_update.notify = handle_pointer_swipe_update; 1094 cursor->swipe_begin.notify = handle_pointer_swipe_begin;
1108 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); 1095 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);
1109 cursor->swipe_end.notify = handle_pointer_swipe_end; 1096 cursor->swipe_update.notify = handle_pointer_swipe_update;
1110 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); 1097 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);
1098 cursor->swipe_end.notify = handle_pointer_swipe_end;
1111 1099
1112 // input events 1100 // input events
1113 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 1101 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
@@ -1132,6 +1120,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1132 wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); 1120 wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up);
1133 cursor->touch_up.notify = handle_touch_up; 1121 cursor->touch_up.notify = handle_touch_up;
1134 1122
1123 wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel);
1124 cursor->touch_cancel.notify = handle_touch_cancel;
1125
1135 wl_signal_add(&wlr_cursor->events.touch_motion, 1126 wl_signal_add(&wlr_cursor->events.touch_motion,
1136 &cursor->touch_motion); 1127 &cursor->touch_motion);
1137 cursor->touch_motion.notify = handle_touch_motion; 1128 cursor->touch_motion.notify = handle_touch_motion;
@@ -1224,11 +1215,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) {
1224 // Get event code from name 1215 // Get event code from name
1225 int code = libevdev_event_code_from_name(EV_KEY, name); 1216 int code = libevdev_event_code_from_name(EV_KEY, name);
1226 if (code == -1) { 1217 if (code == -1) {
1227 size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; 1218 *error = format_str("Unknown event %s", name);
1228 *error = malloc(len);
1229 if (*error) {
1230 snprintf(*error, len, "Unknown event %s", name);
1231 }
1232 return 0; 1219 return 0;
1233 } 1220 }
1234 return code; 1221 return code;
@@ -1250,13 +1237,8 @@ uint32_t get_mouse_bindcode(const char *name, char **error) {
1250 } 1237 }
1251 const char *event = libevdev_event_code_get_name(EV_KEY, code); 1238 const char *event = libevdev_event_code_get_name(EV_KEY, code);
1252 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { 1239 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) {
1253 size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", 1240 *error = format_str("Event code %d (%s) is not a button",
1254 code, event ? event : "(null)") + 1; 1241 code, event ? event : "(null)");
1255 *error = malloc(len);
1256 if (*error) {
1257 snprintf(*error, len, "Event code %d (%s) is not a button",
1258 code, event ? event : "(null)");
1259 }
1260 return 0; 1242 return 0;
1261 } 1243 }
1262 return code; 1244 return code;
@@ -1289,12 +1271,15 @@ const char *get_mouse_button_name(uint32_t button) {
1289static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { 1271static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) {
1290 struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; 1272 struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint;
1291 1273
1292 if (constraint->current.committed & 1274 if (constraint->current.cursor_hint.enabled) {
1293 WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
1294 double sx = constraint->current.cursor_hint.x; 1275 double sx = constraint->current.cursor_hint.x;
1295 double sy = constraint->current.cursor_hint.y; 1276 double sy = constraint->current.cursor_hint.y;
1296 1277
1297 struct sway_view *view = view_from_wlr_surface(constraint->surface); 1278 struct sway_view *view = view_from_wlr_surface(constraint->surface);
1279 if (!view) {
1280 return;
1281 }
1282
1298 struct sway_container *con = view->container; 1283 struct sway_container *con = view->container;
1299 1284
1300 double lx = sx + con->pending.content_x - view->geometry.x; 1285 double lx = sx + con->pending.content_x - view->geometry.x;
@@ -1345,12 +1330,9 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) {
1345 sway_constraint->destroy.notify = handle_constraint_destroy; 1330 sway_constraint->destroy.notify = handle_constraint_destroy;
1346 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); 1331 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy);
1347 1332
1348 struct sway_node *focus = seat_get_focus(seat); 1333 struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;
1349 if (focus && focus->type == N_CONTAINER && focus->sway_container->view) { 1334 if (surface && surface == constraint->surface) {
1350 struct wlr_surface *surface = focus->sway_container->view->surface; 1335 sway_cursor_constrain(seat->cursor, constraint);
1351 if (surface == constraint->surface) {
1352 sway_cursor_constrain(seat->cursor, constraint);
1353 }
1354 } 1336 }
1355} 1337}
1356 1338
@@ -1408,3 +1390,26 @@ void sway_cursor_constrain(struct sway_cursor *cursor,
1408 wl_signal_add(&constraint->surface->events.commit, 1390 wl_signal_add(&constraint->surface->events.commit,
1409 &cursor->constraint_commit); 1391 &cursor->constraint_commit);
1410} 1392}
1393
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 f04a8ce0..248ca34e 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -1,12 +1,12 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <string.h> 3#include <string.h>
5#include <math.h> 4#include <math.h>
5#include <assert.h>
6#include <wlr/config.h>
6#include <wlr/backend/libinput.h> 7#include <wlr/backend/libinput.h>
7#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
8#include <wlr/types/wlr_keyboard_group.h> 9#include <wlr/types/wlr_keyboard_group.h>
9#include <wlr/types/wlr_input_inhibitor.h>
10#include <wlr/types/wlr_virtual_keyboard_v1.h> 10#include <wlr/types/wlr_virtual_keyboard_v1.h>
11#include <wlr/types/wlr_virtual_pointer_v1.h> 11#include <wlr/types/wlr_virtual_pointer_v1.h>
12#include "sway/config.h" 12#include "sway/config.h"
@@ -22,6 +22,10 @@
22#include "list.h" 22#include "list.h"
23#include "log.h" 23#include "log.h"
24 24
25#if WLR_HAS_LIBINPUT_BACKEND
26#include <wlr/backend/libinput.h>
27#endif
28
25#define DEFAULT_SEAT "seat0" 29#define DEFAULT_SEAT "seat0"
26 30
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,29 +285,9 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
274 } 285 }
275 286
276 ipc_event_input("added", input_device); 287 ipc_event_input("added", input_device);
277}
278 288
279static void handle_inhibit_activate(struct wl_listener *listener, void *data) { 289 if (config_changed) {
280 struct sway_input_manager *input_manager = wl_container_of( 290 ipc_event_input("libinput_config", input_device);
281 listener, input_manager, inhibit_activate);
282 struct sway_seat *seat;
283 wl_list_for_each(seat, &input_manager->seats, link) {
284 seat_set_exclusive_client(seat, input_manager->inhibit->active_client);
285 }
286}
287
288static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) {
289 struct sway_input_manager *input_manager = wl_container_of(
290 listener, input_manager, inhibit_deactivate);
291 struct sway_seat *seat;
292 wl_list_for_each(seat, &input_manager->seats, link) {
293 seat_set_exclusive_client(seat, NULL);
294 struct sway_node *previous = seat_get_focus(seat);
295 if (previous) {
296 // Hack to get seat to re-focus the return value of get_focus
297 seat_set_focus(seat, NULL);
298 seat_set_focus(seat, previous);
299 }
300 } 291 }
301} 292}
302 293
@@ -377,7 +368,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) {
377 struct sway_input_manager *input_manager = 368 struct sway_input_manager *input_manager =
378 wl_container_of(listener, input_manager, virtual_keyboard_new); 369 wl_container_of(listener, input_manager, virtual_keyboard_new);
379 struct wlr_virtual_keyboard_v1 *keyboard = data; 370 struct wlr_virtual_keyboard_v1 *keyboard = data;
380 struct wlr_input_device *device = &keyboard->input_device; 371 struct wlr_input_device *device = &keyboard->keyboard.base;
381 372
382 // TODO: Amend protocol to allow NULL seat 373 // TODO: Amend protocol to allow NULL seat
383 struct sway_seat *seat = keyboard->seat ? 374 struct sway_seat *seat = keyboard->seat ?
@@ -410,7 +401,7 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) {
410 wl_container_of(listener, input_manager, virtual_pointer_new); 401 wl_container_of(listener, input_manager, virtual_pointer_new);
411 struct wlr_virtual_pointer_v1_new_pointer_event *event = data; 402 struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
412 struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; 403 struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
413 struct wlr_input_device *device = &pointer->input_device; 404 struct wlr_input_device *device = &pointer->pointer.base;
414 405
415 struct sway_seat *seat = event->suggested_seat ? 406 struct sway_seat *seat = event->suggested_seat ?
416 input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : 407 input_manager_sway_seat_from_wlr_seat(event->suggested_seat) :
@@ -442,6 +433,20 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) {
442 } 433 }
443} 434}
444 435
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
445struct sway_input_manager *input_manager_create(struct sway_server *server) { 450struct sway_input_manager *input_manager_create(struct sway_server *server) {
446 struct sway_input_manager *input = 451 struct sway_input_manager *input =
447 calloc(1, sizeof(struct sway_input_manager)); 452 calloc(1, sizeof(struct sway_input_manager));
@@ -468,14 +473,6 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) {
468 &input->virtual_pointer_new); 473 &input->virtual_pointer_new);
469 input->virtual_pointer_new.notify = handle_virtual_pointer; 474 input->virtual_pointer_new.notify = handle_virtual_pointer;
470 475
471 input->inhibit = wlr_input_inhibit_manager_create(server->wl_display);
472 input->inhibit_activate.notify = handle_inhibit_activate;
473 wl_signal_add(&input->inhibit->events.activate,
474 &input->inhibit_activate);
475 input->inhibit_deactivate.notify = handle_inhibit_deactivate;
476 wl_signal_add(&input->inhibit->events.deactivate,
477 &input->inhibit_deactivate);
478
479 input->keyboard_shortcuts_inhibit = 476 input->keyboard_shortcuts_inhibit =
480 wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); 477 wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display);
481 input->keyboard_shortcuts_inhibit_new_inhibitor.notify = 478 input->keyboard_shortcuts_inhibit_new_inhibitor.notify =
@@ -483,6 +480,17 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) {
483 wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, 480 wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor,
484 &input->keyboard_shortcuts_inhibit_new_inhibitor); 481 &input->keyboard_shortcuts_inhibit_new_inhibitor);
485 482
483 input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display);
484
485 input->transient_seat_manager =
486 wlr_transient_seat_manager_v1_create(server->wl_display);
487 assert(input->transient_seat_manager);
488
489 input->transient_seat_create.notify =
490 handle_transient_seat_manager_create_seat;
491 wl_signal_add(&input->transient_seat_manager->events.create_seat,
492 &input->transient_seat_create);
493
486 return input; 494 return input;
487} 495}
488 496
@@ -520,21 +528,50 @@ static void retranslate_keysyms(struct input_config *input_config) {
520 return; 528 return;
521 } 529 }
522 } 530 }
531
532 for (int i = 0; i < config->input_type_configs->length; ++i) {
533 struct input_config *ic = config->input_type_configs->items[i];
534 if (ic->xkb_layout || ic->xkb_file) {
535 // this is the first config with xkb_layout or xkb_file
536 if (ic->identifier == input_config->identifier) {
537 translate_keysyms(ic);
538 }
539
540 return;
541 }
542 }
523} 543}
524 544
525static void input_manager_configure_input( 545static void input_manager_configure_input(
526 struct sway_input_device *input_device) { 546 struct sway_input_device *input_device) {
527 sway_input_configure_libinput_device(input_device); 547#if WLR_HAS_LIBINPUT_BACKEND
548 bool config_changed = sway_input_configure_libinput_device(input_device);
549#else
550 bool config_changed = false;
551#endif
528 struct sway_seat *seat = NULL; 552 struct sway_seat *seat = NULL;
529 wl_list_for_each(seat, &server.input->seats, link) { 553 wl_list_for_each(seat, &server.input->seats, link) {
530 seat_configure_device(seat, input_device); 554 seat_configure_device(seat, input_device);
531 } 555 }
556 if (config_changed) {
557 ipc_event_input("libinput_config", input_device);
558 }
532} 559}
533 560
534void input_manager_configure_all_inputs(void) { 561void input_manager_configure_all_input_mappings(void) {
535 struct sway_input_device *input_device = NULL; 562 struct sway_input_device *input_device;
536 wl_list_for_each(input_device, &server.input->devices, link) { 563 wl_list_for_each(input_device, &server.input->devices, link) {
537 input_manager_configure_input(input_device); 564 struct sway_seat *seat;
565 wl_list_for_each(seat, &server.input->seats, link) {
566 seat_configure_device_mapping(seat, input_device);
567 }
568
569#if WLR_HAS_LIBINPUT_BACKEND
570 // Input devices mapped to unavailable outputs get their libinput
571 // send_events setting switched to false. We need to re-enable this
572 // when the output appears.
573 sway_input_configure_libinput_device_send_events(input_device);
574#endif
538 } 575 }
539} 576}
540 577
@@ -556,7 +593,9 @@ void input_manager_apply_input_config(struct input_config *input_config) {
556} 593}
557 594
558void 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
559 sway_input_reset_libinput_device(input_device); 597 sway_input_reset_libinput_device(input_device);
598#endif
560 struct sway_seat *seat = NULL; 599 struct sway_seat *seat = NULL;
561 wl_list_for_each(seat, &server.input->seats, link) { 600 wl_list_for_each(seat, &server.input->seats, link) {
562 seat_reset_device(seat, input_device); 601 seat_reset_device(seat, input_device);
@@ -564,6 +603,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) {
564} 603}
565 604
566void 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
567 struct sway_input_device *input_device = NULL; 613 struct sway_input_device *input_device = NULL;
568 wl_list_for_each(input_device, &server.input->devices, link) { 614 wl_list_for_each(input_device, &server.input->devices, link) {
569 input_manager_reset_input(input_device); 615 input_manager_reset_input(input_device);
@@ -572,7 +618,6 @@ void input_manager_reset_all_inputs(void) {
572 // If there is at least one keyboard using the default keymap, repeat delay, 618 // If there is at least one keyboard using the default keymap, repeat delay,
573 // and repeat rate, then it is possible that there is a keyboard group that 619 // and repeat rate, then it is possible that there is a keyboard group that
574 // need their keyboard disarmed. 620 // need their keyboard disarmed.
575 struct sway_seat *seat;
576 wl_list_for_each(seat, &server.input->seats, link) { 621 wl_list_for_each(seat, &server.input->seats, link) {
577 struct sway_keyboard_group *group; 622 struct sway_keyboard_group *group;
578 wl_list_for_each(group, &seat->keyboard_groups, link) { 623 wl_list_for_each(group, &seat->keyboard_groups, link) {
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index f258ac7d..f74d0658 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -1,10 +1,9 @@
1#include <assert.h> 1#include <assert.h>
2#include <limits.h> 2#include <limits.h>
3#include <strings.h> 3#include <strings.h>
4#include <wlr/config.h>
4#include <wlr/backend/multi.h> 5#include <wlr/backend/multi.h>
5#include <wlr/backend/session.h>
6#include <wlr/interfaces/wlr_keyboard.h> 6#include <wlr/interfaces/wlr_keyboard.h>
7#include <wlr/types/wlr_idle.h>
8#include <wlr/types/wlr_keyboard.h> 7#include <wlr/types/wlr_keyboard.h>
9#include <wlr/types/wlr_keyboard_group.h> 8#include <wlr/types/wlr_keyboard_group.h>
10#include <xkbcommon/xkbcommon-names.h> 9#include <xkbcommon/xkbcommon-names.h>
@@ -16,6 +15,10 @@
16#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
17#include "log.h" 16#include "log.h"
18 17
18#if WLR_HAS_SESSION
19#include <wlr/backend/session.h>
20#endif
21
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 }
@@ -291,14 +293,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
291static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, 293static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
292 xkb_keycode_t keycode, const xkb_keysym_t **keysyms, 294 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
293 uint32_t *modifiers) { 295 uint32_t *modifiers) {
294 struct wlr_input_device *device = 296 *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
295 keyboard->seat_device->input_device->wlr_device;
296 *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
297 xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( 297 xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
298 device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); 298 keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
299 *modifiers = *modifiers & ~consumed; 299 *modifiers = *modifiers & ~consumed;
300 300
301 return xkb_state_key_get_syms(device->keyboard->xkb_state, 301 return xkb_state_key_get_syms(keyboard->wlr->xkb_state,
302 keycode, keysyms); 302 keycode, keysyms);
303} 303}
304 304
@@ -314,13 +314,11 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
314static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, 314static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
315 xkb_keycode_t keycode, const xkb_keysym_t **keysyms, 315 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
316 uint32_t *modifiers) { 316 uint32_t *modifiers) {
317 struct wlr_input_device *device = 317 *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
318 keyboard->seat_device->input_device->wlr_device;
319 *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
320 318
321 xkb_layout_index_t layout_index = xkb_state_key_get_layout( 319 xkb_layout_index_t layout_index = xkb_state_key_get_layout(
322 device->keyboard->xkb_state, keycode); 320 keyboard->wlr->xkb_state, keycode);
323 return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, 321 return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap,
324 keycode, layout_index, 0, keysyms); 322 keycode, layout_index, 0, keysyms);
325} 323}
326 324
@@ -360,8 +358,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard,
360 keyinfo->keycode, &keyinfo->translated_keysyms, 358 keyinfo->keycode, &keyinfo->translated_keysyms,
361 &keyinfo->translated_modifiers); 359 &keyinfo->translated_modifiers);
362 360
363 keyinfo->code_modifiers = wlr_keyboard_get_modifiers( 361 keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
364 keyboard->seat_device->input_device->wlr_device->keyboard);
365 362
366 // Update shortcut model keyinfo 363 // Update shortcut model keyinfo
367 update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, 364 update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate,
@@ -401,15 +398,15 @@ static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab(
401} 398}
402 399
403static void handle_key_event(struct sway_keyboard *keyboard, 400static void handle_key_event(struct sway_keyboard *keyboard,
404 struct wlr_event_keyboard_key *event) { 401 struct wlr_keyboard_key_event *event) {
405 struct sway_seat *seat = keyboard->seat_device->sway_seat; 402 struct sway_seat *seat = keyboard->seat_device->sway_seat;
406 struct wlr_seat *wlr_seat = seat->wlr_seat; 403 struct wlr_seat *wlr_seat = seat->wlr_seat;
407 struct wlr_input_device *wlr_device = 404 struct wlr_input_device *wlr_device =
408 keyboard->seat_device->input_device->wlr_device; 405 keyboard->seat_device->input_device->wlr_device;
409 char *device_identifier = input_device_get_identifier(wlr_device); 406 char *device_identifier = input_device_get_identifier(wlr_device);
410 bool exact_identifier = wlr_device->keyboard->group != NULL; 407 bool exact_identifier = keyboard->wlr->group != NULL;
411 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); 408 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD);
412 bool input_inhibited = seat->exclusive_client != NULL; 409 bool locked = server.session_lock.lock;
413 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = 410 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =
414 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); 411 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat);
415 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; 412 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active;
@@ -427,17 +424,17 @@ static void handle_key_event(struct sway_keyboard *keyboard,
427 struct sway_binding *binding_released = NULL; 424 struct sway_binding *binding_released = NULL;
428 get_active_binding(&keyboard->state_keycodes, 425 get_active_binding(&keyboard->state_keycodes,
429 config->current_mode->keycode_bindings, &binding_released, 426 config->current_mode->keycode_bindings, &binding_released,
430 keyinfo.code_modifiers, true, input_inhibited, 427 keyinfo.code_modifiers, true, locked,
431 shortcuts_inhibited, device_identifier, 428 shortcuts_inhibited, device_identifier,
432 exact_identifier, keyboard->effective_layout); 429 exact_identifier, keyboard->effective_layout);
433 get_active_binding(&keyboard->state_keysyms_raw, 430 get_active_binding(&keyboard->state_keysyms_raw,
434 config->current_mode->keysym_bindings, &binding_released, 431 config->current_mode->keysym_bindings, &binding_released,
435 keyinfo.raw_modifiers, true, input_inhibited, 432 keyinfo.raw_modifiers, true, locked,
436 shortcuts_inhibited, device_identifier, 433 shortcuts_inhibited, device_identifier,
437 exact_identifier, keyboard->effective_layout); 434 exact_identifier, keyboard->effective_layout);
438 get_active_binding(&keyboard->state_keysyms_translated, 435 get_active_binding(&keyboard->state_keysyms_translated,
439 config->current_mode->keysym_bindings, &binding_released, 436 config->current_mode->keysym_bindings, &binding_released,
440 keyinfo.translated_modifiers, true, input_inhibited, 437 keyinfo.translated_modifiers, true, locked,
441 shortcuts_inhibited, device_identifier, 438 shortcuts_inhibited, device_identifier,
442 exact_identifier, keyboard->effective_layout); 439 exact_identifier, keyboard->effective_layout);
443 440
@@ -459,17 +456,17 @@ static void handle_key_event(struct sway_keyboard *keyboard,
459 if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { 456 if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
460 get_active_binding(&keyboard->state_keycodes, 457 get_active_binding(&keyboard->state_keycodes,
461 config->current_mode->keycode_bindings, &binding, 458 config->current_mode->keycode_bindings, &binding,
462 keyinfo.code_modifiers, false, input_inhibited, 459 keyinfo.code_modifiers, false, locked,
463 shortcuts_inhibited, device_identifier, 460 shortcuts_inhibited, device_identifier,
464 exact_identifier, keyboard->effective_layout); 461 exact_identifier, keyboard->effective_layout);
465 get_active_binding(&keyboard->state_keysyms_raw, 462 get_active_binding(&keyboard->state_keysyms_raw,
466 config->current_mode->keysym_bindings, &binding, 463 config->current_mode->keysym_bindings, &binding,
467 keyinfo.raw_modifiers, false, input_inhibited, 464 keyinfo.raw_modifiers, false, locked,
468 shortcuts_inhibited, device_identifier, 465 shortcuts_inhibited, device_identifier,
469 exact_identifier, keyboard->effective_layout); 466 exact_identifier, keyboard->effective_layout);
470 get_active_binding(&keyboard->state_keysyms_translated, 467 get_active_binding(&keyboard->state_keysyms_translated,
471 config->current_mode->keysym_bindings, &binding, 468 config->current_mode->keysym_bindings, &binding,
472 keyinfo.translated_modifiers, false, input_inhibited, 469 keyinfo.translated_modifiers, false, locked,
473 shortcuts_inhibited, device_identifier, 470 shortcuts_inhibited, device_identifier,
474 exact_identifier, keyboard->effective_layout); 471 exact_identifier, keyboard->effective_layout);
475 } 472 }
@@ -477,10 +474,10 @@ static void handle_key_event(struct sway_keyboard *keyboard,
477 // Set up (or clear) keyboard repeat for a pressed binding. Since the 474 // Set up (or clear) keyboard repeat for a pressed binding. Since the
478 // binding may remove the keyboard, the timer needs to be updated first 475 // binding may remove the keyboard, the timer needs to be updated first
479 if (binding && !(binding->flags & BINDING_NOREPEAT) && 476 if (binding && !(binding->flags & BINDING_NOREPEAT) &&
480 wlr_device->keyboard->repeat_info.delay > 0) { 477 keyboard->wlr->repeat_info.delay > 0) {
481 keyboard->repeat_binding = binding; 478 keyboard->repeat_binding = binding;
482 if (wl_event_source_timer_update(keyboard->key_repeat_source, 479 if (wl_event_source_timer_update(keyboard->key_repeat_source,
483 wlr_device->keyboard->repeat_info.delay) < 0) { 480 keyboard->wlr->repeat_info.delay) < 0) {
484 sway_log(SWAY_DEBUG, "failed to set key repeat timer"); 481 sway_log(SWAY_DEBUG, "failed to set key repeat timer");
485 } 482 }
486 } else if (keyboard->repeat_binding) { 483 } else if (keyboard->repeat_binding) {
@@ -492,7 +489,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
492 handled = true; 489 handled = true;
493 } 490 }
494 491
495 if (!handled && wlr_device->keyboard->group) { 492 if (!handled && keyboard->wlr->group) {
496 // Only handle device specific bindings for keyboards in a group 493 // Only handle device specific bindings for keyboards in a group
497 free(device_identifier); 494 free(device_identifier);
498 return; 495 return;
@@ -517,7 +514,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
517 &keyboard->state_pressed_sent, event->keycode, 514 &keyboard->state_pressed_sent, event->keycode,
518 event->state, keyinfo.keycode, 0); 515 event->state, keyinfo.keycode, 0);
519 if (pressed_sent) { 516 if (pressed_sent) {
520 wlr_seat_set_keyboard(wlr_seat, wlr_device); 517 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
521 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, 518 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
522 event->keycode, event->state); 519 event->keycode, event->state);
523 handled = true; 520 handled = true;
@@ -528,8 +525,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
528 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); 525 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
529 526
530 if (kb_grab) { 527 if (kb_grab) {
531 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, 528 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
532 wlr_device->keyboard);
533 wlr_input_method_keyboard_grab_v2_send_key(kb_grab, 529 wlr_input_method_keyboard_grab_v2_send_key(kb_grab,
534 event->time_msec, event->keycode, event->state); 530 event->time_msec, event->keycode, event->state);
535 handled = true; 531 handled = true;
@@ -542,7 +538,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
542 update_shortcut_state( 538 update_shortcut_state(
543 &keyboard->state_pressed_sent, event->keycode, event->state, 539 &keyboard->state_pressed_sent, event->keycode, event->state,
544 keyinfo.keycode, 0); 540 keyinfo.keycode, 0);
545 wlr_seat_set_keyboard(wlr_seat, wlr_device); 541 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
546 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, 542 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
547 event->keycode, event->state); 543 event->keycode, event->state);
548 } 544 }
@@ -618,14 +614,12 @@ static void handle_keyboard_group_leave(struct wl_listener *listener,
618} 614}
619 615
620static int handle_keyboard_repeat(void *data) { 616static int handle_keyboard_repeat(void *data) {
621 struct sway_keyboard *keyboard = (struct sway_keyboard *)data; 617 struct sway_keyboard *keyboard = data;
622 struct wlr_keyboard *wlr_device =
623 keyboard->seat_device->input_device->wlr_device->keyboard;
624 if (keyboard->repeat_binding) { 618 if (keyboard->repeat_binding) {
625 if (wlr_device->repeat_info.rate > 0) { 619 if (keyboard->wlr->repeat_info.rate > 0) {
626 // We queue the next event first, as the command might cancel it 620 // We queue the next event first, as the command might cancel it
627 if (wl_event_source_timer_update(keyboard->key_repeat_source, 621 if (wl_event_source_timer_update(keyboard->key_repeat_source,
628 1000 / wlr_device->repeat_info.rate) < 0) { 622 1000 / keyboard->wlr->repeat_info.rate) < 0) {
629 sway_log(SWAY_DEBUG, "failed to update key repeat timer"); 623 sway_log(SWAY_DEBUG, "failed to update key repeat timer");
630 } 624 }
631 } 625 }
@@ -658,31 +652,28 @@ static void determine_bar_visibility(uint32_t modifiers) {
658} 652}
659 653
660static void handle_modifier_event(struct sway_keyboard *keyboard) { 654static void handle_modifier_event(struct sway_keyboard *keyboard) {
661 struct wlr_input_device *wlr_device = 655 if (!keyboard->wlr->group) {
662 keyboard->seat_device->input_device->wlr_device;
663 if (!wlr_device->keyboard->group) {
664 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); 656 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
665 657
666 if (kb_grab) { 658 if (kb_grab) {
667 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, 659 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
668 wlr_device->keyboard);
669 wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, 660 wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab,
670 &wlr_device->keyboard->modifiers); 661 &keyboard->wlr->modifiers);
671 } else { 662 } else {
672 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; 663 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
673 wlr_seat_set_keyboard(wlr_seat, wlr_device); 664 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
674 wlr_seat_keyboard_notify_modifiers(wlr_seat, 665 wlr_seat_keyboard_notify_modifiers(wlr_seat,
675 &wlr_device->keyboard->modifiers); 666 &keyboard->wlr->modifiers);
676 } 667 }
677 668
678 uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); 669 uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
679 determine_bar_visibility(modifiers); 670 determine_bar_visibility(modifiers);
680 } 671 }
681 672
682 if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { 673 if (keyboard->wlr->modifiers.group != keyboard->effective_layout) {
683 keyboard->effective_layout = wlr_device->keyboard->modifiers.group; 674 keyboard->effective_layout = keyboard->wlr->modifiers.group;
684 675
685 if (!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { 676 if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) {
686 ipc_event_input("xkb_layout", keyboard->seat_device->input_device); 677 ipc_event_input("xkb_layout", keyboard->seat_device->input_device);
687 } 678 }
688 } 679 }
@@ -711,6 +702,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
711 } 702 }
712 703
713 keyboard->seat_device = device; 704 keyboard->seat_device = device;
705 keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device);
714 device->keyboard = keyboard; 706 device->keyboard = keyboard;
715 707
716 wl_list_init(&keyboard->keyboard_key.link); 708 wl_list_init(&keyboard->keyboard_key.link);
@@ -724,23 +716,11 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
724 716
725static void handle_xkb_context_log(struct xkb_context *context, 717static void handle_xkb_context_log(struct xkb_context *context,
726 enum xkb_log_level level, const char *format, va_list args) { 718 enum xkb_log_level level, const char *format, va_list args) {
727 va_list args_copy; 719 char *error = vformat_str(format, args);
728 va_copy(args_copy, args);
729 size_t length = vsnprintf(NULL, 0, format, args_copy) + 1;
730 va_end(args_copy);
731
732 char *error = malloc(length);
733 if (!error) {
734 sway_log(SWAY_ERROR, "Failed to allocate libxkbcommon log message");
735 return;
736 }
737
738 va_copy(args_copy, args);
739 vsnprintf(error, length, format, args_copy);
740 va_end(args_copy);
741 720
742 if (error[length - 2] == '\n') { 721 size_t len = strlen(error);
743 error[length - 2] = '\0'; 722 if (error[len - 1] == '\n') {
723 error[len - 1] = '\0';
744 } 724 }
745 725
746 sway_log_importance_t importance = SWAY_DEBUG; 726 sway_log_importance_t importance = SWAY_DEBUG;
@@ -761,7 +741,7 @@ static void handle_xkb_context_log(struct xkb_context *context,
761 741
762struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, 742struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,
763 char **error) { 743 char **error) {
764 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); 744 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV);
765 if (!sway_assert(context, "cannot create XKB context")) { 745 if (!sway_assert(context, "cannot create XKB context")) {
766 return NULL; 746 return NULL;
767 } 747 }
@@ -775,13 +755,8 @@ struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,
775 if (!keymap_file) { 755 if (!keymap_file) {
776 sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file); 756 sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file);
777 if (error) { 757 if (error) {
778 size_t len = snprintf(NULL, 0, "cannot read xkb file %s: %s", 758 *error = format_str("cannot read xkb file %s: %s",
779 ic->xkb_file, strerror(errno)) + 1; 759 ic->xkb_file, strerror(errno));
780 *error = malloc(len);
781 if (*error) {
782 snprintf(*error, len, "cannot read xkb_file %s: %s",
783 ic->xkb_file, strerror(errno));
784 }
785 } 760 }
786 goto cleanup; 761 goto cleanup;
787 } 762 }
@@ -819,13 +794,12 @@ static void destroy_empty_wlr_keyboard_group(void *data) {
819 794
820static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { 795static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
821 struct sway_input_device *device = keyboard->seat_device->input_device; 796 struct sway_input_device *device = keyboard->seat_device->input_device;
822 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; 797 struct wlr_keyboard_group *wlr_group = keyboard->wlr->group;
823 struct wlr_keyboard_group *wlr_group = wlr_keyboard->group;
824 798
825 sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", 799 sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p",
826 device->identifier, wlr_group); 800 device->identifier, wlr_group);
827 801
828 wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); 802 wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr);
829 803
830 if (wl_list_empty(&wlr_group->devices)) { 804 if (wl_list_empty(&wlr_group->devices)) {
831 sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", 805 sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p",
@@ -850,9 +824,7 @@ static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
850} 824}
851 825
852static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { 826static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
853 struct sway_input_device *device = keyboard->seat_device->input_device; 827 if (!keyboard->wlr->group) {
854 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
855 if (!wlr_keyboard->group) {
856 return; 828 return;
857 } 829 }
858 830
@@ -868,7 +840,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
868 break; 840 break;
869 case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ 841 case KEYBOARD_GROUP_DEFAULT: /* fallthrough */
870 case KEYBOARD_GROUP_SMART:; 842 case KEYBOARD_GROUP_SMART:;
871 struct wlr_keyboard_group *group = wlr_keyboard->group; 843 struct wlr_keyboard_group *group = keyboard->wlr->group;
872 if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || 844 if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) ||
873 !repeat_info_match(keyboard, &group->keyboard)) { 845 !repeat_info_match(keyboard, &group->keyboard)) {
874 sway_keyboard_group_remove(keyboard); 846 sway_keyboard_group_remove(keyboard);
@@ -879,7 +851,6 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
879 851
880static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { 852static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
881 struct sway_input_device *device = keyboard->seat_device->input_device; 853 struct sway_input_device *device = keyboard->seat_device->input_device;
882 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
883 struct sway_seat *seat = keyboard->seat_device->sway_seat; 854 struct sway_seat *seat = keyboard->seat_device->sway_seat;
884 struct seat_config *sc = seat_get_config(seat); 855 struct seat_config *sc = seat_get_config(seat);
885 856
@@ -911,7 +882,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
911 repeat_info_match(keyboard, &wlr_group->keyboard)) { 882 repeat_info_match(keyboard, &wlr_group->keyboard)) {
912 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", 883 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
913 device->identifier, wlr_group); 884 device->identifier, wlr_group);
914 wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); 885 wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr);
915 return; 886 return;
916 } 887 }
917 break; 888 break;
@@ -950,7 +921,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
950 goto cleanup; 921 goto cleanup;
951 } 922 }
952 sway_group->seat_device->input_device->wlr_device = 923 sway_group->seat_device->input_device->wlr_device =
953 sway_group->wlr_group->input_device; 924 &sway_group->wlr_group->keyboard.base;
954 925
955 if (!sway_keyboard_create(seat, sway_group->seat_device)) { 926 if (!sway_keyboard_create(seat, sway_group->seat_device)) {
956 sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); 927 sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group");
@@ -959,7 +930,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
959 930
960 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", 931 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
961 device->identifier, sway_group->wlr_group); 932 device->identifier, sway_group->wlr_group);
962 wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); 933 wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr);
963 934
964 wl_list_insert(&seat->keyboard_groups, &sway_group->link); 935 wl_list_insert(&seat->keyboard_groups, &sway_group->link);
965 936
@@ -991,10 +962,8 @@ cleanup:
991void sway_keyboard_configure(struct sway_keyboard *keyboard) { 962void sway_keyboard_configure(struct sway_keyboard *keyboard) {
992 struct input_config *input_config = 963 struct input_config *input_config =
993 input_device_get_config(keyboard->seat_device->input_device); 964 input_device_get_config(keyboard->seat_device->input_device);
994 struct wlr_input_device *wlr_device =
995 keyboard->seat_device->input_device->wlr_device;
996 965
997 if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard), 966 if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr),
998 "sway_keyboard_configure should not be called with a " 967 "sway_keyboard_configure should not be called with a "
999 "keyboard group's keyboard")) { 968 "keyboard group's keyboard")) {
1000 return; 969 return;
@@ -1036,11 +1005,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
1036 1005
1037 sway_keyboard_group_remove_invalid(keyboard); 1006 sway_keyboard_group_remove_invalid(keyboard);
1038 1007
1039 wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); 1008 wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap);
1040 wlr_keyboard_set_repeat_info(wlr_device->keyboard, 1009 wlr_keyboard_set_repeat_info(keyboard->wlr,
1041 keyboard->repeat_rate, keyboard->repeat_delay); 1010 keyboard->repeat_rate, keyboard->repeat_delay);
1042 1011
1043 if (!wlr_device->keyboard->group) { 1012 if (!keyboard->wlr->group) {
1044 sway_keyboard_group_add(keyboard); 1013 sway_keyboard_group_add(keyboard);
1045 } 1014 }
1046 1015
@@ -1060,40 +1029,42 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
1060 } 1029 }
1061 } 1030 }
1062 if (locked_mods) { 1031 if (locked_mods) {
1063 wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, 1032 wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0,
1064 locked_mods, 0); 1033 locked_mods, 0);
1065 uint32_t leds = 0; 1034 uint32_t leds = 0;
1066 for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { 1035 for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {
1067 if (xkb_state_led_index_is_active( 1036 if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state,
1068 wlr_device->keyboard->xkb_state, 1037 keyboard->wlr->led_indexes[i])) {
1069 wlr_device->keyboard->led_indexes[i])) {
1070 leds |= (1 << i); 1038 leds |= (1 << i);
1071 } 1039 }
1072 } 1040 }
1073 if (wlr_device->keyboard->group) { 1041 if (keyboard->wlr->group) {
1074 wlr_keyboard_led_update( 1042 wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds);
1075 &wlr_device->keyboard->group->keyboard, leds);
1076 } else { 1043 } else {
1077 wlr_keyboard_led_update(wlr_device->keyboard, leds); 1044 wlr_keyboard_led_update(keyboard->wlr, leds);
1078 } 1045 }
1079 } 1046 }
1080 } else { 1047 } else {
1081 xkb_keymap_unref(keymap); 1048 xkb_keymap_unref(keymap);
1082 sway_keyboard_group_remove_invalid(keyboard); 1049 sway_keyboard_group_remove_invalid(keyboard);
1083 if (!wlr_device->keyboard->group) { 1050 if (!keyboard->wlr->group) {
1084 sway_keyboard_group_add(keyboard); 1051 sway_keyboard_group_add(keyboard);
1085 } 1052 }
1086 } 1053 }
1087 1054
1055 // If the seat has no active keyboard, set this one
1088 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; 1056 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
1089 wlr_seat_set_keyboard(seat, wlr_device); 1057 struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard;
1058 if (current_keyboard == NULL) {
1059 wlr_seat_set_keyboard(seat, keyboard->wlr);
1060 }
1090 1061
1091 wl_list_remove(&keyboard->keyboard_key.link); 1062 wl_list_remove(&keyboard->keyboard_key.link);
1092 wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); 1063 wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key);
1093 keyboard->keyboard_key.notify = handle_keyboard_key; 1064 keyboard->keyboard_key.notify = handle_keyboard_key;
1094 1065
1095 wl_list_remove(&keyboard->keyboard_modifiers.link); 1066 wl_list_remove(&keyboard->keyboard_modifiers.link);
1096 wl_signal_add(&wlr_device->keyboard->events.modifiers, 1067 wl_signal_add(&keyboard->wlr->events.modifiers,
1097 &keyboard->keyboard_modifiers); 1068 &keyboard->keyboard_modifiers);
1098 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; 1069 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
1099 1070
@@ -1110,12 +1081,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
1110 if (!keyboard) { 1081 if (!keyboard) {
1111 return; 1082 return;
1112 } 1083 }
1113 if (keyboard->seat_device->input_device->wlr_device->keyboard->group) { 1084 if (keyboard->wlr->group) {
1114 sway_keyboard_group_remove(keyboard); 1085 sway_keyboard_group_remove(keyboard);
1115 } 1086 }
1116 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; 1087 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
1117 struct sway_input_device *device = keyboard->seat_device->input_device; 1088 if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) {
1118 if (wlr_seat_get_keyboard(wlr_seat) == device->wlr_device->keyboard) {
1119 wlr_seat_set_keyboard(wlr_seat, NULL); 1089 wlr_seat_set_keyboard(wlr_seat, NULL);
1120 } 1090 }
1121 if (keyboard->keymap) { 1091 if (keyboard->keymap) {
diff --git a/sway/input/libinput.c b/sway/input/libinput.c
index 060a584a..0266c7a9 100644
--- a/sway/input/libinput.c
+++ b/sway/input/libinput.c
@@ -79,6 +79,16 @@ static bool set_accel_speed(struct libinput_device *device, double speed) {
79 return true; 79 return true;
80} 80}
81 81
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 }
247
248 struct libinput_device *device =
249 wlr_libinput_get_device_handle(input_device->wlr_device);
250 sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')",
251 ic->identifier, input_device->identifier);
218 252
253 bool changed = configure_send_events(device, ic);
219 if (ic->tap != INT_MIN) { 254 if (ic->tap != INT_MIN) {
220 changed |= set_tap(device, ic->tap); 255 changed |= set_tap(device, ic->tap);
221 } 256 }
@@ -231,6 +266,9 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device
231 if (ic->pointer_accel != FLT_MIN) { 266 if (ic->pointer_accel != FLT_MIN) {
232 changed |= set_accel_speed(device, ic->pointer_accel); 267 changed |= set_accel_speed(device, ic->pointer_accel);
233 } 268 }
269 if (ic->rotation_angle != FLT_MIN) {
270 changed |= set_rotation_angle(device, ic->rotation_angle);
271 }
234 if (ic->accel_profile != INT_MIN) { 272 if (ic->accel_profile != INT_MIN) {
235 changed |= set_accel_profile(device, ic->accel_profile); 273 changed |= set_accel_profile(device, ic->accel_profile);
236 } 274 }
@@ -252,13 +290,33 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device
252 if (ic->scroll_button != INT_MIN) { 290 if (ic->scroll_button != INT_MIN) {
253 changed |= set_scroll_button(device, ic->scroll_button); 291 changed |= set_scroll_button(device, ic->scroll_button);
254 } 292 }
293 if (ic->scroll_button_lock != INT_MIN) {
294 changed |= set_scroll_button_lock(device, ic->scroll_button_lock);
295 }
255 if (ic->dwt != INT_MIN) { 296 if (ic->dwt != INT_MIN) {
256 changed |= set_dwt(device, ic->dwt); 297 changed |= set_dwt(device, ic->dwt);
257 } 298 }
299 if (ic->dwtp != INT_MIN) {
300 changed |= set_dwtp(device, ic->dwtp);
301 }
258 if (ic->calibration_matrix.configured) { 302 if (ic->calibration_matrix.configured) {
259 changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); 303 changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix);
260 } 304 }
261 305
306 return changed;
307}
308
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);
@@ -332,6 +394,13 @@ bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) {
332 return false; 394 return false;
333 } 395 }
334 396
335 const char prefix[] = "platform-"; 397 const char prefix_platform[] = "platform-";
336 return strncmp(id_path, prefix, strlen(prefix)) == 0; 398 if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) {
399 return false;
400 }
401
402 const char prefix_pci[] = "pci-";
403 const char infix_platform[] = "-platform-";
404 return (strncmp(id_path, prefix_pci, strlen(prefix_pci)) == 0) &&
405 strstr(id_path, infix_platform);
337} 406}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 2d714acd..0c5672bc 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,22 +1,23 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <string.h> 3#include <string.h>
5#include <strings.h> 4#include <strings.h>
6#include <time.h> 5#include <time.h>
6#include <wlr/config.h>
7#include <wlr/types/wlr_cursor.h> 7#include <wlr/types/wlr_cursor.h>
8#include <wlr/types/wlr_data_device.h> 8#include <wlr/types/wlr_data_device.h>
9#include <wlr/types/wlr_idle.h> 9#include <wlr/types/wlr_idle_notify_v1.h>
10#include <wlr/types/wlr_keyboard_group.h> 10#include <wlr/types/wlr_keyboard_group.h>
11#include <wlr/types/wlr_output_layout.h> 11#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_primary_selection.h> 12#include <wlr/types/wlr_primary_selection.h>
13#include <wlr/types/wlr_tablet_v2.h> 13#include <wlr/types/wlr_tablet_v2.h>
14#include <wlr/types/wlr_touch.h>
14#include <wlr/types/wlr_xcursor_manager.h> 15#include <wlr/types/wlr_xcursor_manager.h>
15#include "config.h" 16#include "config.h"
16#include "list.h" 17#include "list.h"
17#include "log.h" 18#include "log.h"
18#include "sway/config.h" 19#include "sway/config.h"
19#include "sway/desktop.h" 20#include "sway/scene_descriptor.h"
20#include "sway/input/cursor.h" 21#include "sway/input/cursor.h"
21#include "sway/input/input-manager.h" 22#include "sway/input/input-manager.h"
22#include "sway/input/keyboard.h" 23#include "sway/input/keyboard.h"
@@ -42,6 +43,7 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
42 sway_keyboard_destroy(seat_device->keyboard); 43 sway_keyboard_destroy(seat_device->keyboard);
43 sway_tablet_destroy(seat_device->tablet); 44 sway_tablet_destroy(seat_device->tablet);
44 sway_tablet_pad_destroy(seat_device->tablet_pad); 45 sway_tablet_pad_destroy(seat_device->tablet_pad);
46 sway_switch_destroy(seat_device->switch_device);
45 wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, 47 wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,
46 seat_device->input_device->wlr_device); 48 seat_device->input_device->wlr_device);
47 wl_list_remove(&seat_device->link); 49 wl_list_remove(&seat_device->link);
@@ -51,10 +53,26 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
51static void seat_node_destroy(struct sway_seat_node *seat_node) { 53static void seat_node_destroy(struct sway_seat_node *seat_node) {
52 wl_list_remove(&seat_node->destroy.link); 54 wl_list_remove(&seat_node->destroy.link);
53 wl_list_remove(&seat_node->link); 55 wl_list_remove(&seat_node->link);
56
57 /*
58 * This is the only time we remove items from the focus stack without
59 * immediately re-adding them. If we just removed the last thing,
60 * mark that nothing has focus anymore.
61 */
62 if (wl_list_empty(&seat_node->seat->focus_stack)) {
63 seat_node->seat->has_focus = false;
64 }
65
54 free(seat_node); 66 free(seat_node);
55} 67}
56 68
57void 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
58 if (seat == config->handler_context.seat) { 76 if (seat == config->handler_context.seat) {
59 config->handler_context.seat = input_manager_get_default_seat(); 77 config->handler_context.seat = input_manager_get_default_seat();
60 } 78 }
@@ -75,10 +93,11 @@ void seat_destroy(struct sway_seat *seat) {
75 wl_list_remove(&seat->request_set_selection.link); 93 wl_list_remove(&seat->request_set_selection.link);
76 wl_list_remove(&seat->request_set_primary_selection.link); 94 wl_list_remove(&seat->request_set_primary_selection.link);
77 wl_list_remove(&seat->link); 95 wl_list_remove(&seat->link);
78 wlr_seat_destroy(seat->wlr_seat); 96 wl_list_remove(&seat->destroy.link);
79 for (int i = 0; i < seat->deferred_bindings->length; i++) { 97 for (int i = 0; i < seat->deferred_bindings->length; i++) {
80 free_sway_binding(seat->deferred_bindings->items[i]); 98 free_sway_binding(seat->deferred_bindings->items[i]);
81 } 99 }
100 wlr_scene_node_destroy(&seat->scene_tree->node);
82 list_free(seat->deferred_bindings); 101 list_free(seat->deferred_bindings);
83 free(seat->prev_workspace_name); 102 free(seat->prev_workspace_name);
84 free(seat); 103 free(seat);
@@ -86,21 +105,10 @@ void seat_destroy(struct sway_seat *seat) {
86 105
87void seat_idle_notify_activity(struct sway_seat *seat, 106void seat_idle_notify_activity(struct sway_seat *seat,
88 enum sway_input_idle_source source) { 107 enum sway_input_idle_source source) {
89 uint32_t mask = seat->idle_inhibit_sources; 108 if ((source & seat->idle_inhibit_sources) == 0) {
90 struct wlr_idle_timeout *timeout; 109 return;
91 int ntimers = 0, nidle = 0;
92 wl_list_for_each(timeout, &server.idle->idle_timers, link) {
93 ++ntimers;
94 if (timeout->idle_state) {
95 ++nidle;
96 }
97 }
98 if (nidle == ntimers) {
99 mask = seat->idle_wake_sources;
100 }
101 if ((source & mask) > 0) {
102 wlr_idle_notify_activity(server.idle, seat->wlr_seat);
103 } 110 }
111 wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat);
104} 112}
105 113
106/** 114/**
@@ -130,7 +138,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
130 if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { 138 if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
131 continue; 139 continue;
132 } 140 }
133 if (input_device->wlr_device->keyboard == wlr_keyboard) { 141 if (input_device->wlr_device == &wlr_keyboard->base) {
134 return seat_device->keyboard; 142 return seat_device->keyboard;
135 } 143 }
136 } 144 }
@@ -138,7 +146,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
138 wl_list_for_each(group, &seat->keyboard_groups, link) { 146 wl_list_for_each(group, &seat->keyboard_groups, link) {
139 struct sway_input_device *input_device = 147 struct sway_input_device *input_device =
140 group->seat_device->input_device; 148 group->seat_device->input_device;
141 if (input_device->wlr_device->keyboard == wlr_keyboard) { 149 if (input_device->wlr_device == &wlr_keyboard->base) {
142 return group->seat_device->keyboard; 150 return group->seat_device->keyboard;
143 } 151 }
144 } 152 }
@@ -162,11 +170,11 @@ static void seat_keyboard_notify_enter(struct sway_seat *seat,
162 state->pressed_keycodes, state->npressed, &keyboard->modifiers); 170 state->pressed_keycodes, state->npressed, &keyboard->modifiers);
163} 171}
164 172
165static void seat_tablet_pads_notify_enter(struct sway_seat *seat, 173static void seat_tablet_pads_set_focus(struct sway_seat *seat,
166 struct wlr_surface *surface) { 174 struct wlr_surface *surface) {
167 struct sway_seat_device *seat_device; 175 struct sway_seat_device *seat_device;
168 wl_list_for_each(seat_device, &seat->devices, link) { 176 wl_list_for_each(seat_device, &seat->devices, link) {
169 sway_tablet_pad_notify_enter(seat_device->tablet_pad, surface); 177 sway_tablet_pad_set_focus(seat_device->tablet_pad, surface);
170 } 178 }
171} 179}
172 180
@@ -190,7 +198,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
190#endif 198#endif
191 199
192 seat_keyboard_notify_enter(seat, view->surface); 200 seat_keyboard_notify_enter(seat, view->surface);
193 seat_tablet_pads_notify_enter(seat, view->surface); 201 seat_tablet_pads_set_focus(seat, view->surface);
194 sway_input_method_relay_set_focus(&seat->im_relay, view->surface); 202 sway_input_method_relay_set_focus(&seat->im_relay, view->surface);
195 203
196 struct wlr_pointer_constraint_v1 *constraint = 204 struct wlr_pointer_constraint_v1 *constraint =
@@ -210,14 +218,13 @@ void seat_for_each_node(struct sway_seat *seat,
210 218
211struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, 219struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
212 struct sway_node *ancestor) { 220 struct sway_node *ancestor) {
213 if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) { 221 if (node_is_view(ancestor)) {
214 return ancestor->sway_container; 222 return ancestor->sway_container;
215 } 223 }
216 struct sway_seat_node *current; 224 struct sway_seat_node *current;
217 wl_list_for_each(current, &seat->focus_stack, link) { 225 wl_list_for_each(current, &seat->focus_stack, link) {
218 struct sway_node *node = current->node; 226 struct sway_node *node = current->node;
219 if (node->type == N_CONTAINER && node->sway_container->view && 227 if (node_is_view(node) && node_has_ancestor(node, ancestor)) {
220 node_has_ancestor(node, ancestor)) {
221 return node->sway_container; 228 return node->sway_container;
222 } 229 }
223 } 230 }
@@ -236,7 +243,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
236 seat_node_destroy(seat_node); 243 seat_node_destroy(seat_node);
237 // If an unmanaged or layer surface is focused when an output gets 244 // If an unmanaged or layer surface is focused when an output gets
238 // disabled and an empty workspace on the output was focused by the 245 // disabled and an empty workspace on the output was focused by the
239 // seat, the seat needs to refocus it's focus inactive to update the 246 // seat, the seat needs to refocus its focus inactive to update the
240 // value of seat->workspace. 247 // value of seat->workspace.
241 if (seat->workspace == node->sway_workspace) { 248 if (seat->workspace == node->sway_workspace) {
242 struct sway_node *node = seat_get_focus_inactive(seat, &root->node); 249 struct sway_node *node = seat_get_focus_inactive(seat, &root->node);
@@ -352,25 +359,15 @@ static void handle_new_node(struct wl_listener *listener, void *data) {
352 seat_node_from_node(seat, node); 359 seat_node_from_node(seat, node);
353} 360}
354 361
355static 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) {
356 if (!icon->wlr_drag_icon->mapped) { 363 struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON);
357 return;
358 }
359 desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true);
360}
361
362void drag_icon_update_position(struct sway_drag_icon *icon) {
363 drag_icon_damage_whole(icon);
364
365 struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon;
366 struct sway_seat *seat = icon->seat;
367 struct wlr_cursor *cursor = seat->cursor->cursor; 364 struct wlr_cursor *cursor = seat->cursor->cursor;
365
368 switch (wlr_icon->drag->grab_type) { 366 switch (wlr_icon->drag->grab_type) {
369 case WLR_DRAG_GRAB_KEYBOARD: 367 case WLR_DRAG_GRAB_KEYBOARD:
370 return; 368 return;
371 case WLR_DRAG_GRAB_KEYBOARD_POINTER: 369 case WLR_DRAG_GRAB_KEYBOARD_POINTER:
372 icon->x = cursor->x; 370 wlr_scene_node_set_position(node, cursor->x, cursor->y);
373 icon->y = cursor->y;
374 break; 371 break;
375 case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; 372 case WLR_DRAG_GRAB_KEYBOARD_TOUCH:;
376 struct wlr_touch_point *point = 373 struct wlr_touch_point *point =
@@ -378,39 +375,15 @@ void drag_icon_update_position(struct sway_drag_icon *icon) {
378 if (point == NULL) { 375 if (point == NULL) {
379 return; 376 return;
380 } 377 }
381 icon->x = seat->touch_x; 378 wlr_scene_node_set_position(node, seat->touch_x, seat->touch_y);
382 icon->y = seat->touch_y;
383 } 379 }
384
385 drag_icon_damage_whole(icon);
386} 380}
387 381
388static void drag_icon_handle_surface_commit(struct wl_listener *listener, 382void drag_icons_update_position(struct sway_seat *seat) {
389 void *data) { 383 struct wlr_scene_node *node;
390 struct sway_drag_icon *icon = 384 wl_list_for_each(node, &seat->drag_icons->children, link) {
391 wl_container_of(listener, icon, surface_commit); 385 drag_icon_update_position(seat, node);
392 drag_icon_update_position(icon); 386 }
393}
394
395static void drag_icon_handle_map(struct wl_listener *listener, void *data) {
396 struct sway_drag_icon *icon = wl_container_of(listener, icon, map);
397 drag_icon_damage_whole(icon);
398}
399
400static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) {
401 struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap);
402 drag_icon_damage_whole(icon);
403}
404
405static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) {
406 struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy);
407 icon->wlr_drag_icon->data = NULL;
408 wl_list_remove(&icon->link);
409 wl_list_remove(&icon->surface_commit.link);
410 wl_list_remove(&icon->unmap.link);
411 wl_list_remove(&icon->map.link);
412 wl_list_remove(&icon->destroy.link);
413 free(icon);
414} 387}
415 388
416static void drag_handle_destroy(struct wl_listener *listener, void *data) { 389static void drag_handle_destroy(struct wl_listener *listener, void *data) {
@@ -482,27 +455,20 @@ static void handle_start_drag(struct wl_listener *listener, void *data) {
482 455
483 struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; 456 struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon;
484 if (wlr_drag_icon != NULL) { 457 if (wlr_drag_icon != NULL) {
485 struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); 458 struct wlr_scene_tree *tree = wlr_scene_drag_icon_create(seat->drag_icons, wlr_drag_icon);
486 if (icon == NULL) { 459 if (!tree) {
487 sway_log(SWAY_ERROR, "Allocation failed"); 460 sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree");
488 return; 461 return;
489 } 462 }
490 icon->seat = seat;
491 icon->wlr_drag_icon = wlr_drag_icon;
492 wlr_drag_icon->data = icon;
493 463
494 icon->surface_commit.notify = drag_icon_handle_surface_commit; 464 if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON,
495 wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); 465 wlr_drag_icon)) {
496 icon->unmap.notify = drag_icon_handle_unmap; 466 sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor");
497 wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); 467 wlr_scene_node_destroy(&tree->node);
498 icon->map.notify = drag_icon_handle_map; 468 return;
499 wl_signal_add(&wlr_drag_icon->events.map, &icon->map); 469 }
500 icon->destroy.notify = drag_icon_handle_destroy;
501 wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy);
502
503 wl_list_insert(&root->drag_icons, &icon->link);
504 470
505 drag_icon_update_position(icon); 471 drag_icon_update_position(seat, &tree->node);
506 } 472 }
507 seatop_begin_default(seat); 473 seatop_begin_default(seat);
508} 474}
@@ -549,8 +515,18 @@ struct sway_seat *seat_create(const char *seat_name) {
549 return NULL; 515 return NULL;
550 } 516 }
551 517
518 bool failed = false;
519 seat->scene_tree = alloc_scene_tree(root->layers.seat, &failed);
520 seat->drag_icons = alloc_scene_tree(seat->scene_tree, &failed);
521 if (failed) {
522 wlr_scene_node_destroy(&seat->scene_tree->node);
523 free(seat);
524 return NULL;
525 }
526
552 seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); 527 seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name);
553 if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { 528 if (!sway_assert(seat->wlr_seat, "could not allocate seat")) {
529 wlr_scene_node_destroy(&seat->scene_tree->node);
554 free(seat); 530 free(seat);
555 return NULL; 531 return NULL;
556 } 532 }
@@ -558,11 +534,15 @@ struct sway_seat *seat_create(const char *seat_name) {
558 534
559 seat->cursor = sway_cursor_create(seat); 535 seat->cursor = sway_cursor_create(seat);
560 if (!seat->cursor) { 536 if (!seat->cursor) {
537 wlr_scene_node_destroy(&seat->scene_tree->node);
561 wlr_seat_destroy(seat->wlr_seat); 538 wlr_seat_destroy(seat->wlr_seat);
562 free(seat); 539 free(seat);
563 return NULL; 540 return NULL;
564 } 541 }
565 542
543 seat->destroy.notify = handle_seat_destroy;
544 wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy);
545
566 seat->idle_inhibit_sources = seat->idle_wake_sources = 546 seat->idle_inhibit_sources = seat->idle_wake_sources =
567 IDLE_SOURCE_KEYBOARD | 547 IDLE_SOURCE_KEYBOARD |
568 IDLE_SOURCE_POINTER | 548 IDLE_SOURCE_POINTER |
@@ -636,7 +616,7 @@ static void seat_update_capabilities(struct sway_seat *seat) {
636 case WLR_INPUT_DEVICE_TOUCH: 616 case WLR_INPUT_DEVICE_TOUCH:
637 caps |= WL_SEAT_CAPABILITY_TOUCH; 617 caps |= WL_SEAT_CAPABILITY_TOUCH;
638 break; 618 break;
639 case WLR_INPUT_DEVICE_TABLET_TOOL: 619 case WLR_INPUT_DEVICE_TABLET:
640 caps |= WL_SEAT_CAPABILITY_POINTER; 620 caps |= WL_SEAT_CAPABILITY_POINTER;
641 break; 621 break;
642 case WLR_INPUT_DEVICE_SWITCH: 622 case WLR_INPUT_DEVICE_SWITCH:
@@ -654,7 +634,7 @@ static void seat_update_capabilities(struct sway_seat *seat) {
654 } else { 634 } else {
655 wlr_seat_set_capabilities(seat->wlr_seat, caps); 635 wlr_seat_set_capabilities(seat->wlr_seat, caps);
656 if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { 636 if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
657 cursor_set_image(seat->cursor, "left_ptr", NULL); 637 cursor_set_image(seat->cursor, "default", NULL);
658 } 638 }
659 } 639 }
660} 640}
@@ -694,19 +674,28 @@ static const char *get_builtin_output_name(void) {
694static 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) {
695 switch (seat_device->input_device->wlr_device->type) { 675 switch (seat_device->input_device->wlr_device->type) {
696 case WLR_INPUT_DEVICE_TOUCH: 676 case WLR_INPUT_DEVICE_TOUCH:
697 case WLR_INPUT_DEVICE_TABLET_TOOL: 677 case WLR_INPUT_DEVICE_TABLET:
698 return true; 678 return true;
699 default: 679 default:
700 return false; 680 return false;
701 } 681 }
702} 682}
703 683
704static void seat_apply_input_config(struct sway_seat *seat, 684static void seat_apply_input_mapping(struct sway_seat *seat,
705 struct sway_seat_device *sway_device) { 685 struct sway_seat_device *sway_device) {
706 struct input_config *ic = 686 struct input_config *ic =
707 input_device_get_config(sway_device->input_device); 687 input_device_get_config(sway_device->input_device);
708 688
709 sway_log(SWAY_DEBUG, "Applying input config to %s", 689 switch (sway_device->input_device->wlr_device->type) {
690 case WLR_INPUT_DEVICE_POINTER:
691 case WLR_INPUT_DEVICE_TOUCH:
692 case WLR_INPUT_DEVICE_TABLET:
693 break;
694 default:
695 return; // these devices don't support mappings
696 }
697
698 sway_log(SWAY_DEBUG, "Applying input mapping to %s",
710 sway_device->input_device->identifier); 699 sway_device->input_device->identifier);
711 700
712 const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output; 701 const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output;
@@ -715,14 +704,26 @@ static void seat_apply_input_config(struct sway_seat *seat,
715 ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; 704 ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to;
716 705
717 switch (mapped_to) { 706 switch (mapped_to) {
718 case MAPPED_TO_DEFAULT: 707 case MAPPED_TO_DEFAULT:;
719 /* 708 /*
720 * If the wlroots backend provides an output name, use that. 709 * If the wlroots backend provides an output name, use that.
721 * 710 *
722 * Otherwise, try to map built-in touch and tablet tool devices to the 711 * Otherwise, try to map built-in touch and pointer devices to the
723 * built-in output. 712 * built-in output.
724 */ 713 */
725 mapped_to_output = sway_device->input_device->wlr_device->output_name; 714 struct wlr_input_device *dev = sway_device->input_device->wlr_device;
715 switch (dev->type) {
716 case WLR_INPUT_DEVICE_POINTER:
717 mapped_to_output = wlr_pointer_from_input_device(dev)->output_name;
718 break;
719 case WLR_INPUT_DEVICE_TOUCH:
720 mapped_to_output = wlr_touch_from_input_device(dev)->output_name;
721 break;
722 default:
723 mapped_to_output = NULL;
724 break;
725 }
726#if WLR_HAS_LIBINPUT_BACKEND
726 if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && 727 if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) &&
727 sway_libinput_device_is_builtin(sway_device->input_device)) { 728 sway_libinput_device_is_builtin(sway_device->input_device)) {
728 mapped_to_output = get_builtin_output_name(); 729 mapped_to_output = get_builtin_output_name();
@@ -731,6 +732,10 @@ static void seat_apply_input_config(struct sway_seat *seat,
731 mapped_to_output, sway_device->input_device->identifier); 732 mapped_to_output, sway_device->input_device->identifier);
732 } 733 }
733 } 734 }
735#else
736 (void)is_touch_or_tablet_tool;
737 (void)get_builtin_output_name;
738#endif
734 if (mapped_to_output == NULL) { 739 if (mapped_to_output == NULL) {
735 return; 740 return;
736 } 741 }
@@ -774,12 +779,9 @@ static void seat_apply_input_config(struct sway_seat *seat,
774 779
775static void seat_configure_pointer(struct sway_seat *seat, 780static void seat_configure_pointer(struct sway_seat *seat,
776 struct sway_seat_device *sway_device) { 781 struct sway_seat_device *sway_device) {
777 if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { 782 seat_configure_xcursor(seat);
778 seat_configure_xcursor(seat);
779 }
780 wlr_cursor_attach_input_device(seat->cursor->cursor, 783 wlr_cursor_attach_input_device(seat->cursor->cursor,
781 sway_device->input_device->wlr_device); 784 sway_device->input_device->wlr_device);
782 seat_apply_input_config(seat, sway_device);
783 wl_event_source_timer_update( 785 wl_event_source_timer_update(
784 seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); 786 seat->cursor->hide_source, cursor_get_timeout(seat->cursor));
785} 787}
@@ -790,13 +792,22 @@ static void seat_configure_keyboard(struct sway_seat *seat,
790 sway_keyboard_create(seat, seat_device); 792 sway_keyboard_create(seat, seat_device);
791 } 793 }
792 sway_keyboard_configure(seat_device->keyboard); 794 sway_keyboard_configure(seat_device->keyboard);
793 wlr_seat_set_keyboard(seat->wlr_seat, 795
794 seat_device->input_device->wlr_device); 796 // We only need to update the current keyboard, as the rest will be updated
795 struct sway_node *focus = seat_get_focus(seat); 797 // as they are activated.
796 if (focus && node_is_view(focus)) { 798 struct wlr_keyboard *wlr_keyboard =
797 // force notify reenter to pick up the new configuration 799 wlr_keyboard_from_input_device(seat_device->input_device->wlr_device);
800 struct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard;
801 if (wlr_keyboard != current_keyboard) {
802 return;
803 }
804
805 // force notify reenter to pick up the new configuration. This reuses
806 // the current focused surface to avoid breaking input grabs.
807 struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;
808 if (surface) {
798 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); 809 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat);
799 seat_keyboard_notify_enter(seat, focus->sway_container->view->surface); 810 seat_keyboard_notify_enter(seat, surface);
800 } 811 }
801} 812}
802 813
@@ -805,7 +816,6 @@ static void seat_configure_switch(struct sway_seat *seat,
805 if (!seat_device->switch_device) { 816 if (!seat_device->switch_device) {
806 sway_switch_create(seat, seat_device); 817 sway_switch_create(seat, seat_device);
807 } 818 }
808 seat_apply_input_config(seat, seat_device);
809 sway_switch_configure(seat_device->switch_device); 819 sway_switch_configure(seat_device->switch_device);
810} 820}
811 821
@@ -813,7 +823,6 @@ static void seat_configure_touch(struct sway_seat *seat,
813 struct sway_seat_device *sway_device) { 823 struct sway_seat_device *sway_device) {
814 wlr_cursor_attach_input_device(seat->cursor->cursor, 824 wlr_cursor_attach_input_device(seat->cursor->cursor,
815 sway_device->input_device->wlr_device); 825 sway_device->input_device->wlr_device);
816 seat_apply_input_config(seat, sway_device);
817} 826}
818 827
819static void seat_configure_tablet_tool(struct sway_seat *seat, 828static void seat_configure_tablet_tool(struct sway_seat *seat,
@@ -824,7 +833,6 @@ static void seat_configure_tablet_tool(struct sway_seat *seat,
824 sway_configure_tablet(sway_device->tablet); 833 sway_configure_tablet(sway_device->tablet);
825 wlr_cursor_attach_input_device(seat->cursor->cursor, 834 wlr_cursor_attach_input_device(seat->cursor->cursor,
826 sway_device->input_device->wlr_device); 835 sway_device->input_device->wlr_device);
827 seat_apply_input_config(seat, sway_device);
828} 836}
829 837
830static void seat_configure_tablet_pad(struct sway_seat *seat, 838static void seat_configure_tablet_pad(struct sway_seat *seat,
@@ -874,13 +882,25 @@ void seat_configure_device(struct sway_seat *seat,
874 case WLR_INPUT_DEVICE_TOUCH: 882 case WLR_INPUT_DEVICE_TOUCH:
875 seat_configure_touch(seat, seat_device); 883 seat_configure_touch(seat, seat_device);
876 break; 884 break;
877 case WLR_INPUT_DEVICE_TABLET_TOOL: 885 case WLR_INPUT_DEVICE_TABLET:
878 seat_configure_tablet_tool(seat, seat_device); 886 seat_configure_tablet_tool(seat, seat_device);
879 break; 887 break;
880 case WLR_INPUT_DEVICE_TABLET_PAD: 888 case WLR_INPUT_DEVICE_TABLET_PAD:
881 seat_configure_tablet_pad(seat, seat_device); 889 seat_configure_tablet_pad(seat, seat_device);
882 break; 890 break;
883 } 891 }
892
893 seat_apply_input_mapping(seat, seat_device);
894}
895
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);
884} 904}
885 905
886void seat_reset_device(struct sway_seat *seat, 906void seat_reset_device(struct sway_seat *seat,
@@ -901,7 +921,7 @@ void seat_reset_device(struct sway_seat *seat,
901 case WLR_INPUT_DEVICE_TOUCH: 921 case WLR_INPUT_DEVICE_TOUCH:
902 seat_reset_input_config(seat, seat_device); 922 seat_reset_input_config(seat, seat_device);
903 break; 923 break;
904 case WLR_INPUT_DEVICE_TABLET_TOOL: 924 case WLR_INPUT_DEVICE_TABLET:
905 seat_reset_input_config(seat, seat_device); 925 seat_reset_input_config(seat, seat_device);
906 break; 926 break;
907 case WLR_INPUT_DEVICE_TABLET_PAD: 927 case WLR_INPUT_DEVICE_TABLET_PAD:
@@ -997,7 +1017,7 @@ void seat_configure_xcursor(struct sway_seat *seat) {
997 1017
998 wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); 1018 wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1);
999 struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( 1019 struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
1000 server.xwayland.xcursor_manager, "left_ptr", 1); 1020 server.xwayland.xcursor_manager, "default", 1);
1001 if (xcursor != NULL) { 1021 if (xcursor != NULL) {
1002 struct wlr_xcursor_image *image = xcursor->images[0]; 1022 struct wlr_xcursor_image *image = xcursor->images[0];
1003 wlr_xwayland_set_cursor( 1023 wlr_xwayland_set_cursor(
@@ -1023,32 +1043,35 @@ void seat_configure_xcursor(struct sway_seat *seat) {
1023 sway_log(SWAY_ERROR, 1043 sway_log(SWAY_ERROR,
1024 "Cannot create XCursor manager for theme '%s'", cursor_theme); 1044 "Cannot create XCursor manager for theme '%s'", cursor_theme);
1025 } 1045 }
1026 }
1027 1046
1028 for (int i = 0; i < root->outputs->length; ++i) { 1047
1029 struct sway_output *sway_output = root->outputs->items[i]; 1048 for (int i = 0; i < root->outputs->length; ++i) {
1030 struct wlr_output *output = sway_output->wlr_output; 1049 struct sway_output *sway_output = root->outputs->items[i];
1031 bool result = 1050 struct wlr_output *output = sway_output->wlr_output;
1032 wlr_xcursor_manager_load(seat->cursor->xcursor_manager, 1051 bool result =
1033 output->scale); 1052 wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
1034 if (!result) { 1053 output->scale);
1035 sway_log(SWAY_ERROR, 1054 if (!result) {
1036 "Cannot load xcursor theme for output '%s' with scale %f", 1055 sway_log(SWAY_ERROR,
1037 output->name, output->scale); 1056 "Cannot load xcursor theme for output '%s' with scale %f",
1057 output->name, output->scale);
1058 }
1038 } 1059 }
1039 }
1040 1060
1041 // Reset the cursor so that we apply it to outputs that just appeared 1061 // Reset the cursor so that we apply it to outputs that just appeared
1042 cursor_set_image(seat->cursor, NULL, NULL); 1062 cursor_set_image(seat->cursor, NULL, NULL);
1043 cursor_set_image(seat->cursor, "left_ptr", NULL); 1063 cursor_set_image(seat->cursor, "default", NULL);
1044 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, 1064 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
1045 seat->cursor->cursor->y); 1065 seat->cursor->cursor->y);
1066 }
1046} 1067}
1047 1068
1048bool seat_is_input_allowed(struct sway_seat *seat, 1069bool seat_is_input_allowed(struct sway_seat *seat,
1049 struct wlr_surface *surface) { 1070 struct wlr_surface *surface) {
1050 struct wl_client *client = wl_resource_get_client(surface->resource); 1071 if (server.session_lock.lock) {
1051 return !seat->exclusive_client || seat->exclusive_client == client; 1072 return sway_session_lock_has_surface(server.session_lock.lock, surface);
1073 }
1074 return true;
1052} 1075}
1053 1076
1054static void send_unfocus(struct sway_container *con, void *data) { 1077static void send_unfocus(struct sway_container *con, void *data) {
@@ -1107,15 +1130,7 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) {
1107 } 1130 }
1108} 1131}
1109 1132
1110void 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) {
1111 if (seat->focused_layer) {
1112 struct wlr_layer_surface_v1 *layer = seat->focused_layer;
1113 seat_set_focus_layer(seat, NULL);
1114 seat_set_focus(seat, node);
1115 seat_set_focus_layer(seat, layer);
1116 return;
1117 }
1118
1119 struct sway_node *last_focus = seat_get_focus(seat); 1134 struct sway_node *last_focus = seat_get_focus(seat);
1120 if (last_focus == node) { 1135 if (last_focus == node) {
1121 return; 1136 return;
@@ -1248,6 +1263,24 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
1248 } 1263 }
1249} 1264}
1250 1265
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
1251void seat_set_focus_container(struct sway_seat *seat, 1284void seat_set_focus_container(struct sway_seat *seat,
1252 struct sway_container *con) { 1285 struct sway_container *con) {
1253 seat_set_focus(seat, con ? &con->node : NULL); 1286 seat_set_focus(seat, con ? &con->node : NULL);
@@ -1273,7 +1306,7 @@ void seat_set_focus_surface(struct sway_seat *seat,
1273 } 1306 }
1274 1307
1275 sway_input_method_relay_set_focus(&seat->im_relay, surface); 1308 sway_input_method_relay_set_focus(&seat->im_relay, surface);
1276 seat_tablet_pads_notify_enter(seat, surface); 1309 seat_tablet_pads_set_focus(seat, surface);
1277} 1310}
1278 1311
1279void seat_set_focus_layer(struct sway_seat *seat, 1312void seat_set_focus_layer(struct sway_seat *seat,
@@ -1287,28 +1320,23 @@ void seat_set_focus_layer(struct sway_seat *seat,
1287 seat_set_focus(seat, previous); 1320 seat_set_focus(seat, previous);
1288 } 1321 }
1289 return; 1322 return;
1290 } else if (!layer || seat->focused_layer == layer) { 1323 } else if (!layer) {
1291 return; 1324 return;
1292 } 1325 }
1293 assert(layer->mapped); 1326 assert(layer->surface->mapped);
1294 seat_set_focus_surface(seat, layer->surface, true); 1327 if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP &&
1295 if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { 1328 layer->current.keyboard_interactive
1296 seat->focused_layer = layer; 1329 == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
1330 seat->has_exclusive_layer = true;
1297 } 1331 }
1298} 1332 if (seat->focused_layer == layer) {
1299
1300void seat_set_exclusive_client(struct sway_seat *seat,
1301 struct wl_client *client) {
1302 if (!client) {
1303 seat->exclusive_client = client;
1304 // Triggers a refocus of the topmost surface layer if necessary
1305 // TODO: Make layer surface focus per-output based on cursor position
1306 for (int i = 0; i < root->outputs->length; ++i) {
1307 struct sway_output *output = root->outputs->items[i];
1308 arrange_layers(output);
1309 }
1310 return; 1333 return;
1311 } 1334 }
1335 seat_set_focus_surface(seat, layer->surface, true);
1336 seat->focused_layer = layer;
1337}
1338
1339void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client) {
1312 if (seat->focused_layer) { 1340 if (seat->focused_layer) {
1313 if (wl_resource_get_client(seat->focused_layer->resource) != client) { 1341 if (wl_resource_get_client(seat->focused_layer->resource) != client) {
1314 seat_set_focus_layer(seat, NULL); 1342 seat_set_focus_layer(seat, NULL);
@@ -1335,7 +1363,6 @@ void seat_set_exclusive_client(struct sway_seat *seat,
1335 now.tv_nsec / 1000, point->touch_id); 1363 now.tv_nsec / 1000, point->touch_id);
1336 } 1364 }
1337 } 1365 }
1338 seat->exclusive_client = client;
1339} 1366}
1340 1367
1341struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, 1368struct sway_node *seat_get_focus_inactive(struct sway_seat *seat,
@@ -1416,9 +1443,8 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) {
1416 if (!seat->has_focus) { 1443 if (!seat->has_focus) {
1417 return NULL; 1444 return NULL;
1418 } 1445 }
1419 if (wl_list_empty(&seat->focus_stack)) { 1446 sway_assert(!wl_list_empty(&seat->focus_stack),
1420 return NULL; 1447 "focus_stack is empty, but has_focus is true");
1421 }
1422 struct sway_seat_node *current = 1448 struct sway_seat_node *current =
1423 wl_container_of(seat->focus_stack.next, current, link); 1449 wl_container_of(seat->focus_stack.next, current, link);
1424 return current->node; 1450 return current->node;
@@ -1503,7 +1529,7 @@ struct seat_config *seat_get_config_by_name(const char *name) {
1503} 1529}
1504 1530
1505void 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,
1506 uint32_t button, enum wlr_button_state state) { 1532 uint32_t button, enum wl_pointer_button_state state) {
1507 seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, 1533 seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat,
1508 time_msec, button, state); 1534 time_msec, button, state);
1509} 1535}
@@ -1540,7 +1566,7 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) {
1540 1566
1541void seatop_button(struct sway_seat *seat, uint32_t time_msec, 1567void seatop_button(struct sway_seat *seat, uint32_t time_msec,
1542 struct wlr_input_device *device, uint32_t button, 1568 struct wlr_input_device *device, uint32_t button,
1543 enum wlr_button_state state) { 1569 enum wl_pointer_button_state state) {
1544 if (seat->seatop_impl->button) { 1570 if (seat->seatop_impl->button) {
1545 seat->seatop_impl->button(seat, time_msec, device, button, state); 1571 seat->seatop_impl->button(seat, time_msec, device, button, state);
1546 } 1572 }
@@ -1553,12 +1579,38 @@ void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
1553} 1579}
1554 1580
1555void seatop_pointer_axis(struct sway_seat *seat, 1581void seatop_pointer_axis(struct sway_seat *seat,
1556 struct wlr_event_pointer_axis *event) { 1582 struct wlr_pointer_axis_event *event) {
1557 if (seat->seatop_impl->pointer_axis) { 1583 if (seat->seatop_impl->pointer_axis) {
1558 seat->seatop_impl->pointer_axis(seat, event); 1584 seat->seatop_impl->pointer_axis(seat, event);
1559 } 1585 }
1560} 1586}
1561 1587
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
1562void seatop_tablet_tool_tip(struct sway_seat *seat, 1614void seatop_tablet_tool_tip(struct sway_seat *seat,
1563 struct sway_tablet_tool *tool, uint32_t time_msec, 1615 struct sway_tablet_tool *tool, uint32_t time_msec,
1564 enum wlr_tablet_tool_tip_state state) { 1616 enum wlr_tablet_tool_tip_state state) {
@@ -1576,6 +1628,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat,
1576 } 1628 }
1577} 1629}
1578 1630
1631void seatop_hold_begin(struct sway_seat *seat,
1632 struct wlr_pointer_hold_begin_event *event) {
1633 if (seat->seatop_impl->hold_begin) {
1634 seat->seatop_impl->hold_begin(seat, event);
1635 }
1636}
1637
1638void seatop_hold_end(struct sway_seat *seat,
1639 struct wlr_pointer_hold_end_event *event) {
1640 if (seat->seatop_impl->hold_end) {
1641 seat->seatop_impl->hold_end(seat, event);
1642 }
1643}
1644
1645void seatop_pinch_begin(struct sway_seat *seat,
1646 struct wlr_pointer_pinch_begin_event *event) {
1647 if (seat->seatop_impl->pinch_begin) {
1648 seat->seatop_impl->pinch_begin(seat, event);
1649 }
1650}
1651
1652void seatop_pinch_update(struct sway_seat *seat,
1653 struct wlr_pointer_pinch_update_event *event) {
1654 if (seat->seatop_impl->pinch_update) {
1655 seat->seatop_impl->pinch_update(seat, event);
1656 }
1657}
1658
1659void seatop_pinch_end(struct sway_seat *seat,
1660 struct wlr_pointer_pinch_end_event *event) {
1661 if (seat->seatop_impl->pinch_end) {
1662 seat->seatop_impl->pinch_end(seat, event);
1663 }
1664}
1665
1666void seatop_swipe_begin(struct sway_seat *seat,
1667 struct wlr_pointer_swipe_begin_event *event) {
1668 if (seat->seatop_impl->swipe_begin) {
1669 seat->seatop_impl->swipe_begin(seat, event);
1670 }
1671}
1672
1673void seatop_swipe_update(struct sway_seat *seat,
1674 struct wlr_pointer_swipe_update_event *event) {
1675 if (seat->seatop_impl->swipe_update) {
1676 seat->seatop_impl->swipe_update(seat, event);
1677 }
1678}
1679
1680void seatop_swipe_end(struct sway_seat *seat,
1681 struct wlr_pointer_swipe_end_event *event) {
1682 if (seat->seatop_impl->swipe_end) {
1683 seat->seatop_impl->swipe_end(seat, event);
1684 }
1685}
1686
1579void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { 1687void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) {
1580 if (seat->seatop_impl->rebase) { 1688 if (seat->seatop_impl->rebase) {
1581 seat->seatop_impl->rebase(seat, time_msec); 1689 seat->seatop_impl->rebase(seat, time_msec);
@@ -1591,13 +1699,6 @@ void seatop_end(struct sway_seat *seat) {
1591 seat->seatop_impl = NULL; 1699 seat->seatop_impl = NULL;
1592} 1700}
1593 1701
1594void seatop_render(struct sway_seat *seat, struct sway_output *output,
1595 pixman_region32_t *damage) {
1596 if (seat->seatop_impl->render) {
1597 seat->seatop_impl->render(seat, output, damage);
1598 }
1599}
1600
1601bool seatop_allows_set_cursor(struct sway_seat *seat) { 1702bool seatop_allows_set_cursor(struct sway_seat *seat) {
1602 return seat->seatop_impl->allow_set_cursor; 1703 return seat->seatop_impl->allow_set_cursor;
1603} 1704}
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c
index f9eb8c8a..0c6f7c5e 100644
--- a/sway/input/seatop_default.c
+++ b/sway/input/seatop_default.c
@@ -1,14 +1,17 @@
1#define _POSIX_C_SOURCE 200809L
2#include <float.h> 1#include <float.h>
3#include <libevdev/libevdev.h> 2#include <libevdev/libevdev.h>
4#include <wlr/types/wlr_cursor.h> 3#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_subcompositor.h>
5#include <wlr/types/wlr_tablet_v2.h> 5#include <wlr/types/wlr_tablet_v2.h>
6#include <wlr/types/wlr_xcursor_manager.h> 6#include <wlr/types/wlr_xcursor_manager.h>
7#include "gesture.h"
7#include "sway/desktop/transaction.h" 8#include "sway/desktop/transaction.h"
8#include "sway/input/cursor.h" 9#include "sway/input/cursor.h"
9#include "sway/input/seat.h" 10#include "sway/input/seat.h"
10#include "sway/input/tablet.h" 11#include "sway/input/tablet.h"
12#include "sway/layers.h"
11#include "sway/output.h" 13#include "sway/output.h"
14#include "sway/scene_descriptor.h"
12#include "sway/tree/view.h" 15#include "sway/tree/view.h"
13#include "sway/tree/workspace.h" 16#include "sway/tree/workspace.h"
14#include "log.h" 17#include "log.h"
@@ -20,6 +23,7 @@ struct seatop_default_event {
20 struct sway_node *previous_node; 23 struct sway_node *previous_node;
21 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; 24 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
22 size_t pressed_button_count; 25 size_t pressed_button_count;
26 struct gesture_tracker gestures;
23}; 27};
24 28
25/*-----------------------------------------\ 29/*-----------------------------------------\
@@ -51,6 +55,9 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
51 while (cont) { 55 while (cont) {
52 if (container_parent_layout(cont) == layout) { 56 if (container_parent_layout(cont) == layout) {
53 list_t *siblings = container_get_siblings(cont); 57 list_t *siblings = container_get_siblings(cont);
58 if (!siblings) {
59 return false;
60 }
54 int index = list_find(siblings, cont); 61 int index = list_find(siblings, cont);
55 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { 62 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) {
56 return false; 63 return false;
@@ -226,14 +233,15 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
226 struct sway_container *cont = node && node->type == N_CONTAINER ? 233 struct sway_container *cont = node && node->type == N_CONTAINER ?
227 node->sway_container : NULL; 234 node->sway_container : NULL;
228 235
229 if (wlr_surface_is_layer_surface(surface)) { 236 struct wlr_layer_surface_v1 *layer;
237#if HAVE_XWAYLAND
238 struct wlr_xwayland_surface *xsurface;
239#endif
240 if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) &&
241 layer->current.keyboard_interactive) {
230 // Handle tapping a layer surface 242 // Handle tapping a layer surface
231 struct wlr_layer_surface_v1 *layer = 243 seat_set_focus_layer(seat, layer);
232 wlr_layer_surface_v1_from_wlr_surface(surface); 244 transaction_commit_dirty();
233 if (layer->current.keyboard_interactive) {
234 seat_set_focus_layer(seat, layer);
235 transaction_commit_dirty();
236 }
237 } else if (cont) { 245 } else if (cont) {
238 bool is_floating_or_child = container_is_floating_or_child(cont); 246 bool is_floating_or_child = container_is_floating_or_child(cont);
239 bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); 247 bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont);
@@ -258,20 +266,17 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
258 266
259 // Handle tapping on a container surface 267 // Handle tapping on a container surface
260 seat_set_focus_container(seat, cont); 268 seat_set_focus_container(seat, cont);
261 seatop_begin_down(seat, node->sway_container, time_msec, sx, sy); 269 seatop_begin_down(seat, node->sway_container, sx, sy);
262 } 270 }
263#if HAVE_XWAYLAND 271#if HAVE_XWAYLAND
264 // Handle tapping on an xwayland unmanaged view 272 // Handle tapping on an xwayland unmanaged view
265 else if (wlr_surface_is_xwayland_surface(surface)) { 273 else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&
266 struct wlr_xwayland_surface *xsurface = 274 xsurface->override_redirect &&
267 wlr_xwayland_surface_from_wlr_surface(surface); 275 wlr_xwayland_or_surface_wants_focus(xsurface)) {
268 if (xsurface->override_redirect && 276 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
269 wlr_xwayland_or_surface_wants_focus(xsurface)) { 277 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
270 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 278 seat_set_focus_surface(seat, xsurface->surface, false);
271 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 279 transaction_commit_dirty();
272 seat_set_focus_surface(seat, xsurface->surface, false);
273 transaction_commit_dirty();
274 }
275 } 280 }
276#endif 281#endif
277 282
@@ -285,7 +290,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
285 290
286static bool trigger_pointer_button_binding(struct sway_seat *seat, 291static bool trigger_pointer_button_binding(struct sway_seat *seat,
287 struct wlr_input_device *device, uint32_t button, 292 struct wlr_input_device *device, uint32_t button,
288 enum wlr_button_state state, uint32_t modifiers, 293 enum wl_pointer_button_state state, uint32_t modifiers,
289 bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { 294 bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) {
290 // We can reach this for non-pointer devices if we're currently emulating 295 // We can reach this for non-pointer devices if we're currently emulating
291 // pointer input for one. Emulated input should not trigger bindings. The 296 // pointer input for one. Emulated input should not trigger bindings. The
@@ -299,7 +304,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
299 char *device_identifier = device ? input_device_get_identifier(device) 304 char *device_identifier = device ? input_device_get_identifier(device)
300 : strdup("*"); 305 : strdup("*");
301 struct sway_binding *binding = NULL; 306 struct sway_binding *binding = NULL;
302 if (state == WLR_BUTTON_PRESSED) { 307 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
303 state_add_button(e, button); 308 state_add_button(e, button);
304 binding = get_active_mouse_binding(e, 309 binding = get_active_mouse_binding(e,
305 config->current_mode->mouse_bindings, modifiers, false, 310 config->current_mode->mouse_bindings, modifiers, false,
@@ -324,7 +329,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
324 329
325static void handle_button(struct sway_seat *seat, uint32_t time_msec, 330static void handle_button(struct sway_seat *seat, uint32_t time_msec,
326 struct wlr_input_device *device, uint32_t button, 331 struct wlr_input_device *device, uint32_t button,
327 enum wlr_button_state state) { 332 enum wl_pointer_button_state state) {
328 struct sway_cursor *cursor = seat->cursor; 333 struct sway_cursor *cursor = seat->cursor;
329 334
330 // Determine what's under the cursor 335 // Determine what's under the cursor
@@ -357,7 +362,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
357 362
358 // Handle clicking an empty workspace 363 // Handle clicking an empty workspace
359 if (node && node->type == N_WORKSPACE) { 364 if (node && node->type == N_WORKSPACE) {
360 if (state == WLR_BUTTON_PRESSED) { 365 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
361 seat_set_focus(seat, node); 366 seat_set_focus(seat, node);
362 transaction_commit_dirty(); 367 transaction_commit_dirty();
363 } 368 }
@@ -365,21 +370,23 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
365 return; 370 return;
366 } 371 }
367 372
368 // Handle clicking a layer surface 373 // Handle clicking a layer surface and its popups/subsurfaces
369 if (surface && wlr_surface_is_layer_surface(surface)) { 374 struct wlr_layer_surface_v1 *layer = NULL;
370 struct wlr_layer_surface_v1 *layer = 375 if ((layer = toplevel_layer_surface_from_surface(surface))) {
371 wlr_layer_surface_v1_from_wlr_surface(surface);
372 if (layer->current.keyboard_interactive) { 376 if (layer->current.keyboard_interactive) {
373 seat_set_focus_layer(seat, layer); 377 seat_set_focus_layer(seat, layer);
374 transaction_commit_dirty(); 378 transaction_commit_dirty();
375 } 379 }
380 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
381 seatop_begin_down_on_surface(seat, surface, sx, sy);
382 }
376 seat_pointer_notify_button(seat, time_msec, button, state); 383 seat_pointer_notify_button(seat, time_msec, button, state);
377 return; 384 return;
378 } 385 }
379 386
380 // Handle tiling resize via border 387 // Handle tiling resize via border
381 if (cont && resize_edge && button == BTN_LEFT && 388 if (cont && resize_edge && button == BTN_LEFT &&
382 state == WLR_BUTTON_PRESSED && !is_floating) { 389 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) {
383 // If a resize is triggered on a tabbed or stacked container, change 390 // If a resize is triggered on a tabbed or stacked container, change
384 // focus to the tab which already had inactive focus -- otherwise, we'd 391 // focus to the tab which already had inactive focus -- otherwise, we'd
385 // change the active tab when the user probably just wanted to resize. 392 // change the active tab when the user probably just wanted to resize.
@@ -397,7 +404,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
397 // Handle tiling resize via mod 404 // Handle tiling resize via mod
398 bool mod_pressed = modifiers & config->floating_mod; 405 bool mod_pressed = modifiers & config->floating_mod;
399 if (cont && !is_floating_or_child && mod_pressed && 406 if (cont && !is_floating_or_child && mod_pressed &&
400 state == WLR_BUTTON_PRESSED) { 407 state == WL_POINTER_BUTTON_STATE_PRESSED) {
401 uint32_t btn_resize = config->floating_mod_inverse ? 408 uint32_t btn_resize = config->floating_mod_inverse ?
402 BTN_LEFT : BTN_RIGHT; 409 BTN_LEFT : BTN_RIGHT;
403 if (button == btn_resize) { 410 if (button == btn_resize) {
@@ -424,13 +431,31 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
424 } 431 }
425 } 432 }
426 433
434 // Handle changing focus when clicking on a container
435 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
436 // Default case: focus the container that was just clicked.
437 node = &cont->node;
438
439 // If the container is a tab/stacked container and the click happened
440 // on a tab, switch to the tab. If the tab contents were already
441 // focused, focus the tab container itself. If the tab container was
442 // already focused, cycle back to focusing the tab contents.
443 if (on_titlebar) {
444 struct sway_container *focus = seat_get_focused_container(seat);
445 if (focus == cont || !container_has_ancestor(focus, cont)) {
446 node = seat_get_focus_inactive(seat, &cont->node);
447 }
448 }
449
450 seat_set_focus(seat, node);
451 transaction_commit_dirty();
452 }
453
427 // Handle beginning floating move 454 // Handle beginning floating move
428 if (cont && is_floating_or_child && !is_fullscreen_or_child && 455 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
429 state == WLR_BUTTON_PRESSED) { 456 state == WL_POINTER_BUTTON_STATE_PRESSED) {
430 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; 457 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
431 if (button == btn_move && (mod_pressed || on_titlebar)) { 458 if (button == btn_move && (mod_pressed || on_titlebar)) {
432 seat_set_focus_container(seat,
433 seat_get_focus_inactive_view(seat, &cont->node));
434 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); 459 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont));
435 return; 460 return;
436 } 461 }
@@ -438,9 +463,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
438 463
439 // Handle beginning floating resize 464 // Handle beginning floating resize
440 if (cont && is_floating_or_child && !is_fullscreen_or_child && 465 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
441 state == WLR_BUTTON_PRESSED) { 466 state == WL_POINTER_BUTTON_STATE_PRESSED) {
442 // Via border 467 // Via border
443 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { 468 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
469 seat_set_focus_container(seat, cont);
444 seatop_begin_resize_floating(seat, cont, resize_edge); 470 seatop_begin_resize_floating(seat, cont, resize_edge);
445 return; 471 return;
446 } 472 }
@@ -455,6 +481,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
455 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 481 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
456 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? 482 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ?
457 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 483 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
484 seat_set_focus_container(seat, floater);
458 seatop_begin_resize_floating(seat, floater, edge); 485 seatop_begin_resize_floating(seat, floater, edge);
459 return; 486 return;
460 } 487 }
@@ -462,55 +489,43 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
462 489
463 // Handle moving a tiling container 490 // Handle moving a tiling container
464 if (config->tiling_drag && (mod_pressed || on_titlebar) && 491 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
465 state == WLR_BUTTON_PRESSED && !is_floating_or_child && 492 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child &&
466 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { 493 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
467 struct sway_container *focus = seat_get_focused_container(seat); 494 // If moving a container by its title bar, use a threshold for the drag
468 bool focused = focus == cont || container_has_ancestor(focus, cont);
469 if (on_titlebar && !focused) {
470 node = seat_get_focus_inactive(seat, &cont->node);
471 seat_set_focus(seat, node);
472 }
473
474 // If moving a container by it's title bar, use a threshold for the drag
475 if (!mod_pressed && config->tiling_drag_threshold > 0) { 495 if (!mod_pressed && config->tiling_drag_threshold > 0) {
476 seatop_begin_move_tiling_threshold(seat, cont); 496 seatop_begin_move_tiling_threshold(seat, cont);
477 } else { 497 } else {
478 seatop_begin_move_tiling(seat, cont); 498 seatop_begin_move_tiling(seat, cont);
479 } 499 }
500
480 return; 501 return;
481 } 502 }
482 503
483 // Handle mousedown on a container surface 504 // Handle mousedown on a container surface
484 if (surface && cont && state == WLR_BUTTON_PRESSED) { 505 if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
485 seat_set_focus_container(seat, cont); 506 seatop_begin_down(seat, cont, sx, sy);
486 seatop_begin_down(seat, cont, time_msec, sx, sy); 507 seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED);
487 seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED);
488 return; 508 return;
489 } 509 }
490 510
491 // Handle clicking a container surface or decorations 511 // Handle clicking a container surface or decorations
492 if (cont && state == WLR_BUTTON_PRESSED) { 512 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
493 node = seat_get_focus_inactive(seat, &cont->node);
494 seat_set_focus(seat, node);
495 transaction_commit_dirty();
496 seat_pointer_notify_button(seat, time_msec, button, state); 513 seat_pointer_notify_button(seat, time_msec, button, state);
497 return; 514 return;
498 } 515 }
499 516
500#if HAVE_XWAYLAND 517#if HAVE_XWAYLAND
501 // Handle clicking on xwayland unmanaged view 518 // Handle clicking on xwayland unmanaged view
502 if (surface && wlr_surface_is_xwayland_surface(surface)) { 519 struct wlr_xwayland_surface *xsurface;
503 struct wlr_xwayland_surface *xsurface = 520 if (surface &&
504 wlr_xwayland_surface_from_wlr_surface(surface); 521 (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&
505 if (xsurface->override_redirect && 522 xsurface->override_redirect &&
506 wlr_xwayland_or_surface_wants_focus(xsurface)) { 523 wlr_xwayland_or_surface_wants_focus(xsurface)) {
507 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 524 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
508 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 525 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
509 seat_set_focus_surface(seat, xsurface->surface, false); 526 seat_set_focus_surface(seat, xsurface->surface, false);
510 transaction_commit_dirty(); 527 transaction_commit_dirty();
511 seat_pointer_notify_button(seat, time_msec, button, state); 528 seat_pointer_notify_button(seat, time_msec, button, state);
512 return;
513 }
514 } 529 }
515#endif 530#endif
516 531
@@ -533,6 +548,21 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
533 if (wlr_output == NULL) { 548 if (wlr_output == NULL) {
534 return; 549 return;
535 } 550 }
551
552 struct wlr_surface *surface = NULL;
553 double sx, sy;
554 node_at_coords(seat, seat->cursor->cursor->x, seat->cursor->cursor->y,
555 &surface, &sx, &sy);
556
557 // Focus topmost layer surface
558 struct wlr_layer_surface_v1 *layer = NULL;
559 if ((layer = toplevel_layer_surface_from_surface(surface)) &&
560 layer->current.keyboard_interactive) {
561 seat_set_focus_layer(seat, layer);
562 transaction_commit_dirty();
563 return;
564 }
565
536 struct sway_output *hovered_output = wlr_output->data; 566 struct sway_output *hovered_output = wlr_output->data;
537 if (focus && hovered_output != node_get_output(focus)) { 567 if (focus && hovered_output != node_get_output(focus)) {
538 struct sway_workspace *ws = output_get_active_workspace(hovered_output); 568 struct sway_workspace *ws = output_get_active_workspace(hovered_output);
@@ -593,12 +623,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
593 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 623 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
594 } 624 }
595 625
596 struct sway_drag_icon *drag_icon; 626 drag_icons_update_position(seat);
597 wl_list_for_each(drag_icon, &root->drag_icons, link) {
598 if (drag_icon->seat == seat) {
599 drag_icon_update_position(drag_icon);
600 }
601 }
602 627
603 e->previous_node = node; 628 e->previous_node = node;
604} 629}
@@ -628,25 +653,50 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
628 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); 653 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool);
629 } 654 }
630 655
631 struct sway_drag_icon *drag_icon; 656 drag_icons_update_position(seat);
632 wl_list_for_each(drag_icon, &root->drag_icons, link) {
633 if (drag_icon->seat == seat) {
634 drag_icon_update_position(drag_icon);
635 }
636 }
637 657
638 e->previous_node = node; 658 e->previous_node = node;
639} 659}
640 660
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
641/*----------------------------------------\ 691/*----------------------------------------\
642 * Functions used by handle_pointer_axis / 692 * Functions used by handle_pointer_axis /
643 *--------------------------------------*/ 693 *--------------------------------------*/
644 694
645static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { 695static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) {
646 switch (event->orientation) { 696 switch (event->orientation) {
647 case WLR_AXIS_ORIENTATION_VERTICAL: 697 case WL_POINTER_AXIS_VERTICAL_SCROLL:
648 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; 698 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
649 case WLR_AXIS_ORIENTATION_HORIZONTAL: 699 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
650 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; 700 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
651 default: 701 default:
652 sway_log(SWAY_DEBUG, "Unknown axis orientation"); 702 sway_log(SWAY_DEBUG, "Unknown axis orientation");
@@ -655,9 +705,9 @@ static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
655} 705}
656 706
657static void handle_pointer_axis(struct sway_seat *seat, 707static void handle_pointer_axis(struct sway_seat *seat,
658 struct wlr_event_pointer_axis *event) { 708 struct wlr_pointer_axis_event *event) {
659 struct sway_input_device *input_device = 709 struct sway_input_device *input_device =
660 event->device ? event->device->data : NULL; 710 event->pointer ? event->pointer->base.data : NULL;
661 struct input_config *ic = 711 struct input_config *ic =
662 input_device ? input_device_get_config(input_device) : NULL; 712 input_device ? input_device_get_config(input_device) : NULL;
663 struct sway_cursor *cursor = seat->cursor; 713 struct sway_cursor *cursor = seat->cursor;
@@ -703,6 +753,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
703 753
704 // Scrolling on a tabbed or stacked title bar (handled as press event) 754 // Scrolling on a tabbed or stacked title bar (handled as press event)
705 if (!handled && (on_titlebar || on_titlebar_border)) { 755 if (!handled && (on_titlebar || on_titlebar_border)) {
756 struct sway_node *new_focus;
706 enum sway_container_layout layout = container_parent_layout(cont); 757 enum sway_container_layout layout = container_parent_layout(cont);
707 if (layout == L_TABBED || layout == L_STACKED) { 758 if (layout == L_TABBED || layout == L_STACKED) {
708 struct sway_node *tabcontainer = node_get_parent(node); 759 struct sway_node *tabcontainer = node_get_parent(node);
@@ -710,7 +761,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
710 seat_get_active_tiling_child(seat, tabcontainer); 761 seat_get_active_tiling_child(seat, tabcontainer);
711 list_t *siblings = container_get_siblings(cont); 762 list_t *siblings = container_get_siblings(cont);
712 int desired = list_find(siblings, active->sway_container) + 763 int desired = list_find(siblings, active->sway_container) +
713 round(scroll_factor * event->delta_discrete); 764 roundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP);
714 if (desired < 0) { 765 if (desired < 0) {
715 desired = 0; 766 desired = 0;
716 } else if (desired >= siblings->length) { 767 } else if (desired >= siblings->length) {
@@ -719,14 +770,16 @@ static void handle_pointer_axis(struct sway_seat *seat,
719 770
720 struct sway_container *new_sibling_con = siblings->items[desired]; 771 struct sway_container *new_sibling_con = siblings->items[desired];
721 struct sway_node *new_sibling = &new_sibling_con->node; 772 struct sway_node *new_sibling = &new_sibling_con->node;
722 struct sway_node *new_focus =
723 seat_get_focus_inactive(seat, new_sibling);
724 // Use the focused child of the tabbed/stacked container, not the 773 // Use the focused child of the tabbed/stacked container, not the
725 // container the user scrolled on. 774 // container the user scrolled on.
726 seat_set_focus(seat, new_focus); 775 new_focus = seat_get_focus_inactive(seat, new_sibling);
727 transaction_commit_dirty(); 776 } else {
728 handled = true; 777 new_focus = seat_get_focus_inactive(seat, &cont->node);
729 } 778 }
779
780 seat_set_focus(seat, new_focus);
781 transaction_commit_dirty();
782 handled = true;
730 } 783 }
731 784
732 // Handle mouse bindings - x11 mouse buttons 4-7 - release event 785 // Handle mouse bindings - x11 mouse buttons 4-7 - release event
@@ -742,8 +795,307 @@ static void handle_pointer_axis(struct sway_seat *seat,
742 795
743 if (!handled) { 796 if (!handled) {
744 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, 797 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
745 event->orientation, scroll_factor * event->delta, 798 event->orientation, scroll_factor * event->delta,
746 round(scroll_factor * event->delta_discrete), event->source); 799 roundf(scroll_factor * event->delta_discrete), event->source,
800 event->relative_direction);
801 }
802}
803
804/*------------------------------------\
805 * Functions used by gesture support /
806 *----------------------------------*/
807
808/**
809 * Check gesture binding for a specific gesture type and finger count.
810 * Returns true if binding is present, false otherwise
811 */
812static bool gesture_binding_check(list_t *bindings, enum gesture_type type,
813 uint8_t fingers, struct sway_input_device *device) {
814 char *input =
815 device ? input_device_get_identifier(device->wlr_device) : strdup("*");
816
817 for (int i = 0; i < bindings->length; ++i) {
818 struct sway_gesture_binding *binding = bindings->items[i];
819
820 // Check type and finger count
821 if (!gesture_check(&binding->gesture, type, fingers)) {
822 continue;
823 }
824
825 // Check that input matches
826 if (strcmp(binding->input, "*") != 0 &&
827 strcmp(binding->input, input) != 0) {
828 continue;
829 }
830
831 free(input);
832
833 return true;
834 }
835
836 free(input);
837
838 return false;
839}
840
841/**
842 * Return the gesture binding which matches gesture type, finger count
843 * and direction, otherwise return null.
844 */
845static struct sway_gesture_binding* gesture_binding_match(
846 list_t *bindings, struct gesture *gesture, const char *input) {
847 struct sway_gesture_binding *current = NULL;
848
849 // Find best matching binding
850 for (int i = 0; i < bindings->length; ++i) {
851 struct sway_gesture_binding *binding = bindings->items[i];
852 bool exact = binding->flags & BINDING_EXACT;
853
854 // Check gesture matching
855 if (!gesture_match(&binding->gesture, gesture, exact)) {
856 continue;
857 }
858
859 // Check input matching
860 if (strcmp(binding->input, "*") != 0 &&
861 strcmp(binding->input, input) != 0) {
862 continue;
863 }
864
865 // If we already have a match ...
866 if (current) {
867 // ... check if input matching is equivalent
868 if (strcmp(current->input, binding->input) == 0) {
869
870 // ... - do not override an exact binding
871 if (!exact && current->flags & BINDING_EXACT) {
872 continue;
873 }
874
875 // ... - and ensure direction matching is better or equal
876 if (gesture_compare(&current->gesture, &binding->gesture) > 0) {
877 continue;
878 }
879 } else if (strcmp(binding->input, "*") == 0) {
880 // ... do not accept worse input match
881 continue;
882 }
883 }
884
885 // Accept newer or better match
886 current = binding;
887
888 // If exact binding and input is found, quit search
889 if (strcmp(current->input, input) == 0 &&
890 gesture_compare(&current->gesture, gesture) == 0) {
891 break;
892 }
893 } // for all gesture bindings
894
895 return current;
896}
897
898// Wrapper around gesture_tracker_end to use tracker with sway bindings
899static struct sway_gesture_binding* gesture_tracker_end_and_match(
900 struct gesture_tracker *tracker, struct sway_input_device* device) {
901 // Determine name of input that received gesture
902 char *input = device
903 ? input_device_get_identifier(device->wlr_device)
904 : strdup("*");
905
906 // Match tracking result to binding
907 struct gesture *gesture = gesture_tracker_end(tracker);
908 struct sway_gesture_binding *binding = gesture_binding_match(
909 config->current_mode->gesture_bindings, gesture, input);
910 free(gesture);
911 free(input);
912
913 return binding;
914}
915
916// Small wrapper around seat_execute_command to work on gesture bindings
917static void gesture_binding_execute(struct sway_seat *seat,
918 struct sway_gesture_binding *binding) {
919 struct sway_binding *dummy_binding =
920 calloc(1, sizeof(struct sway_binding));
921 dummy_binding->type = BINDING_GESTURE;
922 dummy_binding->command = binding->command;
923
924 char *description = gesture_to_string(&binding->gesture);
925 sway_log(SWAY_DEBUG, "executing gesture binding: %s", description);
926 free(description);
927
928 seat_execute_command(seat, dummy_binding);
929
930 free(dummy_binding);
931}
932
933static void handle_hold_begin(struct sway_seat *seat,
934 struct wlr_pointer_hold_begin_event *event) {
935 // Start tracking gesture if there is a matching binding ...
936 struct sway_input_device *device =
937 event->pointer ? event->pointer->base.data : NULL;
938 list_t *bindings = config->current_mode->gesture_bindings;
939 if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) {
940 struct seatop_default_event *seatop = seat->seatop_data;
941 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers);
942 } else {
943 // ... otherwise forward to client
944 struct sway_cursor *cursor = seat->cursor;
945 wlr_pointer_gestures_v1_send_hold_begin(
946 server.input->pointer_gestures, cursor->seat->wlr_seat,
947 event->time_msec, event->fingers);
948 }
949}
950
951static void handle_hold_end(struct sway_seat *seat,
952 struct wlr_pointer_hold_end_event *event) {
953 // Ensure that gesture is being tracked and was not cancelled
954 struct seatop_default_event *seatop = seat->seatop_data;
955 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) {
956 struct sway_cursor *cursor = seat->cursor;
957 wlr_pointer_gestures_v1_send_hold_end(
958 server.input->pointer_gestures, cursor->seat->wlr_seat,
959 event->time_msec, event->cancelled);
960 return;
961 }
962 if (event->cancelled) {
963 gesture_tracker_cancel(&seatop->gestures);
964 return;
965 }
966
967 // End gesture tracking and execute matched binding
968 struct sway_input_device *device =
969 event->pointer ? event->pointer->base.data : NULL;
970 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
971 &seatop->gestures, device);
972
973 if (binding) {
974 gesture_binding_execute(seat, binding);
975 }
976}
977
978static void handle_pinch_begin(struct sway_seat *seat,
979 struct wlr_pointer_pinch_begin_event *event) {
980 // Start tracking gesture if there is a matching binding ...
981 struct sway_input_device *device =
982 event->pointer ? event->pointer->base.data : NULL;
983 list_t *bindings = config->current_mode->gesture_bindings;
984 if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) {
985 struct seatop_default_event *seatop = seat->seatop_data;
986 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers);
987 } else {
988 // ... otherwise forward to client
989 struct sway_cursor *cursor = seat->cursor;
990 wlr_pointer_gestures_v1_send_pinch_begin(
991 server.input->pointer_gestures, cursor->seat->wlr_seat,
992 event->time_msec, event->fingers);
993 }
994}
995
996static void handle_pinch_update(struct sway_seat *seat,
997 struct wlr_pointer_pinch_update_event *event) {
998 // Update any ongoing tracking ...
999 struct seatop_default_event *seatop = seat->seatop_data;
1000 if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
1001 gesture_tracker_update(&seatop->gestures, event->dx, event->dy,
1002 event->scale, event->rotation);
1003 } else {
1004 // ... otherwise forward to client
1005 struct sway_cursor *cursor = seat->cursor;
1006 wlr_pointer_gestures_v1_send_pinch_update(
1007 server.input->pointer_gestures,
1008 cursor->seat->wlr_seat,
1009 event->time_msec, event->dx, event->dy,
1010 event->scale, event->rotation);
1011 }
1012}
1013
1014static void handle_pinch_end(struct sway_seat *seat,
1015 struct wlr_pointer_pinch_end_event *event) {
1016 // Ensure that gesture is being tracked and was not cancelled
1017 struct seatop_default_event *seatop = seat->seatop_data;
1018 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
1019 struct sway_cursor *cursor = seat->cursor;
1020 wlr_pointer_gestures_v1_send_pinch_end(
1021 server.input->pointer_gestures, cursor->seat->wlr_seat,
1022 event->time_msec, event->cancelled);
1023 return;
1024 }
1025 if (event->cancelled) {
1026 gesture_tracker_cancel(&seatop->gestures);
1027 return;
1028 }
1029
1030 // End gesture tracking and execute matched binding
1031 struct sway_input_device *device =
1032 event->pointer ? event->pointer->base.data : NULL;
1033 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
1034 &seatop->gestures, device);
1035
1036 if (binding) {
1037 gesture_binding_execute(seat, binding);
1038 }
1039}
1040
1041static void handle_swipe_begin(struct sway_seat *seat,
1042 struct wlr_pointer_swipe_begin_event *event) {
1043 // Start tracking gesture if there is a matching binding ...
1044 struct sway_input_device *device =
1045 event->pointer ? event->pointer->base.data : NULL;
1046 list_t *bindings = config->current_mode->gesture_bindings;
1047 if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
1048 struct seatop_default_event *seatop = seat->seatop_data;
1049 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
1050 } else {
1051 // ... otherwise forward to client
1052 struct sway_cursor *cursor = seat->cursor;
1053 wlr_pointer_gestures_v1_send_swipe_begin(
1054 server.input->pointer_gestures, cursor->seat->wlr_seat,
1055 event->time_msec, event->fingers);
1056 }
1057}
1058
1059static void handle_swipe_update(struct sway_seat *seat,
1060 struct wlr_pointer_swipe_update_event *event) {
1061
1062 // Update any ongoing tracking ...
1063 struct seatop_default_event *seatop = seat->seatop_data;
1064 if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
1065 gesture_tracker_update(&seatop->gestures,
1066 event->dx, event->dy, NAN, NAN);
1067 } else {
1068 // ... otherwise forward to client
1069 struct sway_cursor *cursor = seat->cursor;
1070 wlr_pointer_gestures_v1_send_swipe_update(
1071 server.input->pointer_gestures, cursor->seat->wlr_seat,
1072 event->time_msec, event->dx, event->dy);
1073 }
1074}
1075
1076static void handle_swipe_end(struct sway_seat *seat,
1077 struct wlr_pointer_swipe_end_event *event) {
1078 // Ensure gesture is being tracked and was not cancelled
1079 struct seatop_default_event *seatop = seat->seatop_data;
1080 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
1081 struct sway_cursor *cursor = seat->cursor;
1082 wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures,
1083 cursor->seat->wlr_seat, event->time_msec, event->cancelled);
1084 return;
1085 }
1086 if (event->cancelled) {
1087 gesture_tracker_cancel(&seatop->gestures);
1088 return;
1089 }
1090
1091 // End gesture tracking and execute matched binding
1092 struct sway_input_device *device =
1093 event->pointer ? event->pointer->base.data : NULL;
1094 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
1095 &seatop->gestures, device);
1096
1097 if (binding) {
1098 gesture_binding_execute(seat, binding);
747 } 1099 }
748} 1100}
749 1101
@@ -776,6 +1128,15 @@ static const struct sway_seatop_impl seatop_impl = {
776 .pointer_axis = handle_pointer_axis, 1128 .pointer_axis = handle_pointer_axis,
777 .tablet_tool_tip = handle_tablet_tool_tip, 1129 .tablet_tool_tip = handle_tablet_tool_tip,
778 .tablet_tool_motion = handle_tablet_tool_motion, 1130 .tablet_tool_motion = handle_tablet_tool_motion,
1131 .hold_begin = handle_hold_begin,
1132 .hold_end = handle_hold_end,
1133 .pinch_begin = handle_pinch_begin,
1134 .pinch_update = handle_pinch_update,
1135 .pinch_end = handle_pinch_end,
1136 .swipe_begin = handle_swipe_begin,
1137 .swipe_update = handle_swipe_update,
1138 .swipe_end = handle_swipe_end,
1139 .touch_down = handle_touch_down,
779 .rebase = handle_rebase, 1140 .rebase = handle_rebase,
780 .allow_set_cursor = true, 1141 .allow_set_cursor = true,
781}; 1142};
@@ -786,8 +1147,8 @@ void seatop_begin_default(struct sway_seat *seat) {
786 struct seatop_default_event *e = 1147 struct seatop_default_event *e =
787 calloc(1, sizeof(struct seatop_default_event)); 1148 calloc(1, sizeof(struct seatop_default_event));
788 sway_assert(e, "Unable to allocate seatop_default_event"); 1149 sway_assert(e, "Unable to allocate seatop_default_event");
1150
789 seat->seatop_impl = &seatop_impl; 1151 seat->seatop_impl = &seatop_impl;
790 seat->seatop_data = e; 1152 seat->seatop_data = e;
791
792 seatop_rebase(seat, 0); 1153 seatop_rebase(seat, 0);
793} 1154}
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
index 844cf5ab..340e334b 100644
--- a/sway/input/seatop_down.c
+++ b/sway/input/seatop_down.c
@@ -1,23 +1,138 @@
1#define _POSIX_C_SOURCE 200809L
2#include <float.h> 1#include <float.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_tablet_v2.h> 3#include <wlr/types/wlr_tablet_v2.h>
4#include <wlr/types/wlr_touch.h>
5#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 6#include "sway/input/seat.h"
7#include "sway/tree/view.h" 7#include "sway/tree/view.h"
8#include "sway/desktop/transaction.h" 8#include "sway/desktop/transaction.h"
9#include "log.h" 9#include "log.h"
10 10
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;
20 struct sway_seat *seat;
21 struct wl_listener surface_destroy;
22 struct wlr_surface *surface;
13 double ref_lx, ref_ly; // cursor's x/y at start of op 23 double ref_lx, ref_ly; // cursor's x/y at start of op
14 double ref_con_lx, ref_con_ly; // container's x/y at start of op 24 double ref_con_lx, ref_con_ly; // container's x/y at start of op
25 struct wl_list point_events; // seatop_touch_point_event::link
15}; 26};
16 27
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
17static void handle_pointer_axis(struct sway_seat *seat, 132static void handle_pointer_axis(struct sway_seat *seat,
18 struct wlr_event_pointer_axis *event) { 133 struct wlr_pointer_axis_event *event) {
19 struct sway_input_device *input_device = 134 struct sway_input_device *input_device =
20 event->device ? event->device->data : NULL; 135 event->pointer ? event->pointer->base.data : NULL;
21 struct input_config *ic = 136 struct input_config *ic =
22 input_device ? input_device_get_config(input_device) : NULL; 137 input_device ? input_device_get_config(input_device) : NULL;
23 float scroll_factor = 138 float scroll_factor =
@@ -25,12 +140,13 @@ static void handle_pointer_axis(struct sway_seat *seat,
25 140
26 wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, 141 wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec,
27 event->orientation, scroll_factor * event->delta, 142 event->orientation, scroll_factor * event->delta,
28 round(scroll_factor * event->delta_discrete), event->source); 143 roundf(scroll_factor * event->delta_discrete), event->source,
144 event->relative_direction);
29} 145}
30 146
31static void handle_button(struct sway_seat *seat, uint32_t time_msec, 147static void handle_button(struct sway_seat *seat, uint32_t time_msec,
32 struct wlr_input_device *device, uint32_t button, 148 struct wlr_input_device *device, uint32_t button,
33 enum wlr_button_state state) { 149 enum wl_pointer_button_state state) {
34 seat_pointer_notify_button(seat, time_msec, button, state); 150 seat_pointer_notify_button(seat, time_msec, button, state);
35 151
36 if (seat->cursor->pressed_button_count == 0) { 152 if (seat->cursor->pressed_button_count == 0) {
@@ -40,8 +156,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
40 156
41static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { 157static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
42 struct seatop_down_event *e = seat->seatop_data; 158 struct seatop_down_event *e = seat->seatop_data;
43 struct sway_container *con = e->con; 159 if (seat_is_input_allowed(seat, e->surface)) {
44 if (seat_is_input_allowed(seat, con->view->surface)) {
45 double moved_x = seat->cursor->cursor->x - e->ref_lx; 160 double moved_x = seat->cursor->cursor->x - e->ref_lx;
46 double moved_y = seat->cursor->cursor->y - e->ref_ly; 161 double moved_y = seat->cursor->cursor->y - e->ref_ly;
47 double sx = e->ref_con_lx + moved_x; 162 double sx = e->ref_con_lx + moved_x;
@@ -62,8 +177,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
62static void handle_tablet_tool_motion(struct sway_seat *seat, 177static void handle_tablet_tool_motion(struct sway_seat *seat,
63 struct sway_tablet_tool *tool, uint32_t time_msec) { 178 struct sway_tablet_tool *tool, uint32_t time_msec) {
64 struct seatop_down_event *e = seat->seatop_data; 179 struct seatop_down_event *e = seat->seatop_data;
65 struct sway_container *con = e->con; 180 if (seat_is_input_allowed(seat, e->surface)) {
66 if (seat_is_input_allowed(seat, con->view->surface)) {
67 double moved_x = seat->cursor->cursor->x - e->ref_lx; 181 double moved_x = seat->cursor->cursor->x - e->ref_lx;
68 double moved_y = seat->cursor->cursor->y - e->ref_ly; 182 double moved_y = seat->cursor->cursor->y - e->ref_ly;
69 double sx = e->ref_con_lx + moved_x; 183 double sx = e->ref_con_lx + moved_x;
@@ -72,6 +186,14 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
72 } 186 }
73} 187}
74 188
189static void handle_destroy(struct wl_listener *listener, void *data) {
190 struct seatop_down_event *e =
191 wl_container_of(listener, e, surface_destroy);
192 if (e) {
193 seatop_begin_default(e->seat);
194 }
195}
196
75static void handle_unref(struct sway_seat *seat, struct sway_container *con) { 197static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
76 struct seatop_down_event *e = seat->seatop_data; 198 struct seatop_down_event *e = seat->seatop_data;
77 if (e->con == con) { 199 if (e->con == con) {
@@ -79,34 +201,63 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
79 } 201 }
80} 202}
81 203
204static void handle_end(struct sway_seat *seat) {
205 struct seatop_down_event *e = seat->seatop_data;
206 wl_list_remove(&e->surface_destroy.link);
207}
208
82static const struct sway_seatop_impl seatop_impl = { 209static const struct sway_seatop_impl seatop_impl = {
83 .button = handle_button, 210 .button = handle_button,
84 .pointer_motion = handle_pointer_motion, 211 .pointer_motion = handle_pointer_motion,
85 .pointer_axis = handle_pointer_axis, 212 .pointer_axis = handle_pointer_axis,
86 .tablet_tool_tip = handle_tablet_tool_tip, 213 .tablet_tool_tip = handle_tablet_tool_tip,
87 .tablet_tool_motion = handle_tablet_tool_motion, 214 .tablet_tool_motion = handle_tablet_tool_motion,
215 .touch_motion = handle_touch_motion,
216 .touch_up = handle_touch_up,
217 .touch_down = handle_touch_down,
218 .touch_cancel = handle_touch_cancel,
88 .unref = handle_unref, 219 .unref = handle_unref,
220 .end = handle_end,
89 .allow_set_cursor = true, 221 .allow_set_cursor = true,
90}; 222};
91 223
92void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 224void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
93 uint32_t time_msec, int sx, int sy) { 225 double sx, double sy) {
226 seatop_begin_down_on_surface(seat, con->view->surface, sx, sy);
227 struct seatop_down_event *e = seat->seatop_data;
228 e->con = con;
229
230 container_raise_floating(con);
231 transaction_commit_dirty();
232}
233
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
241void seatop_begin_down_on_surface(struct sway_seat *seat,
242 struct wlr_surface *surface, double sx, double sy) {
94 seatop_end(seat); 243 seatop_end(seat);
95 244
96 struct seatop_down_event *e = 245 struct seatop_down_event *e =
97 calloc(1, sizeof(struct seatop_down_event)); 246 calloc(1, sizeof(struct seatop_down_event));
98 if (!e) { 247 if (!sway_assert(e, "Unable to allocate e")) {
99 return; 248 return;
100 } 249 }
101 e->con = con; 250 e->con = NULL;
251 e->seat = seat;
252 e->surface = surface;
253 wl_signal_add(&e->surface->events.destroy, &e->surface_destroy);
254 e->surface_destroy.notify = handle_destroy;
102 e->ref_lx = seat->cursor->cursor->x; 255 e->ref_lx = seat->cursor->cursor->x;
103 e->ref_ly = seat->cursor->cursor->y; 256 e->ref_ly = seat->cursor->cursor->y;
104 e->ref_con_lx = sx; 257 e->ref_con_lx = sx;
105 e->ref_con_ly = sy; 258 e->ref_con_ly = sy;
259 wl_list_init(&e->point_events);
106 260
107 seat->seatop_impl = &seatop_impl; 261 seat->seatop_impl = &seatop_impl;
108 seat->seatop_data = e; 262 seat->seatop_data = e;
109
110 container_raise_floating(con);
111 transaction_commit_dirty();
112} 263}
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c
index ddcd4c53..83668d88 100644
--- a/sway/input/seatop_move_floating.c
+++ b/sway/input/seatop_move_floating.c
@@ -1,6 +1,4 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h> 1#include <wlr/types/wlr_cursor.h>
3#include "sway/desktop.h"
4#include "sway/desktop/transaction.h" 2#include "sway/desktop/transaction.h"
5#include "sway/input/cursor.h" 3#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 4#include "sway/input/seat.h"
@@ -23,7 +21,7 @@ static void finalize_move(struct sway_seat *seat) {
23 21
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 8400a4b3..bec86e33 100644
--- a/sway/input/seatop_resize_floating.c
+++ b/sway/input/seatop_resize_floating.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_xcursor_manager.h> 3#include <wlr/types/wlr_xcursor_manager.h>
@@ -21,7 +20,7 @@ struct seatop_resize_floating_event {
21 20
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
@@ -80,17 +79,25 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
80 double height = e->ref_height + grow_height; 79 double height = e->ref_height + grow_height;
81 int min_width, max_width, min_height, max_height; 80 int min_width, max_width, min_height, max_height;
82 floating_calculate_constraints(&min_width, &max_width, 81 floating_calculate_constraints(&min_width, &max_width,
83 &min_height, &max_height); 82 &min_height, &max_height);
84 width = fmax(min_width + border_width, fmin(width, max_width)); 83 width = fmin(width, max_width - border_width);
85 height = fmax(min_height + border_height, fmin(height, max_height)); 84 width = fmax(width, min_width + border_width);
85 width = fmax(width, 1);
86 height = fmin(height, max_height - border_height);
87 height = fmax(height, min_height + border_height);
88 height = fmax(height, 1);
86 89
87 // Apply the view's min/max size 90 // Apply the view's min/max size
88 if (con->view) { 91 if (con->view) {
89 double view_min_width, view_max_width, view_min_height, view_max_height; 92 double view_min_width, view_max_width, view_min_height, view_max_height;
90 view_get_constraints(con->view, &view_min_width, &view_max_width, 93 view_get_constraints(con->view, &view_min_width, &view_max_width,
91 &view_min_height, &view_max_height); 94 &view_min_height, &view_max_height);
92 width = fmax(view_min_width + border_width, fmin(width, view_max_width)); 95 width = fmin(width, view_max_width - border_width);
93 height = fmax(view_min_height + border_height, fmin(height, view_max_height)); 96 width = fmax(width, view_min_width + border_width);
97 width = fmax(width, 1);
98 height = fmin(height, view_max_height - border_height);
99 height = fmax(height, view_min_height + border_height);
100 height = fmax(height, 1);
94 101
95 } 102 }
96 103
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c
index 869d11b5..15fd333b 100644
--- a/sway/input/seatop_resize_tiling.c
+++ b/sway/input/seatop_resize_tiling.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h> 1#include <wlr/types/wlr_cursor.h>
3#include <wlr/util/edges.h> 2#include <wlr/util/edges.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -46,7 +45,7 @@ static struct sway_container *container_get_resize_sibling(
46 45
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 9ea87a1a..831f4dbf 100644
--- a/sway/input/switch.c
+++ b/sway/input/switch.c
@@ -1,6 +1,5 @@
1#include "sway/config.h" 1#include "sway/config.h"
2#include "sway/input/switch.h" 2#include "sway/input/switch.h"
3#include <wlr/types/wlr_idle.h>
4#include "log.h" 3#include "log.h"
5 4
6struct sway_switch *sway_switch_create(struct sway_seat *seat, 5struct sway_switch *sway_switch_create(struct sway_seat *seat,
@@ -11,6 +10,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
11 return NULL; 10 return NULL;
12 } 11 }
13 device->switch_device = switch_device; 12 device->switch_device = switch_device;
13 switch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device);
14 switch_device->seat_device = device; 14 switch_device->seat_device = device;
15 switch_device->state = WLR_SWITCH_STATE_OFF; 15 switch_device->state = WLR_SWITCH_STATE_OFF;
16 wl_list_init(&switch_device->switch_toggle.link); 16 wl_list_init(&switch_device->switch_toggle.link);
@@ -19,9 +19,22 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
19 return switch_device; 19 return switch_device;
20} 20}
21 21
22static bool sway_switch_trigger_test(enum sway_switch_trigger trigger,
23 enum wlr_switch_state state) {
24 switch (trigger) {
25 case SWAY_SWITCH_TRIGGER_ON:
26 return state == WLR_SWITCH_STATE_ON;
27 case SWAY_SWITCH_TRIGGER_OFF:
28 return state == WLR_SWITCH_STATE_OFF;
29 case SWAY_SWITCH_TRIGGER_TOGGLE:
30 return true;
31 }
32 abort(); // unreachable
33}
34
22static void execute_binding(struct sway_switch *sway_switch) { 35static void execute_binding(struct sway_switch *sway_switch) {
23 struct sway_seat* seat = sway_switch->seat_device->sway_seat; 36 struct sway_seat *seat = sway_switch->seat_device->sway_seat;
24 bool input_inhibited = seat->exclusive_client != NULL; 37 bool locked = server.session_lock.lock;
25 38
26 list_t *bindings = config->current_mode->switch_bindings; 39 list_t *bindings = config->current_mode->switch_bindings;
27 struct sway_switch_binding *matched_binding = NULL; 40 struct sway_switch_binding *matched_binding = NULL;
@@ -30,22 +43,21 @@ static void execute_binding(struct sway_switch *sway_switch) {
30 if (binding->type != sway_switch->type) { 43 if (binding->type != sway_switch->type) {
31 continue; 44 continue;
32 } 45 }
33 if (binding->state != WLR_SWITCH_STATE_TOGGLE && 46 if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) {
34 binding->state != sway_switch->state) {
35 continue; 47 continue;
36 } 48 }
37 if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE 49 if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE
38 || (binding->flags & BINDING_RELOAD) == 0)) { 50 || (binding->flags & BINDING_RELOAD) == 0)) {
39 continue; 51 continue;
40 } 52 }
41 bool binding_locked = binding->flags & BINDING_LOCKED; 53 bool binding_locked = binding->flags & BINDING_LOCKED;
42 if (!binding_locked && input_inhibited) { 54 if (!binding_locked && locked) {
43 continue; 55 continue;
44 } 56 }
45 57
46 matched_binding = binding; 58 matched_binding = binding;
47 59
48 if (binding_locked == input_inhibited) { 60 if (binding_locked == locked) {
49 break; 61 break;
50 } 62 }
51 } 63 }
@@ -65,7 +77,7 @@ static void execute_binding(struct sway_switch *sway_switch) {
65static void handle_switch_toggle(struct wl_listener *listener, void *data) { 77static void handle_switch_toggle(struct wl_listener *listener, void *data) {
66 struct sway_switch *sway_switch = 78 struct sway_switch *sway_switch =
67 wl_container_of(listener, sway_switch, switch_toggle); 79 wl_container_of(listener, sway_switch, switch_toggle);
68 struct wlr_event_switch_toggle *event = data; 80 struct wlr_switch_toggle_event *event = data;
69 struct sway_seat *seat = sway_switch->seat_device->sway_seat; 81 struct sway_seat *seat = sway_switch->seat_device->sway_seat;
70 seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); 82 seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH);
71 83
@@ -82,10 +94,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) {
82} 94}
83 95
84void sway_switch_configure(struct sway_switch *sway_switch) { 96void sway_switch_configure(struct sway_switch *sway_switch) {
85 struct wlr_input_device *wlr_device =
86 sway_switch->seat_device->input_device->wlr_device;
87 wl_list_remove(&sway_switch->switch_toggle.link); 97 wl_list_remove(&sway_switch->switch_toggle.link);
88 wl_signal_add(&wlr_device->switch_device->events.toggle, 98 wl_signal_add(&sway_switch->wlr->events.toggle,
89 &sway_switch->switch_toggle); 99 &sway_switch->switch_toggle);
90 sway_switch->switch_toggle.notify = handle_switch_toggle; 100 sway_switch->switch_toggle.notify = handle_switch_toggle;
91 sway_log(SWAY_DEBUG, "Configured switch for device"); 101 sway_log(SWAY_DEBUG, "Configured switch for device");
diff --git a/sway/input/tablet.c b/sway/input/tablet.c
index 26e86e36..2863642a 100644
--- a/sway/input/tablet.c
+++ b/sway/input/tablet.c
@@ -1,6 +1,5 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <wlr/backend/libinput.h> 2#include <wlr/config.h>
4#include <wlr/types/wlr_tablet_v2.h> 3#include <wlr/types/wlr_tablet_v2.h>
5#include <wlr/types/wlr_tablet_tool.h> 4#include <wlr/types/wlr_tablet_tool.h>
6#include <wlr/types/wlr_tablet_pad.h> 5#include <wlr/types/wlr_tablet_pad.h>
@@ -9,6 +8,10 @@
9#include "sway/input/seat.h" 8#include "sway/input/seat.h"
10#include "sway/input/tablet.h" 9#include "sway/input/tablet.h"
11 10
11#if WLR_HAS_LIBINPUT_BACKEND
12#include <wlr/backend/libinput.h>
13#endif
14
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) {
@@ -196,7 +199,7 @@ static void handle_tablet_pad_attach(struct wl_listener *listener,
196 199
197static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { 200static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
198 struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); 201 struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring);
199 struct wlr_event_tablet_pad_ring *event = data; 202 struct wlr_tablet_pad_ring_event *event = data;
200 203
201 if (!pad->current_surface) { 204 if (!pad->current_surface) {
202 return; 205 return;
@@ -210,7 +213,7 @@ static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
210 213
211static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { 214static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
212 struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); 215 struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip);
213 struct wlr_event_tablet_pad_strip *event = data; 216 struct wlr_tablet_pad_strip_event *event = data;
214 217
215 if (!pad->current_surface) { 218 if (!pad->current_surface) {
216 return; 219 return;
@@ -224,7 +227,7 @@ static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
224 227
225static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { 228static void handle_tablet_pad_button(struct wl_listener *listener, void *data) {
226 struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); 229 struct sway_tablet_pad *pad = wl_container_of(listener, pad, button);
227 struct wlr_event_tablet_pad_button *event = data; 230 struct wlr_tablet_pad_button_event *event = data;
228 231
229 if (!pad->current_surface) { 232 if (!pad->current_surface) {
230 return; 233 return;
@@ -246,6 +249,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
246 return NULL; 249 return NULL;
247 } 250 }
248 251
252 tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device);
249 tablet_pad->seat_device = device; 253 tablet_pad->seat_device = device;
250 wl_list_init(&tablet_pad->attach.link); 254 wl_list_init(&tablet_pad->attach.link);
251 wl_list_init(&tablet_pad->button.link); 255 wl_list_init(&tablet_pad->button.link);
@@ -260,40 +264,41 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
260} 264}
261 265
262void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { 266void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
263 struct wlr_input_device *device = 267 struct wlr_input_device *wlr_device =
264 tablet_pad->seat_device->input_device->wlr_device; 268 tablet_pad->seat_device->input_device->wlr_device;
265 struct sway_seat *seat = tablet_pad->seat_device->sway_seat; 269 struct sway_seat *seat = tablet_pad->seat_device->sway_seat;
266 270
267 if (!tablet_pad->tablet_v2_pad) { 271 if (!tablet_pad->tablet_v2_pad) {
268 tablet_pad->tablet_v2_pad = 272 tablet_pad->tablet_v2_pad =
269 wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); 273 wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device);
270 } 274 }
271 275
272 wl_list_remove(&tablet_pad->attach.link); 276 wl_list_remove(&tablet_pad->attach.link);
273 tablet_pad->attach.notify = handle_tablet_pad_attach; 277 tablet_pad->attach.notify = handle_tablet_pad_attach;
274 wl_signal_add(&device->tablet_pad->events.attach_tablet, 278 wl_signal_add(&tablet_pad->wlr->events.attach_tablet,
275 &tablet_pad->attach); 279 &tablet_pad->attach);
276 280
277 wl_list_remove(&tablet_pad->button.link); 281 wl_list_remove(&tablet_pad->button.link);
278 tablet_pad->button.notify = handle_tablet_pad_button; 282 tablet_pad->button.notify = handle_tablet_pad_button;
279 wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); 283 wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button);
280 284
281 wl_list_remove(&tablet_pad->strip.link); 285 wl_list_remove(&tablet_pad->strip.link);
282 tablet_pad->strip.notify = handle_tablet_pad_strip; 286 tablet_pad->strip.notify = handle_tablet_pad_strip;
283 wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); 287 wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip);
284 288
285 wl_list_remove(&tablet_pad->ring.link); 289 wl_list_remove(&tablet_pad->ring.link);
286 tablet_pad->ring.notify = handle_tablet_pad_ring; 290 tablet_pad->ring.notify = handle_tablet_pad_ring;
287 wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); 291 wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring);
288 292
293#if WLR_HAS_LIBINPUT_BACKEND
289 /* Search for a sibling tablet */ 294 /* Search for a sibling tablet */
290 if (!wlr_input_device_is_libinput(device)) { 295 if (!wlr_input_device_is_libinput(wlr_device)) {
291 /* We can only do this on libinput devices */ 296 /* We can only do this on libinput devices */
292 return; 297 return;
293 } 298 }
294 299
295 struct libinput_device_group *group = 300 struct libinput_device_group *group =
296 libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); 301 libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device));
297 struct sway_tablet *tool; 302 struct sway_tablet *tool;
298 wl_list_for_each(tool, &seat->cursor->tablets, link) { 303 wl_list_for_each(tool, &seat->cursor->tablets, link) {
299 struct wlr_input_device *tablet = 304 struct wlr_input_device *tablet =
@@ -310,6 +315,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
310 break; 315 break;
311 } 316 }
312 } 317 }
318#endif
313} 319}
314 320
315void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { 321void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) {
@@ -333,14 +339,10 @@ static void handle_pad_tablet_surface_destroy(struct wl_listener *listener,
333 struct sway_tablet_pad *tablet_pad = 339 struct sway_tablet_pad *tablet_pad =
334 wl_container_of(listener, tablet_pad, surface_destroy); 340 wl_container_of(listener, tablet_pad, surface_destroy);
335 341
336 wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, 342 sway_tablet_pad_set_focus(tablet_pad, NULL);
337 tablet_pad->current_surface);
338 wl_list_remove(&tablet_pad->surface_destroy.link);
339 wl_list_init(&tablet_pad->surface_destroy.link);
340 tablet_pad->current_surface = NULL;
341} 343}
342 344
343void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, 345void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad,
344 struct wlr_surface *surface) { 346 struct wlr_surface *surface) {
345 if (!tablet_pad || !tablet_pad->tablet) { 347 if (!tablet_pad || !tablet_pad->tablet) {
346 return; 348 return;
@@ -359,7 +361,8 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad,
359 tablet_pad->current_surface = NULL; 361 tablet_pad->current_surface = NULL;
360 } 362 }
361 363
362 if (!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { 364 if (surface == NULL ||
365 !wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) {
363 return; 366 return;
364 } 367 }
365 368
@@ -367,7 +370,6 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad,
367 tablet_pad->tablet->tablet_v2, surface); 370 tablet_pad->tablet->tablet_v2, surface);
368 371
369 tablet_pad->current_surface = surface; 372 tablet_pad->current_surface = surface;
370 wl_list_remove(&tablet_pad->surface_destroy.link);
371 tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; 373 tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy;
372 wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); 374 wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy);
373} 375}
diff --git a/sway/input/text_input.c b/sway/input/text_input.c
index b8c19c17..c38a3bb2 100644
--- a/sway/input/text_input.c
+++ b/sway/input/text_input.c
@@ -2,7 +2,14 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include "log.h" 3#include "log.h"
4#include "sway/input/seat.h" 4#include "sway/input/seat.h"
5#include "sway/scene_descriptor.h"
6#include "sway/tree/root.h"
7#include "sway/tree/view.h"
8#include "sway/output.h"
5#include "sway/input/text_input.h" 9#include "sway/input/text_input.h"
10#include "sway/input/text_input_popup.h"
11#include "sway/layers.h"
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) {
@@ -77,8 +84,6 @@ static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) {
77 struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat); 84 struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat);
78 wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, 85 wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab,
79 active_keyboard); 86 active_keyboard);
80 wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab,
81 &active_keyboard->modifiers);
82 87
83 wl_signal_add(&keyboard_grab->events.destroy, 88 wl_signal_add(&keyboard_grab->events.destroy,
84 &relay->input_method_keyboard_grab_destroy); 89 &relay->input_method_keyboard_grab_destroy);
@@ -104,6 +109,10 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) {
104 input_method_destroy); 109 input_method_destroy);
105 struct wlr_input_method_v2 *context = data; 110 struct wlr_input_method_v2 *context = data;
106 assert(context == relay->input_method); 111 assert(context == relay->input_method);
112 wl_list_remove(&relay->input_method_commit.link);
113 wl_list_remove(&relay->input_method_grab_keyboard.link);
114 wl_list_remove(&relay->input_method_destroy.link);
115 wl_list_remove(&relay->input_method_new_popup_surface.link);
107 relay->input_method = NULL; 116 relay->input_method = NULL;
108 struct sway_text_input *text_input = relay_get_focused_text_input(relay); 117 struct sway_text_input *text_input = relay_get_focused_text_input(relay);
109 if (text_input) { 118 if (text_input) {
@@ -135,6 +144,11 @@ static void relay_send_im_state(struct sway_input_method_relay *relay,
135 input->current.content_type.hint, 144 input->current.content_type.hint,
136 input->current.content_type.purpose); 145 input->current.content_type.purpose);
137 } 146 }
147 struct sway_input_popup *popup;
148 wl_list_for_each(popup, &relay->input_popups, link) {
149 // send_text_input_rectangle is called in this function
150 input_popup_update(popup);
151 }
138 wlr_input_method_v2_send_done(input_method); 152 wlr_input_method_v2_send_done(input_method);
139 // TODO: pass intent, display popup size 153 // TODO: pass intent, display popup size
140} 154}
@@ -257,6 +271,211 @@ static void relay_handle_text_input(struct wl_listener *listener,
257 sway_text_input_create(relay, wlr_text_input); 271 sway_text_input_create(relay, wlr_text_input);
258} 272}
259 273
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
260static void relay_handle_input_method(struct wl_listener *listener, 479static void relay_handle_input_method(struct wl_listener *listener,
261 void *data) { 480 void *data) {
262 struct sway_input_method_relay *relay = wl_container_of(listener, relay, 481 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
@@ -282,10 +501,13 @@ static void relay_handle_input_method(struct wl_listener *listener,
282 wl_signal_add(&relay->input_method->events.destroy, 501 wl_signal_add(&relay->input_method->events.destroy,
283 &relay->input_method_destroy); 502 &relay->input_method_destroy);
284 relay->input_method_destroy.notify = handle_im_destroy; 503 relay->input_method_destroy.notify = handle_im_destroy;
504 wl_signal_add(&relay->input_method->events.new_popup_surface,
505 &relay->input_method_new_popup_surface);
506 relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface;
285 507
286 struct sway_text_input *text_input = relay_get_focusable_text_input(relay); 508 struct sway_text_input *text_input = relay_get_focusable_text_input(relay);
287 if (text_input) { 509 if (text_input) {
288 wlr_text_input_v3_send_enter(text_input->input, 510 text_input_send_enter(text_input,
289 text_input->pending_focused_surface); 511 text_input->pending_focused_surface);
290 text_input_set_pending_focused_surface(text_input, NULL); 512 text_input_set_pending_focused_surface(text_input, NULL);
291 } 513 }
@@ -295,6 +517,7 @@ void sway_input_method_relay_init(struct sway_seat *seat,
295 struct sway_input_method_relay *relay) { 517 struct sway_input_method_relay *relay) {
296 relay->seat = seat; 518 relay->seat = seat;
297 wl_list_init(&relay->text_inputs); 519 wl_list_init(&relay->text_inputs);
520 wl_list_init(&relay->input_popups);
298 521
299 relay->text_input_new.notify = relay_handle_text_input; 522 relay->text_input_new.notify = relay_handle_text_input;
300 wl_signal_add(&server.text_input->events.text_input, 523 wl_signal_add(&server.text_input->events.text_input,
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 1b64f86e..81ca3483 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -3,7 +3,8 @@
3#include <json.h> 3#include <json.h>
4#include <libevdev/libevdev.h> 4#include <libevdev/libevdev.h>
5#include <stdio.h> 5#include <stdio.h>
6#include <wlr/backend/libinput.h> 6#include <wlr/config.h>
7#include <wlr/types/wlr_content_type_v1.h>
7#include <wlr/types/wlr_output.h> 8#include <wlr/types/wlr_output.h>
8#include <xkbcommon/xkbcommon.h> 9#include <xkbcommon/xkbcommon.h>
9#include "config.h" 10#include "config.h"
@@ -20,6 +21,10 @@
20#include "wlr-layer-shell-unstable-v1-protocol.h" 21#include "wlr-layer-shell-unstable-v1-protocol.h"
21#include "sway/desktop/idle_inhibit_v1.h" 22#include "sway/desktop/idle_inhibit_v1.h"
22 23
24#if WLR_HAS_LIBINPUT_BACKEND
25#include <wlr/backend/libinput.h>
26#endif
27
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,27 +288,56 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name,
238 json_object_object_add(object, "focus", focus); 288 json_object_object_add(object, "focus", focus);
239 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); 289 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0));
240 json_object_object_add(object, "sticky", json_object_new_boolean(false)); 290 json_object_object_add(object, "sticky", json_object_new_boolean(false));
291 json_object_object_add(object, "floating", NULL);
292 json_object_object_add(object, "scratchpad_state", NULL);
241 293
242 return object; 294 return object;
243} 295}
244 296
297static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) {
298 json_object_object_add(object, "primary", json_object_new_boolean(false));
299 json_object_object_add(object, "make",
300 json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown"));
301 json_object_object_add(object, "model",
302 json_object_new_string(wlr_output->model ? wlr_output->model : "Unknown"));
303 json_object_object_add(object, "serial",
304 json_object_new_string(wlr_output->serial ? wlr_output->serial : "Unknown"));
305
306 json_object *modes_array = json_object_new_array();
307 struct wlr_output_mode *mode;
308 wl_list_for_each(mode, &wlr_output->modes, link) {
309 json_object *mode_object = json_object_new_object();
310 json_object_object_add(mode_object, "width",
311 json_object_new_int(mode->width));
312 json_object_object_add(mode_object, "height",
313 json_object_new_int(mode->height));
314 json_object_object_add(mode_object, "refresh",
315 json_object_new_int(mode->refresh));
316 json_object_array_add(modes_array, mode_object);
317 }
318 json_object_object_add(object, "modes", modes_array);
319}
320
245static void ipc_json_describe_output(struct sway_output *output, 321static void ipc_json_describe_output(struct sway_output *output,
246 json_object *object) { 322 json_object *object) {
323 ipc_json_describe_wlr_output(output->wlr_output, object);
324}
325
326static void ipc_json_describe_enabled_output(struct sway_output *output,
327 json_object *object) {
328 ipc_json_describe_output(output, object);
329
247 struct wlr_output *wlr_output = output->wlr_output; 330 struct wlr_output *wlr_output = output->wlr_output;
331 json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
248 json_object_object_add(object, "active", json_object_new_boolean(true)); 332 json_object_object_add(object, "active", json_object_new_boolean(true));
249 json_object_object_add(object, "dpms", 333 json_object_object_add(object, "dpms",
250 json_object_new_boolean(wlr_output->enabled)); 334 json_object_new_boolean(wlr_output->enabled));
251 json_object_object_add(object, "primary", json_object_new_boolean(false)); 335 json_object_object_add(object, "power",
336 json_object_new_boolean(wlr_output->enabled));
252 json_object_object_add(object, "layout", json_object_new_string("output")); 337 json_object_object_add(object, "layout", json_object_new_string("output"));
253 json_object_object_add(object, "orientation", 338 json_object_object_add(object, "orientation",
254 json_object_new_string( 339 json_object_new_string(
255 ipc_json_orientation_description(L_NONE))); 340 ipc_json_orientation_description(L_NONE)));
256 json_object_object_add(object, "make",
257 json_object_new_string(wlr_output->make));
258 json_object_object_add(object, "model",
259 json_object_new_string(wlr_output->model));
260 json_object_object_add(object, "serial",
261 json_object_new_string(wlr_output->serial));
262 json_object_object_add(object, "scale", 341 json_object_object_add(object, "scale",
263 json_object_new_double(wlr_output->scale)); 342 json_object_new_double(wlr_output->scale));
264 json_object_object_add(object, "scale_filter", 343 json_object_object_add(object, "scale_filter",
@@ -283,25 +362,26 @@ static void ipc_json_describe_output(struct sway_output *output,
283 json_object *modes_array = json_object_new_array(); 362 json_object *modes_array = json_object_new_array();
284 struct wlr_output_mode *mode; 363 struct wlr_output_mode *mode;
285 wl_list_for_each(mode, &wlr_output->modes, link) { 364 wl_list_for_each(mode, &wlr_output->modes, link) {
286 json_object *mode_object = json_object_new_object(); 365 json_object *mode_object =
287 json_object_object_add(mode_object, "width", 366 ipc_json_output_mode_description(mode);
288 json_object_new_int(mode->width));
289 json_object_object_add(mode_object, "height",
290 json_object_new_int(mode->height));
291 json_object_object_add(mode_object, "refresh",
292 json_object_new_int(mode->refresh));
293 json_object_array_add(modes_array, mode_object); 367 json_object_array_add(modes_array, mode_object);
294 } 368 }
295 369
296 json_object_object_add(object, "modes", modes_array); 370 json_object_object_add(object, "modes", modes_array);
297 371
298 json_object *current_mode_object = json_object_new_object(); 372 json_object *current_mode_object;
299 json_object_object_add(current_mode_object, "width", 373 if (wlr_output->current_mode != NULL) {
300 json_object_new_int(wlr_output->width)); 374 current_mode_object =
301 json_object_object_add(current_mode_object, "height", 375 ipc_json_output_mode_description(wlr_output->current_mode);
302 json_object_new_int(wlr_output->height)); 376 } else {
303 json_object_object_add(current_mode_object, "refresh", 377 current_mode_object = json_object_new_object();
304 json_object_new_int(wlr_output->refresh)); 378 json_object_object_add(current_mode_object, "width",
379 json_object_new_int(wlr_output->width));
380 json_object_object_add(current_mode_object, "height",
381 json_object_new_int(wlr_output->height));
382 json_object_object_add(current_mode_object, "refresh",
383 json_object_new_int(wlr_output->refresh));
384 }
305 json_object_object_add(object, "current_mode", current_mode_object); 385 json_object_object_add(object, "current_mode", current_mode_object);
306 386
307 struct sway_node *parent = node_get_parent(&output->node); 387 struct sway_node *parent = node_get_parent(&output->node);
@@ -325,33 +405,15 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
325 405
326 json_object *object = json_object_new_object(); 406 json_object *object = json_object_new_object();
327 407
408 ipc_json_describe_output(output, object);
409
410 json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
328 json_object_object_add(object, "type", json_object_new_string("output")); 411 json_object_object_add(object, "type", json_object_new_string("output"));
329 json_object_object_add(object, "name", 412 json_object_object_add(object, "name",
330 json_object_new_string(wlr_output->name)); 413 json_object_new_string(wlr_output->name));
331 json_object_object_add(object, "active", json_object_new_boolean(false)); 414 json_object_object_add(object, "active", json_object_new_boolean(false));
332 json_object_object_add(object, "dpms", json_object_new_boolean(false)); 415 json_object_object_add(object, "dpms", json_object_new_boolean(false));
333 json_object_object_add(object, "primary", json_object_new_boolean(false)); 416 json_object_object_add(object, "power", json_object_new_boolean(false));
334 json_object_object_add(object, "make",
335 json_object_new_string(wlr_output->make));
336 json_object_object_add(object, "model",
337 json_object_new_string(wlr_output->model));
338 json_object_object_add(object, "serial",
339 json_object_new_string(wlr_output->serial));
340
341 json_object *modes_array = json_object_new_array();
342 struct wlr_output_mode *mode;
343 wl_list_for_each(mode, &wlr_output->modes, link) {
344 json_object *mode_object = json_object_new_object();
345 json_object_object_add(mode_object, "width",
346 json_object_new_int(mode->width));
347 json_object_object_add(mode_object, "height",
348 json_object_new_int(mode->height));
349 json_object_object_add(mode_object, "refresh",
350 json_object_new_int(mode->refresh));
351 json_object_array_add(modes_array, mode_object);
352 }
353
354 json_object_object_add(object, "modes", modes_array);
355 417
356 json_object_object_add(object, "current_workspace", NULL); 418 json_object_object_add(object, "current_workspace", NULL);
357 419
@@ -367,6 +429,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
367 return object; 429 return object;
368} 430}
369 431
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
370static json_object *ipc_json_describe_scratchpad_output(void) { 447static json_object *ipc_json_describe_scratchpad_output(void) {
371 struct wlr_box box; 448 struct wlr_box box;
372 root_get_box(root, &box); 449 root_get_box(root, &box);
@@ -453,7 +530,9 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
453 530
454static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { 531static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) {
455 enum sway_container_layout parent_layout = container_parent_layout(c); 532 enum sway_container_layout parent_layout = container_parent_layout(c);
456 bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; 533 list_t *siblings = container_get_siblings(c);
534 bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED)
535 && ((siblings && siblings->length > 1) || !config->hide_lone_tab);
457 if (((!tab_or_stack || container_is_floating(c)) && 536 if (((!tab_or_stack || container_is_floating(c)) &&
458 c->current.border != B_NORMAL) || 537 c->current.border != B_NORMAL) ||
459 c->pending.fullscreen_mode != FULLSCREEN_NONE || 538 c->pending.fullscreen_mode != FULLSCREEN_NONE ||
@@ -500,7 +579,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
500 579
501 struct wlr_box window_box = { 580 struct wlr_box window_box = {
502 c->pending.content_x - c->pending.x, 581 c->pending.content_x - c->pending.x,
503 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, 582 (c->current.border == B_PIXEL) ? c->pending.content_y - c->pending.y : 0,
504 c->pending.content_width, 583 c->pending.content_width,
505 c->pending.content_height 584 c->pending.content_height
506 }; 585 };
@@ -544,6 +623,16 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
544 623
545 json_object_object_add(object, "idle_inhibitors", idle_inhibitors); 624 json_object_object_add(object, "idle_inhibitors", idle_inhibitors);
546 625
626 enum wp_content_type_v1_type content_type = WP_CONTENT_TYPE_V1_TYPE_NONE;
627 if (c->view->surface != NULL) {
628 content_type = wlr_surface_get_content_type_v1(server.content_type_manager_v1,
629 c->view->surface);
630 }
631 if (content_type != WP_CONTENT_TYPE_V1_TYPE_NONE) {
632 json_object_object_add(object, "content_type",
633 json_object_new_string(ipc_json_content_type_description(content_type)));
634 }
635
547#if HAVE_XWAYLAND 636#if HAVE_XWAYLAND
548 if (c->view->type == SWAY_VIEW_XWAYLAND) { 637 if (c->view->type == SWAY_VIEW_XWAYLAND) {
549 json_object_object_add(object, "window", 638 json_object_object_add(object, "window",
@@ -588,7 +677,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
588static 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) {
589 json_object_object_add(object, "name", 678 json_object_object_add(object, "name",
590 c->title ? json_object_new_string(c->title) : NULL); 679 c->title ? json_object_new_string(c->title) : NULL);
591 if (container_is_floating(c)) { 680 bool floating = container_is_floating(c);
681 if (floating) {
592 json_object_object_add(object, "type", 682 json_object_object_add(object, "type",
593 json_object_new_string("floating_con")); 683 json_object_new_string("floating_con"));
594 } 684 }
@@ -606,9 +696,17 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
606 json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 696 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
607 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); 697 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
608 698
699 // sway doesn't track the floating reason, so we can't use "auto_on" or "user_off"
700 json_object_object_add(object, "floating",
701 json_object_new_string(floating ? "user_on" : "auto_off"));
702
609 json_object_object_add(object, "fullscreen_mode", 703 json_object_object_add(object, "fullscreen_mode",
610 json_object_new_int(c->pending.fullscreen_mode)); 704 json_object_new_int(c->pending.fullscreen_mode));
611 705
706 // sway doesn't track if window was resized in scratchpad, so we can't use "changed"
707 json_object_object_add(object, "scratchpad_state",
708 json_object_new_string(!c->scratchpad ? "none" : "fresh"));
709
612 struct sway_node *parent = node_get_parent(&c->node); 710 struct sway_node *parent = node_get_parent(&c->node);
613 struct wlr_box parent_box = {0, 0, 0, 0}; 711 struct wlr_box parent_box = {0, 0, 0, 0};
614 712
@@ -706,7 +804,7 @@ json_object *ipc_json_describe_node(struct sway_node *node) {
706 case N_ROOT: 804 case N_ROOT:
707 break; 805 break;
708 case N_OUTPUT: 806 case N_OUTPUT:
709 ipc_json_describe_output(node->sway_output, object); 807 ipc_json_describe_enabled_output(node->sway_output, object);
710 break; 808 break;
711 case N_CONTAINER: 809 case N_CONTAINER:
712 ipc_json_describe_container(node->sway_container, object); 810 ipc_json_describe_container(node->sway_container, object);
@@ -764,6 +862,7 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
764 return object; 862 return object;
765} 863}
766 864
865#if WLR_HAS_LIBINPUT_BACKEND
767static json_object *describe_libinput_device(struct libinput_device *device) { 866static json_object *describe_libinput_device(struct libinput_device *device) {
768 json_object *object = json_object_new_object(); 867 json_object *object = json_object_new_object();
769 868
@@ -847,6 +946,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
847 case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: 946 case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
848 accel_profile = "adaptive"; 947 accel_profile = "adaptive";
849 break; 948 break;
949#if HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM
950 case LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM:
951 accel_profile = "custom";
952 break;
953#endif
850 } 954 }
851 json_object_object_add(object, "accel_profile", 955 json_object_object_add(object, "accel_profile",
852 json_object_new_string(accel_profile)); 956 json_object_new_string(accel_profile));
@@ -926,6 +1030,17 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
926 uint32_t button = libinput_device_config_scroll_get_button(device); 1030 uint32_t button = libinput_device_config_scroll_get_button(device);
927 json_object_object_add(object, "scroll_button", 1031 json_object_object_add(object, "scroll_button",
928 json_object_new_int(button)); 1032 json_object_new_int(button));
1033 const char *lock = "unknown";
1034 switch (libinput_device_config_scroll_get_button_lock(device)) {
1035 case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED:
1036 lock = "enabled";
1037 break;
1038 case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED:
1039 lock = "disabled";
1040 break;
1041 }
1042 json_object_object_add(object, "scroll_button_lock",
1043 json_object_new_string(lock));
929 } 1044 }
930 } 1045 }
931 1046
@@ -942,6 +1057,19 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
942 json_object_object_add(object, "dwt", json_object_new_string(dwt)); 1057 json_object_object_add(object, "dwt", json_object_new_string(dwt));
943 } 1058 }
944 1059
1060 if (libinput_device_config_dwtp_is_available(device)) {
1061 const char *dwtp = "unknown";
1062 switch (libinput_device_config_dwtp_get_enabled(device)) {
1063 case LIBINPUT_CONFIG_DWTP_ENABLED:
1064 dwtp = "enabled";
1065 break;
1066 case LIBINPUT_CONFIG_DWTP_DISABLED:
1067 dwtp = "disabled";
1068 break;
1069 }
1070 json_object_object_add(object, "dwtp", json_object_new_string(dwtp));
1071 }
1072
945 if (libinput_device_config_calibration_has_matrix(device)) { 1073 if (libinput_device_config_calibration_has_matrix(device)) {
946 float matrix[6]; 1074 float matrix[6];
947 libinput_device_config_calibration_get_matrix(device, matrix); 1075 libinput_device_config_calibration_get_matrix(device, matrix);
@@ -956,6 +1084,7 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
956 1084
957 return object; 1085 return object;
958} 1086}
1087#endif
959 1088
960json_object *ipc_json_describe_input(struct sway_input_device *device) { 1089json_object *ipc_json_describe_input(struct sway_input_device *device) {
961 if (!(sway_assert(device, "Device must not be null"))) { 1090 if (!(sway_assert(device, "Device must not be null"))) {
@@ -968,19 +1097,21 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
968 json_object_new_string(device->identifier)); 1097 json_object_new_string(device->identifier));
969 json_object_object_add(object, "name", 1098 json_object_object_add(object, "name",
970 json_object_new_string(device->wlr_device->name)); 1099 json_object_new_string(device->wlr_device->name));
971 json_object_object_add(object, "vendor",
972 json_object_new_int(device->wlr_device->vendor));
973 json_object_object_add(object, "product",
974 json_object_new_int(device->wlr_device->product));
975 json_object_object_add(object, "type", 1100 json_object_object_add(object, "type",
976 json_object_new_string( 1101 json_object_new_string(
977 input_device_get_type(device))); 1102 input_device_get_type(device)));
978 1103
979 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { 1104 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
980 struct wlr_keyboard *keyboard = device->wlr_device->keyboard; 1105 struct wlr_keyboard *keyboard =
1106 wlr_keyboard_from_input_device(device->wlr_device);
981 struct xkb_keymap *keymap = keyboard->keymap; 1107 struct xkb_keymap *keymap = keyboard->keymap;
982 struct xkb_state *state = keyboard->xkb_state; 1108 struct xkb_state *state = keyboard->xkb_state;
983 1109
1110 json_object_object_add(object, "repeat_delay",
1111 json_object_new_int(keyboard->repeat_info.delay));
1112 json_object_object_add(object, "repeat_rate",
1113 json_object_new_int(keyboard->repeat_info.rate));
1114
984 json_object *layouts_arr = json_object_new_array(); 1115 json_object *layouts_arr = json_object_new_array();
985 json_object_object_add(object, "xkb_layout_names", layouts_arr); 1116 json_object_object_add(object, "xkb_layout_names", layouts_arr);
986 1117
@@ -1005,20 +1136,26 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
1005 if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 1136 if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
1006 struct input_config *ic = input_device_get_config(device); 1137 struct input_config *ic = input_device_get_config(device);
1007 float scroll_factor = 1.0f; 1138 float scroll_factor = 1.0f;
1008 if (ic != NULL && !isnan(ic->scroll_factor) && 1139 if (ic != NULL && !isnan(ic->scroll_factor) &&
1009 ic->scroll_factor != FLT_MIN) { 1140 ic->scroll_factor != FLT_MIN) {
1010 scroll_factor = ic->scroll_factor; 1141 scroll_factor = ic->scroll_factor;
1011 } 1142 }
1012 json_object_object_add(object, "scroll_factor", 1143 json_object_object_add(object, "scroll_factor",
1013 json_object_new_double(scroll_factor)); 1144 json_object_new_double(scroll_factor));
1014 } 1145 }
1015 1146
1147#if WLR_HAS_LIBINPUT_BACKEND
1016 if (wlr_input_device_is_libinput(device->wlr_device)) { 1148 if (wlr_input_device_is_libinput(device->wlr_device)) {
1017 struct libinput_device *libinput_dev; 1149 struct libinput_device *libinput_dev;
1018 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); 1150 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
1019 json_object_object_add(object, "libinput", 1151 json_object_object_add(object, "libinput",
1020 describe_libinput_device(libinput_dev)); 1152 describe_libinput_device(libinput_dev));
1153 json_object_object_add(object, "vendor",
1154 json_object_new_int(libinput_device_get_id_vendor(libinput_dev)));
1155 json_object_object_add(object, "product",
1156 json_object_new_int(libinput_device_get_id_product(libinput_dev)));
1021 } 1157 }
1158#endif
1022 1159
1023 return object; 1160 return object;
1024} 1161}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index aad9a7b5..7f353c0e 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -1,5 +1,4 @@
1// See https://i3wm.org/docs/ipc.html for protocol information 1// See https://i3wm.org/docs/ipc.html for protocol information
2#define _POSIX_C_SOURCE 200112L
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <assert.h> 3#include <assert.h>
5#include <errno.h> 4#include <errno.h>
@@ -56,7 +55,6 @@ struct ipc_client {
56 enum ipc_command_type pending_type; 55 enum ipc_command_type pending_type;
57}; 56};
58 57
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);
@@ -150,7 +148,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
150int ipc_handle_connection(int fd, uint32_t mask, void *data) { 148int ipc_handle_connection(int fd, uint32_t mask, void *data) {
151 (void) fd; 149 (void) fd;
152 struct sway_server *server = data; 150 struct sway_server *server = data;
153 sway_log(SWAY_DEBUG, "Event on IPC listening socket");
154 assert(mask == WL_EVENT_READABLE); 151 assert(mask == WL_EVENT_READABLE);
155 152
156 int client_fd = accept(ipc_socket, NULL, NULL); 153 int client_fd = accept(ipc_socket, NULL, NULL);
@@ -211,13 +208,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
211 } 208 }
212 209
213 if (mask & WL_EVENT_HANGUP) { 210 if (mask & WL_EVENT_HANGUP) {
214 sway_log(SWAY_DEBUG, "Client %d hung up", client->fd);
215 ipc_client_disconnect(client); 211 ipc_client_disconnect(client);
216 return 0; 212 return 0;
217 } 213 }
218 214
219 sway_log(SWAY_DEBUG, "Client %d readable", client->fd);
220
221 int read_available; 215 int read_available;
222 if (ioctl(client_fd, FIONREAD, &read_available) == -1) { 216 if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
223 sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); 217 sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size");
@@ -513,6 +507,20 @@ void ipc_event_input(const char *change, struct sway_input_device *device) {
513 json_object_put(json); 507 json_object_put(json);
514} 508}
515 509
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
516int 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) {
517 struct ipc_client *client = data; 525 struct ipc_client *client = data;
518 526
@@ -523,7 +531,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
523 } 531 }
524 532
525 if (mask & WL_EVENT_HANGUP) { 533 if (mask & WL_EVENT_HANGUP) {
526 sway_log(SWAY_DEBUG, "Client %d hung up", client->fd);
527 ipc_client_disconnect(client); 534 ipc_client_disconnect(client);
528 return 0; 535 return 0;
529 } 536 }
@@ -532,8 +539,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
532 return 0; 539 return 0;
533 } 540 }
534 541
535 sway_log(SWAY_DEBUG, "Client %d writable", client->fd);
536
537 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); 542 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
538 543
539 if (written == -1 && errno == EAGAIN) { 544 if (written == -1 && errno == EAGAIN) {
@@ -687,11 +692,17 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
687 } 692 }
688 struct sway_output *output; 693 struct sway_output *output;
689 wl_list_for_each(output, &root->all_outputs, link) { 694 wl_list_for_each(output, &root->all_outputs, link) {
690 if (!output->enabled && output != root->noop_output) { 695 if (!output->enabled && output != root->fallback_output) {
691 json_object_array_add(outputs, 696 json_object_array_add(outputs,
692 ipc_json_describe_disabled_output(output)); 697 ipc_json_describe_disabled_output(output));
693 } 698 }
694 } 699 }
700
701 for (int i = 0; i < root->non_desktop_outputs->length; i++) {
702 struct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i];
703 json_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output));
704 }
705
695 const char *json_string = json_object_to_json_string(outputs); 706 const char *json_string = json_object_to_json_string(outputs);
696 ipc_send_reply(client, payload_type, json_string, 707 ipc_send_reply(client, payload_type, json_string,
697 (uint32_t)strlen(json_string)); 708 (uint32_t)strlen(json_string));
@@ -727,6 +738,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
727 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); 738 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
728 if (strcmp(event_type, "workspace") == 0) { 739 if (strcmp(event_type, "workspace") == 0) {
729 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); 740 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE);
741 } else if (strcmp(event_type, "output") == 0) {
742 client->subscribed_events |= event_mask(IPC_EVENT_OUTPUT);
730 } else if (strcmp(event_type, "barconfig_update") == 0) { 743 } else if (strcmp(event_type, "barconfig_update") == 0) {
731 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); 744 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE);
732 } else if (strcmp(event_type, "bar_state_update") == 0) { 745 } else if (strcmp(event_type, "bar_state_update") == 0) {
@@ -911,7 +924,6 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
911 924
912exit_cleanup: 925exit_cleanup:
913 free(buf); 926 free(buf);
914 return;
915} 927}
916 928
917bool 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,
@@ -955,7 +967,5 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ
955 ipc_client_handle_writable, client); 967 ipc_client_handle_writable, client);
956 } 968 }
957 969
958 sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s",
959 payload_type, client->fd, payload);
960 return true; 970 return true;
961} 971}
diff --git a/sway/lock.c b/sway/lock.c
new file mode 100644
index 00000000..289e8ca4
--- /dev/null
+++ b/sway/lock.c
@@ -0,0 +1,354 @@
1#include <assert.h>
2#include <wlr/types/wlr_scene.h>
3#include <wlr/types/wlr_session_lock_v1.h>
4#include "log.h"
5#include "sway/input/cursor.h"
6#include "sway/input/keyboard.h"
7#include "sway/input/seat.h"
8#include "sway/layers.h"
9#include "sway/output.h"
10#include "sway/server.h"
11
12struct sway_session_lock_output {
13 struct wlr_scene_tree *tree;
14 struct wlr_scene_rect *background;
15 struct sway_session_lock *lock;
16
17 struct sway_output *output;
18
19 struct wl_list link; // sway_session_lock::outputs
20
21 struct wl_listener destroy;
22 struct wl_listener commit;
23
24 struct wlr_session_lock_surface_v1 *surface;
25
26 // invalid if surface is NULL
27 struct wl_listener surface_destroy;
28 struct wl_listener surface_map;
29};
30
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
62static void handle_surface_map(struct wl_listener *listener, void *data) {
63 struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map);
64 if (surf->lock->focused == NULL) {
65 focus_surface(surf->lock, surf->surface->surface);
66 }
67 cursor_rebase_all();
68}
69
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);
119}
120
121static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) {
122 if (output->surface) {
123 refocus_output(output);
124 wl_list_remove(&output->surface_destroy.link);
125 wl_list_remove(&output->surface_map.link);
126 }
127
128 wl_list_remove(&output->commit.link);
129 wl_list_remove(&output->destroy.link);
130 wl_list_remove(&output->link);
131
132 free(output);
133}
134
135static void lock_node_handle_destroy(struct wl_listener *listener, void *data) {
136 struct sway_session_lock_output *output =
137 wl_container_of(listener, output, destroy);
138 sway_session_lock_output_destroy(output);
139}
140
141static void lock_output_handle_commit(struct wl_listener *listener, void *data) {
142 struct wlr_output_event_commit *event = data;
143 struct sway_session_lock_output *output =
144 wl_container_of(listener, output, commit);
145 if (event->state->committed & (
146 WLR_OUTPUT_STATE_MODE |
147 WLR_OUTPUT_STATE_SCALE |
148 WLR_OUTPUT_STATE_TRANSFORM)) {
149 lock_output_reconfigure(output);
150 }
151}
152
153static struct sway_session_lock_output *session_lock_output_create(
154 struct sway_session_lock *lock, struct sway_output *output) {
155 struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output));
156 if (!lock_output) {
157 sway_log(SWAY_ERROR, "failed to allocate a session lock output");
158 return NULL;
159 }
160
161 struct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock);
162 if (!tree) {
163 sway_log(SWAY_ERROR, "failed to allocate a session lock output scene tree");
164 free(lock_output);
165 return NULL;
166 }
167
168 struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){
169 lock->abandoned ? 1.f : 0.f,
170 0.f,
171 0.f,
172 1.f,
173 });
174 if (!background) {
175 sway_log(SWAY_ERROR, "failed to allocate a session lock output scene background");
176 wlr_scene_node_destroy(&tree->node);
177 free(lock_output);
178 return NULL;
179 }
180
181 lock_output->output = output;
182 lock_output->tree = tree;
183 lock_output->background = background;
184 lock_output->lock = lock;
185
186 lock_output->destroy.notify = lock_node_handle_destroy;
187 wl_signal_add(&tree->node.events.destroy, &lock_output->destroy);
188
189 lock_output->commit.notify = lock_output_handle_commit;
190 wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit);
191
192 lock_output_reconfigure(lock_output);
193
194 wl_list_insert(&lock->outputs, &lock_output->link);
195
196 return lock_output;
197}
198
199static void sway_session_lock_destroy(struct sway_session_lock* lock) {
200 struct sway_session_lock_output *lock_output, *tmp_lock_output;
201 wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) {
202 // destroying the node will also destroy the whole lock output
203 wlr_scene_node_destroy(&lock_output->tree->node);
204 }
205
206 if (server.session_lock.lock == lock) {
207 server.session_lock.lock = NULL;
208 }
209
210 if (!lock->abandoned) {
211 wl_list_remove(&lock->destroy.link);
212 wl_list_remove(&lock->unlock.link);
213 wl_list_remove(&lock->new_surface.link);
214 }
215
216 free(lock);
217}
218
219static void handle_unlock(struct wl_listener *listener, void *data) {
220 struct sway_session_lock *lock = wl_container_of(listener, lock, unlock);
221 sway_log(SWAY_DEBUG, "session unlocked");
222
223 sway_session_lock_destroy(lock);
224
225 struct sway_seat *seat;
226 wl_list_for_each(seat, &server.input->seats, link) {
227 // copied from seat_set_focus_layer -- deduplicate?
228 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
229 if (previous) {
230 // Hack to get seat to re-focus the return value of get_focus
231 seat_set_focus(seat, NULL);
232 seat_set_focus(seat, previous);
233 }
234 }
235
236 // Triggers a refocus of the topmost surface layer if necessary
237 // TODO: Make layer surface focus per-output based on cursor position
238 for (int i = 0; i < root->outputs->length; ++i) {
239 struct sway_output *output = root->outputs->items[i];
240 arrange_layers(output);
241 }
242}
243
244static void handle_abandon(struct wl_listener *listener, void *data) {
245 struct sway_session_lock *lock = wl_container_of(listener, lock, destroy);
246 sway_log(SWAY_INFO, "session lock abandoned");
247
248 struct sway_session_lock_output *lock_output;
249 wl_list_for_each(lock_output, &lock->outputs, link) {
250 wlr_scene_rect_set_color(lock_output->background,
251 (float[4]){ 1.f, 0.f, 0.f, 1.f });
252 }
253
254 lock->abandoned = true;
255 wl_list_remove(&lock->destroy.link);
256 wl_list_remove(&lock->unlock.link);
257 wl_list_remove(&lock->new_surface.link);
258}
259
260static void handle_session_lock(struct wl_listener *listener, void *data) {
261 struct wlr_session_lock_v1 *lock = data;
262 struct wl_client *client = wl_resource_get_client(lock->resource);
263
264 if (server.session_lock.lock) {
265 if (server.session_lock.lock->abandoned) {
266 sway_log(SWAY_INFO, "Replacing abandoned lock");
267 sway_session_lock_destroy(server.session_lock.lock);
268 } else {
269 sway_log(SWAY_ERROR, "Cannot lock an already locked session");
270 wlr_session_lock_v1_destroy(lock);
271 return;
272 }
273 }
274
275 struct sway_session_lock *sway_lock = calloc(1, sizeof(*sway_lock));
276 if (!sway_lock) {
277 sway_log(SWAY_ERROR, "failed to allocate a session lock object");
278 wlr_session_lock_v1_destroy(lock);
279 return;
280 }
281
282 wl_list_init(&sway_lock->outputs);
283
284 sway_log(SWAY_DEBUG, "session locked");
285
286 struct sway_seat *seat;
287 wl_list_for_each(seat, &server.input->seats, link) {
288 seat_unfocus_unless_client(seat, client);
289 }
290
291 struct sway_output *output;
292 wl_list_for_each(output, &root->all_outputs, link) {
293 sway_session_lock_add_output(sway_lock, output);
294 }
295
296 sway_lock->new_surface.notify = handle_new_surface;
297 wl_signal_add(&lock->events.new_surface, &sway_lock->new_surface);
298 sway_lock->unlock.notify = handle_unlock;
299 wl_signal_add(&lock->events.unlock, &sway_lock->unlock);
300 sway_lock->destroy.notify = handle_abandon;
301 wl_signal_add(&lock->events.destroy, &sway_lock->destroy);
302
303 wlr_session_lock_v1_send_locked(lock);
304 server.session_lock.lock = sway_lock;
305}
306
307static void handle_session_lock_destroy(struct wl_listener *listener, void *data) {
308 // if the server shuts down while a lock is active, destroy the lock
309 if (server.session_lock.lock) {
310 sway_session_lock_destroy(server.session_lock.lock);
311 }
312
313 wl_list_remove(&server.session_lock.new_lock.link);
314 wl_list_remove(&server.session_lock.manager_destroy.link);
315
316 server.session_lock.manager = NULL;
317}
318
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;
343}
344
345void sway_session_lock_init(void) {
346 server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display);
347
348 server.session_lock.new_lock.notify = handle_session_lock;
349 server.session_lock.manager_destroy.notify = handle_session_lock_destroy;
350 wl_signal_add(&server.session_lock.manager->events.new_lock,
351 &server.session_lock.new_lock);
352 wl_signal_add(&server.session_lock.manager->events.destroy,
353 &server.session_lock.manager_destroy);
354}
diff --git a/sway/main.c b/sway/main.c
index 0611e80b..1c4939aa 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <getopt.h> 1#include <getopt.h>
3#include <pango/pangocairo.h> 2#include <pango/pangocairo.h>
4#include <signal.h> 3#include <signal.h>
@@ -6,6 +5,7 @@
6#include <stdio.h> 5#include <stdio.h>
7#include <stdlib.h> 6#include <stdlib.h>
8#include <string.h> 7#include <string.h>
8#include <sys/resource.h>
9#include <sys/stat.h> 9#include <sys/stat.h>
10#include <sys/types.h> 10#include <sys/types.h>
11#include <sys/wait.h> 11#include <sys/wait.h>
@@ -27,6 +27,7 @@
27 27
28static bool terminate_request = false; 28static bool terminate_request = false;
29static int exit_value = 0; 29static int exit_value = 0;
30static struct rlimit original_nofile_rlimit = {0};
30struct sway_server server = {0}; 31struct sway_server server = {0};
31struct sway_debug debug = {0}; 32struct sway_debug debug = {0};
32 33
@@ -47,44 +48,6 @@ void sig_handler(int signal) {
47 sway_terminate(EXIT_SUCCESS); 48 sway_terminate(EXIT_SUCCESS);
48} 49}
49 50
50void detect_proprietary(int allow_unsupported_gpu) {
51 FILE *f = fopen("/proc/modules", "r");
52 if (!f) {
53 return;
54 }
55 char *line = NULL;
56 size_t line_size = 0;
57 while (getline(&line, &line_size, f) != -1) {
58 if (strncmp(line, "nvidia ", 7) == 0) {
59 if (allow_unsupported_gpu) {
60 sway_log(SWAY_ERROR,
61 "!!! Proprietary Nvidia drivers are in use !!!");
62 } else {
63 sway_log(SWAY_ERROR,
64 "Proprietary Nvidia drivers are NOT supported. "
65 "Use Nouveau. To launch sway anyway, launch with "
66 "--my-next-gpu-wont-be-nvidia and DO NOT report issues.");
67 exit(EXIT_FAILURE);
68 }
69 break;
70 }
71 if (strstr(line, "fglrx")) {
72 if (allow_unsupported_gpu) {
73 sway_log(SWAY_ERROR,
74 "!!! Proprietary AMD drivers are in use !!!");
75 } else {
76 sway_log(SWAY_ERROR, "Proprietary AMD drivers do NOT support "
77 "Wayland. Use radeon. To try anyway, launch sway with "
78 "--unsupported-gpu and DO NOT report issues.");
79 exit(EXIT_FAILURE);
80 }
81 break;
82 }
83 }
84 free(line);
85 fclose(f);
86}
87
88void run_as_ipc_client(char *command, char *socket_path) { 51void run_as_ipc_client(char *command, char *socket_path) {
89 int socketfd = ipc_open_socket(socket_path); 52 int socketfd = ipc_open_socket(socket_path);
90 uint32_t len = strlen(command); 53 uint32_t len = strlen(command);
@@ -148,33 +111,49 @@ static void log_kernel(void) {
148 pclose(f); 111 pclose(f);
149} 112}
150 113
151 114static bool detect_suid(void) {
152static bool drop_permissions(void) { 115 if (geteuid() != 0 && getegid() != 0) {
153 if (getuid() != geteuid() || getgid() != getegid()) { 116 return false;
154 // Set the gid and uid in the correct order.
155 if (setgid(getgid()) != 0) {
156 sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start");
157 return false;
158 }
159 if (setuid(getuid()) != 0) {
160 sway_log(SWAY_ERROR, "Unable to drop root user, refusing to start");
161 return false;
162 }
163 } 117 }
164 if (setgid(0) != -1 || setuid(0) != -1) { 118
165 sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to " 119 if (getuid() == geteuid() && getgid() == getegid()) {
166 "restore it after setuid), refusing to start");
167 return false; 120 return false;
168 } 121 }
122
123 sway_log(SWAY_ERROR, "SUID operation is no longer supported, refusing to start. "
124 "This check will be removed in a future release.");
169 return true; 125 return true;
170} 126}
171 127
128static void increase_nofile_limit(void) {
129 if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
130 sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
131 "getrlimit(NOFILE) failed");
132 return;
133 }
134
135 struct rlimit new_rlimit = original_nofile_rlimit;
136 new_rlimit.rlim_cur = new_rlimit.rlim_max;
137 if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) {
138 sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
139 "setrlimit(NOFILE) failed");
140 sway_log(SWAY_INFO, "Running with %d max open files",
141 (int)original_nofile_rlimit.rlim_cur);
142 }
143}
144
145void restore_nofile_limit(void) {
146 if (original_nofile_rlimit.rlim_cur == 0) {
147 return;
148 }
149 if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
150 sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: "
151 "setrlimit(NOFILE) failed");
152 }
153}
154
172void enable_debug_flag(const char *flag) { 155void enable_debug_flag(const char *flag) {
173 if (strcmp(flag, "damage=highlight") == 0) { 156 if (strcmp(flag, "noatomic") == 0) {
174 debug.damage = DAMAGE_HIGHLIGHT;
175 } else if (strcmp(flag, "damage=rerender") == 0) {
176 debug.damage = DAMAGE_RERENDER;
177 } else if (strcmp(flag, "noatomic") == 0) {
178 debug.noatomic = true; 157 debug.noatomic = true;
179 } else if (strcmp(flag, "txn-wait") == 0) { 158 } else if (strcmp(flag, "txn-wait") == 0) {
180 debug.txn_wait = true; 159 debug.txn_wait = true;
@@ -182,6 +161,8 @@ void enable_debug_flag(const char *flag) {
182 debug.txn_timings = true; 161 debug.txn_timings = true;
183 } else if (strncmp(flag, "txn-timeout=", 12) == 0) { 162 } else if (strncmp(flag, "txn-timeout=", 12) == 0) {
184 server.txn_timeout_ms = atoi(&flag[12]); 163 server.txn_timeout_ms = atoi(&flag[12]);
164 } else if (strcmp(flag, "legacy-wl-drm") == 0) {
165 debug.legacy_wl_drm = true;
185 } else { 166 } else {
186 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); 167 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag);
187 } 168 }
@@ -206,36 +187,35 @@ static void handle_wlr_log(enum wlr_log_importance importance,
206 _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); 187 _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args);
207} 188}
208 189
190static const struct option long_options[] = {
191 {"help", no_argument, NULL, 'h'},
192 {"config", required_argument, NULL, 'c'},
193 {"validate", no_argument, NULL, 'C'},
194 {"debug", no_argument, NULL, 'd'},
195 {"version", no_argument, NULL, 'v'},
196 {"verbose", no_argument, NULL, 'V'},
197 {"get-socketpath", no_argument, NULL, 'p'},
198 {"unsupported-gpu", no_argument, NULL, 'u'},
199 {0, 0, 0, 0}
200};
201
202static const char usage[] =
203 "Usage: sway [options] [command]\n"
204 "\n"
205 " -h, --help Show help message and quit.\n"
206 " -c, --config <config> Specify a config file.\n"
207 " -C, --validate Check the validity of the config file, then exit.\n"
208 " -d, --debug Enables full logging, including debug information.\n"
209 " -v, --version Show the version number and quit.\n"
210 " -V, --verbose Enables more verbose logging.\n"
211 " --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
212 "\n";
213
209int main(int argc, char **argv) { 214int main(int argc, char **argv) {
210 static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; 215 static bool verbose = false, debug = false, validate = false;
211
212 static const struct option long_options[] = {
213 {"help", no_argument, NULL, 'h'},
214 {"config", required_argument, NULL, 'c'},
215 {"validate", no_argument, NULL, 'C'},
216 {"debug", no_argument, NULL, 'd'},
217 {"version", no_argument, NULL, 'v'},
218 {"verbose", no_argument, NULL, 'V'},
219 {"get-socketpath", no_argument, NULL, 'p'},
220 {"unsupported-gpu", no_argument, NULL, 'u'},
221 {"my-next-gpu-wont-be-nvidia", no_argument, NULL, 'u'},
222 {0, 0, 0, 0}
223 };
224 216
225 char *config_path = NULL; 217 char *config_path = NULL;
226 218
227 const char* usage =
228 "Usage: sway [options] [command]\n"
229 "\n"
230 " -h, --help Show help message and quit.\n"
231 " -c, --config <config> Specify a config file.\n"
232 " -C, --validate Check the validity of the config file, then exit.\n"
233 " -d, --debug Enables full logging, including debug information.\n"
234 " -v, --version Show the version number and quit.\n"
235 " -V, --verbose Enables more verbose logging.\n"
236 " --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
237 "\n";
238
239 int c; 219 int c;
240 while (1) { 220 while (1) {
241 int option_index = 0; 221 int option_index = 0;
@@ -253,25 +233,25 @@ int main(int argc, char **argv) {
253 config_path = strdup(optarg); 233 config_path = strdup(optarg);
254 break; 234 break;
255 case 'C': // validate 235 case 'C': // validate
256 validate = 1; 236 validate = true;
257 break; 237 break;
258 case 'd': // debug 238 case 'd': // debug
259 debug = 1; 239 debug = true;
260 break; 240 break;
261 case 'D': // extended debug options 241 case 'D': // extended debug options
262 enable_debug_flag(optarg); 242 enable_debug_flag(optarg);
263 break; 243 break;
264 case 'u': 244 case 'u':
265 allow_unsupported_gpu = 1; 245 allow_unsupported_gpu = true;
266 break; 246 break;
267 case 'v': // version 247 case 'v': // version
268 printf("sway version " SWAY_VERSION "\n"); 248 printf("sway version " SWAY_VERSION "\n");
269 exit(EXIT_SUCCESS); 249 exit(EXIT_SUCCESS);
270 break; 250 break;
271 case 'V': // verbose 251 case 'V': // verbose
272 verbose = 1; 252 verbose = true;
273 break; 253 break;
274 case 'p': ; // --get-socketpath 254 case 'p': // --get-socketpath
275 if (getenv("SWAYSOCK")) { 255 if (getenv("SWAYSOCK")) {
276 printf("%s\n", getenv("SWAYSOCK")); 256 printf("%s\n", getenv("SWAYSOCK"));
277 exit(EXIT_SUCCESS); 257 exit(EXIT_SUCCESS);
@@ -286,6 +266,11 @@ int main(int argc, char **argv) {
286 } 266 }
287 } 267 }
288 268
269 // SUID operation is deprecated, so block it for now.
270 if (detect_suid()) {
271 exit(EXIT_FAILURE);
272 }
273
289 // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the 274 // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the
290 // clear error message (when not running as an IPC client). 275 // clear error message (when not running as an IPC client).
291 if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { 276 if (!getenv("XDG_RUNTIME_DIR") && optind == argc) {
@@ -312,7 +297,6 @@ int main(int argc, char **argv) {
312 log_kernel(); 297 log_kernel();
313 log_distro(); 298 log_distro();
314 log_env(); 299 log_env();
315 detect_proprietary(allow_unsupported_gpu);
316 300
317 if (optind < argc) { // Behave as IPC client 301 if (optind < argc) { // Behave as IPC client
318 if (optind != 1) { 302 if (optind != 1) {
@@ -325,9 +309,6 @@ int main(int argc, char **argv) {
325 "`sway -d 2>sway.log`."); 309 "`sway -d 2>sway.log`.");
326 exit(EXIT_FAILURE); 310 exit(EXIT_FAILURE);
327 } 311 }
328 if (!drop_permissions()) {
329 exit(EXIT_FAILURE);
330 }
331 char *socket_path = getenv("SWAYSOCK"); 312 char *socket_path = getenv("SWAYSOCK");
332 if (!socket_path) { 313 if (!socket_path) {
333 sway_log(SWAY_ERROR, "Unable to retrieve socket path"); 314 sway_log(SWAY_ERROR, "Unable to retrieve socket path");
@@ -339,14 +320,7 @@ int main(int argc, char **argv) {
339 return 0; 320 return 0;
340 } 321 }
341 322
342 if (!server_privileged_prepare(&server)) { 323 increase_nofile_limit();
343 return 1;
344 }
345
346 if (!drop_permissions()) {
347 server_fini(&server);
348 exit(EXIT_FAILURE);
349 }
350 324
351 // handle SIGTERM signals 325 // handle SIGTERM signals
352 signal(SIGTERM, sig_handler); 326 signal(SIGTERM, sig_handler);
@@ -357,12 +331,14 @@ int main(int argc, char **argv) {
357 331
358 sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); 332 sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION);
359 333
360 root = root_create();
361
362 if (!server_init(&server)) { 334 if (!server_init(&server)) {
363 return 1; 335 return 1;
364 } 336 }
365 337
338 if (server.linux_dmabuf_v1) {
339 wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1);
340 }
341
366 if (validate) { 342 if (validate) {
367 bool valid = load_main_config(config_path, false, true); 343 bool valid = load_main_config(config_path, false, true);
368 free(config_path); 344 free(config_path);
@@ -377,6 +353,8 @@ int main(int argc, char **argv) {
377 goto shutdown; 353 goto shutdown;
378 } 354 }
379 355
356 set_rr_scheduling();
357
380 if (!server_start(&server)) { 358 if (!server_start(&server)) {
381 sway_terminate(EXIT_FAILURE); 359 sway_terminate(EXIT_FAILURE);
382 goto shutdown; 360 goto shutdown;
diff --git a/sway/meson.build b/sway/meson.build
index 1402db15..d937e425 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -5,25 +5,26 @@ sway_sources = files(
5 'decoration.c', 5 'decoration.c',
6 'ipc-json.c', 6 'ipc-json.c',
7 'ipc-server.c', 7 'ipc-server.c',
8 'lock.c',
8 'main.c', 9 'main.c',
10 'realtime.c',
11 'scene_descriptor.c',
9 'server.c', 12 'server.c',
13 'sway_text_node.c',
10 'swaynag.c', 14 'swaynag.c',
11 'xdg_activation_v1.c', 15 'xdg_activation_v1.c',
12 'xdg_decoration.c', 16 'xdg_decoration.c',
13 17
14 'desktop/desktop.c',
15 'desktop/idle_inhibit_v1.c', 18 'desktop/idle_inhibit_v1.c',
16 'desktop/layer_shell.c', 19 'desktop/layer_shell.c',
17 'desktop/output.c', 20 'desktop/output.c',
18 'desktop/render.c',
19 'desktop/surface.c',
20 'desktop/transaction.c', 21 'desktop/transaction.c',
21 'desktop/xdg_shell.c', 22 'desktop/xdg_shell.c',
23 'desktop/launcher.c',
22 24
23 'input/input-manager.c', 25 'input/input-manager.c',
24 'input/cursor.c', 26 'input/cursor.c',
25 'input/keyboard.c', 27 'input/keyboard.c',
26 'input/libinput.c',
27 'input/seat.c', 28 'input/seat.c',
28 'input/seatop_default.c', 29 'input/seatop_default.c',
29 'input/seatop_down.c', 30 'input/seatop_down.c',
@@ -65,6 +66,7 @@ sway_sources = files(
65 'commands/force_focus_wrapping.c', 66 'commands/force_focus_wrapping.c',
66 'commands/fullscreen.c', 67 'commands/fullscreen.c',
67 'commands/gaps.c', 68 'commands/gaps.c',
69 'commands/gesture.c',
68 'commands/hide_edge_borders.c', 70 'commands/hide_edge_borders.c',
69 'commands/inhibit_idle.c', 71 'commands/inhibit_idle.c',
70 'commands/kill.c', 72 'commands/kill.c',
@@ -83,6 +85,7 @@ sway_sources = files(
83 'commands/nop.c', 85 'commands/nop.c',
84 'commands/output.c', 86 'commands/output.c',
85 'commands/popup_during_fullscreen.c', 87 'commands/popup_during_fullscreen.c',
88 'commands/primary_selection.c',
86 'commands/reload.c', 89 'commands/reload.c',
87 'commands/rename.c', 90 'commands/rename.c',
88 'commands/resize.c', 91 'commands/resize.c',
@@ -154,6 +157,7 @@ sway_sources = files(
154 'commands/input/drag.c', 157 'commands/input/drag.c',
155 'commands/input/drag_lock.c', 158 'commands/input/drag_lock.c',
156 'commands/input/dwt.c', 159 'commands/input/dwt.c',
160 'commands/input/dwtp.c',
157 'commands/input/events.c', 161 'commands/input/events.c',
158 'commands/input/left_handed.c', 162 'commands/input/left_handed.c',
159 'commands/input/map_from_region.c', 163 'commands/input/map_from_region.c',
@@ -162,9 +166,11 @@ sway_sources = files(
162 'commands/input/middle_emulation.c', 166 'commands/input/middle_emulation.c',
163 'commands/input/natural_scroll.c', 167 'commands/input/natural_scroll.c',
164 'commands/input/pointer_accel.c', 168 'commands/input/pointer_accel.c',
169 'commands/input/rotation_angle.c',
165 'commands/input/repeat_delay.c', 170 'commands/input/repeat_delay.c',
166 'commands/input/repeat_rate.c', 171 'commands/input/repeat_rate.c',
167 'commands/input/scroll_button.c', 172 'commands/input/scroll_button.c',
173 'commands/input/scroll_button_lock.c',
168 'commands/input/scroll_factor.c', 174 'commands/input/scroll_factor.c',
169 'commands/input/scroll_method.c', 175 'commands/input/scroll_method.c',
170 'commands/input/tap.c', 176 'commands/input/tap.c',
@@ -188,11 +194,14 @@ sway_sources = files(
188 'commands/output/max_render_time.c', 194 'commands/output/max_render_time.c',
189 'commands/output/mode.c', 195 'commands/output/mode.c',
190 'commands/output/position.c', 196 'commands/output/position.c',
197 'commands/output/power.c',
198 'commands/output/render_bit_depth.c',
191 'commands/output/scale.c', 199 'commands/output/scale.c',
192 'commands/output/scale_filter.c', 200 'commands/output/scale_filter.c',
193 'commands/output/subpixel.c', 201 'commands/output/subpixel.c',
194 'commands/output/toggle.c', 202 'commands/output/toggle.c',
195 'commands/output/transform.c', 203 'commands/output/transform.c',
204 'commands/output/unplug.c',
196 205
197 'tree/arrange.c', 206 'tree/arrange.c',
198 'tree/container.c', 207 'tree/container.c',
@@ -212,23 +221,27 @@ sway_deps = [
212 libudev, 221 libudev,
213 math, 222 math,
214 pango, 223 pango,
215 pcre, 224 pcre2,
216 glesv2,
217 pixman, 225 pixman,
218 server_protos, 226 threads,
219 wayland_server, 227 wayland_server,
220 wlroots, 228 wlroots,
221 xkbcommon, 229 xkbcommon,
230 xcb,
231 xcb_icccm,
222] 232]
223 233
224if have_xwayland 234if have_xwayland
225 sway_sources += 'desktop/xwayland.c' 235 sway_sources += 'desktop/xwayland.c'
226 sway_deps += xcb 236endif
237
238if wlroots_features['libinput_backend']
239 sway_sources += 'input/libinput.c'
227endif 240endif
228 241
229executable( 242executable(
230 'sway', 243 'sway',
231 sway_sources, 244 sway_sources + wl_protos_src,
232 include_directories: [sway_inc], 245 include_directories: [sway_inc],
233 dependencies: sway_deps, 246 dependencies: sway_deps,
234 link_with: [lib_sway_common], 247 link_with: [lib_sway_common],
diff --git a/sway/realtime.c b/sway/realtime.c
new file mode 100644
index 00000000..11154af0
--- /dev/null
+++ b/sway/realtime.c
@@ -0,0 +1,40 @@
1#include <sys/resource.h>
2#include <sched.h>
3#include <unistd.h>
4#include <pthread.h>
5#include "sway/server.h"
6#include "log.h"
7
8static void child_fork_callback(void) {
9 struct sched_param param;
10
11 param.sched_priority = 0;
12
13 int ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, &param);
14 if (ret != 0) {
15 sway_log(SWAY_ERROR, "Failed to reset scheduler policy on fork");
16 }
17}
18
19void set_rr_scheduling(void) {
20 int prio = sched_get_priority_min(SCHED_RR);
21 int old_policy;
22 int ret;
23 struct sched_param param;
24
25 ret = pthread_getschedparam(pthread_self(), &old_policy, &param);
26 if (ret != 0) {
27 sway_log(SWAY_DEBUG, "Failed to get old scheduling priority");
28 return;
29 }
30
31 param.sched_priority = prio;
32
33 ret = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
34 if (ret != 0) {
35 sway_log(SWAY_INFO, "Failed to set scheduling priority to %d", prio);
36 return;
37 }
38
39 pthread_atfork(NULL, NULL, child_fork_callback);
40}
diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c
new file mode 100644
index 00000000..a30d4664
--- /dev/null
+++ b/sway/scene_descriptor.c
@@ -0,0 +1,66 @@
1#include <stdlib.h>
2#include <wlr/util/addon.h>
3#include "log.h"
4#include "sway/scene_descriptor.h"
5
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 2e5ab104..180d3a6b 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -7,21 +6,35 @@
7#include <wlr/backend.h> 6#include <wlr/backend.h>
8#include <wlr/backend/headless.h> 7#include <wlr/backend/headless.h>
9#include <wlr/backend/multi.h> 8#include <wlr/backend/multi.h>
10#include <wlr/backend/noop.h>
11#include <wlr/backend/session.h>
12#include <wlr/config.h> 9#include <wlr/config.h>
10#include <wlr/render/allocator.h>
13#include <wlr/render/wlr_renderer.h> 11#include <wlr/render/wlr_renderer.h>
14#include <wlr/types/wlr_compositor.h> 12#include <wlr/types/wlr_compositor.h>
13#include <wlr/types/wlr_content_type_v1.h>
14#include <wlr/types/wlr_cursor_shape_v1.h>
15#include <wlr/types/wlr_data_control_v1.h> 15#include <wlr/types/wlr_data_control_v1.h>
16#include <wlr/types/wlr_data_device.h>
17#include <wlr/types/wlr_drm.h>
16#include <wlr/types/wlr_export_dmabuf_v1.h> 18#include <wlr/types/wlr_export_dmabuf_v1.h>
19#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
20#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
21#include <wlr/types/wlr_fractional_scale_v1.h>
17#include <wlr/types/wlr_gamma_control_v1.h> 22#include <wlr/types/wlr_gamma_control_v1.h>
18#include <wlr/types/wlr_idle.h> 23#include <wlr/types/wlr_idle_notify_v1.h>
19#include <wlr/types/wlr_layer_shell_v1.h> 24#include <wlr/types/wlr_layer_shell_v1.h>
25#include <wlr/types/wlr_linux_dmabuf_v1.h>
26#include <wlr/types/wlr_output_management_v1.h>
27#include <wlr/types/wlr_output_power_management_v1.h>
20#include <wlr/types/wlr_pointer_constraints_v1.h> 28#include <wlr/types/wlr_pointer_constraints_v1.h>
29#include <wlr/types/wlr_presentation_time.h>
21#include <wlr/types/wlr_primary_selection_v1.h> 30#include <wlr/types/wlr_primary_selection_v1.h>
22#include <wlr/types/wlr_relative_pointer_v1.h> 31#include <wlr/types/wlr_relative_pointer_v1.h>
23#include <wlr/types/wlr_screencopy_v1.h> 32#include <wlr/types/wlr_screencopy_v1.h>
33#include <wlr/types/wlr_security_context_v1.h>
24#include <wlr/types/wlr_server_decoration.h> 34#include <wlr/types/wlr_server_decoration.h>
35#include <wlr/types/wlr_session_lock_v1.h>
36#include <wlr/types/wlr_single_pixel_buffer_v1.h>
37#include <wlr/types/wlr_subcompositor.h>
25#include <wlr/types/wlr_tablet_v2.h> 38#include <wlr/types/wlr_tablet_v2.h>
26#include <wlr/types/wlr_viewporter.h> 39#include <wlr/types/wlr_viewporter.h>
27#include <wlr/types/wlr_xcursor_manager.h> 40#include <wlr/types/wlr_xcursor_manager.h>
@@ -31,6 +44,7 @@
31#include <wlr/types/wlr_xdg_foreign_v1.h> 44#include <wlr/types/wlr_xdg_foreign_v1.h>
32#include <wlr/types/wlr_xdg_foreign_v2.h> 45#include <wlr/types/wlr_xdg_foreign_v2.h>
33#include <wlr/types/wlr_xdg_output_v1.h> 46#include <wlr/types/wlr_xdg_output_v1.h>
47#include <xf86drm.h>
34#include "config.h" 48#include "config.h"
35#include "list.h" 49#include "list.h"
36#include "log.h" 50#include "log.h"
@@ -39,41 +53,221 @@
39#include "sway/input/input-manager.h" 53#include "sway/input/input-manager.h"
40#include "sway/output.h" 54#include "sway/output.h"
41#include "sway/server.h" 55#include "sway/server.h"
56#include "sway/input/cursor.h"
42#include "sway/tree/root.h" 57#include "sway/tree/root.h"
58
43#if HAVE_XWAYLAND 59#if HAVE_XWAYLAND
60#include <wlr/xwayland/shell.h>
44#include "sway/xwayland.h" 61#include "sway/xwayland.h"
45#endif 62#endif
46 63
47bool server_privileged_prepare(struct sway_server *server) { 64#if WLR_HAS_DRM_BACKEND
48 sway_log(SWAY_DEBUG, "Preparing Wayland server initialization"); 65#include <wlr/types/wlr_drm_lease_v1.h>
66#endif
67
68#define SWAY_XDG_SHELL_VERSION 5
69#define SWAY_LAYER_SHELL_VERSION 4
70#define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1
71
72bool allow_unsupported_gpu = false;
73
74#if WLR_HAS_DRM_BACKEND
75static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
76 /* We only offer non-desktop outputs, but in the future we might want to do
77 * more logic here. */
78
79 struct wlr_drm_lease_request_v1 *req = data;
80 struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req);
81 if (!lease) {
82 sway_log(SWAY_ERROR, "Failed to grant lease request");
83 wlr_drm_lease_request_v1_reject(req);
84 }
85}
86#endif
87
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;
199
200 wl_list_remove(&server->renderer_lost.link);
201 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
202
203 wlr_compositor_set_renderer(server->compositor, renderer);
204
205 for (int i = 0; i < root->outputs->length; ++i) {
206 struct sway_output *output = root->outputs->items[i];
207 wlr_output_init_render(output->wlr_output,
208 server->allocator, server->renderer);
209 }
210
211 wlr_allocator_destroy(old_allocator);
212 wlr_renderer_destroy(old_renderer);
213}
214
215bool server_init(struct sway_server *server) {
216 sway_log(SWAY_DEBUG, "Initializing Wayland server");
49 server->wl_display = wl_display_create(); 217 server->wl_display = wl_display_create();
50 server->wl_event_loop = wl_display_get_event_loop(server->wl_display); 218 server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
51 server->backend = wlr_backend_autocreate(server->wl_display);
52 219
220 wl_display_set_global_filter(server->wl_display, filter_global, NULL);
221
222 root = root_create(server->wl_display);
223
224 server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session);
53 if (!server->backend) { 225 if (!server->backend) {
54 sway_log(SWAY_ERROR, "Unable to create backend"); 226 sway_log(SWAY_ERROR, "Unable to create backend");
55 return false; 227 return false;
56 } 228 }
57 return true;
58}
59 229
60bool server_init(struct sway_server *server) { 230 wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL);
61 sway_log(SWAY_DEBUG, "Initializing Wayland server"); 231
232 server->renderer = wlr_renderer_autocreate(server->backend);
233 if (!server->renderer) {
234 sway_log(SWAY_ERROR, "Failed to create renderer");
235 return false;
236 }
237
238 server->renderer_lost.notify = handle_renderer_lost;
239 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
62 240
63 struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); 241 wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
64 assert(renderer);
65 242
66 wlr_renderer_init_wl_display(renderer, server->wl_display); 243 if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) {
244 server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(
245 server->wl_display, 4, server->renderer);
246 if (debug.legacy_wl_drm) {
247 wlr_drm_create(server->wl_display, server->renderer);
248 }
249 }
250
251 server->allocator = wlr_allocator_autocreate(server->backend,
252 server->renderer);
253 if (!server->allocator) {
254 sway_log(SWAY_ERROR, "Failed to create allocator");
255 return false;
256 }
257
258 server->compositor = wlr_compositor_create(server->wl_display, 6,
259 server->renderer);
67 260
68 server->compositor = wlr_compositor_create(server->wl_display, renderer); 261 wlr_subcompositor_create(server->wl_display);
69 server->compositor_new_surface.notify = handle_compositor_new_surface;
70 wl_signal_add(&server->compositor->events.new_surface,
71 &server->compositor_new_surface);
72 262
73 server->data_device_manager = 263 server->data_device_manager =
74 wlr_data_device_manager_create(server->wl_display); 264 wlr_data_device_manager_create(server->wl_display);
75 265
76 wlr_gamma_control_manager_v1_create(server->wl_display); 266 server->gamma_control_manager_v1 =
267 wlr_gamma_control_manager_v1_create(server->wl_display);
268 server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma;
269 wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma,
270 &server->gamma_control_set_gamma);
77 271
78 server->new_output.notify = handle_new_output; 272 server->new_output.notify = handle_new_output;
79 wl_signal_add(&server->backend->events.new_output, &server->new_output); 273 wl_signal_add(&server->backend->events.new_output, &server->new_output);
@@ -83,19 +277,20 @@ bool server_init(struct sway_server *server) {
83 277
84 wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); 278 wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout);
85 279
86 server->idle = wlr_idle_create(server->wl_display); 280 server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display);
87 server->idle_inhibit_manager_v1 = 281 sway_idle_inhibit_manager_v1_init();
88 sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle);
89 282
90 server->layer_shell = wlr_layer_shell_v1_create(server->wl_display); 283 server->layer_shell = wlr_layer_shell_v1_create(server->wl_display,
284 SWAY_LAYER_SHELL_VERSION);
91 wl_signal_add(&server->layer_shell->events.new_surface, 285 wl_signal_add(&server->layer_shell->events.new_surface,
92 &server->layer_shell_surface); 286 &server->layer_shell_surface);
93 server->layer_shell_surface.notify = handle_layer_shell_surface; 287 server->layer_shell_surface.notify = handle_layer_shell_surface;
94 288
95 server->xdg_shell = wlr_xdg_shell_create(server->wl_display); 289 server->xdg_shell = wlr_xdg_shell_create(server->wl_display,
96 wl_signal_add(&server->xdg_shell->events.new_surface, 290 SWAY_XDG_SHELL_VERSION);
97 &server->xdg_shell_surface); 291 wl_signal_add(&server->xdg_shell->events.new_toplevel,
98 server->xdg_shell_surface.notify = handle_xdg_shell_surface; 292 &server->xdg_shell_toplevel);
293 server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel;
99 294
100 server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); 295 server->tablet_v2 = wlr_tablet_v2_create(server->wl_display);
101 296
@@ -126,8 +321,7 @@ bool server_init(struct sway_server *server) {
126 wl_signal_add(&server->pointer_constraints->events.new_constraint, 321 wl_signal_add(&server->pointer_constraints->events.new_constraint,
127 &server->pointer_constraint); 322 &server->pointer_constraint);
128 323
129 server->presentation = 324 wlr_presentation_create(server->wl_display, server->backend);
130 wlr_presentation_create(server->wl_display, server->backend);
131 325
132 server->output_manager_v1 = 326 server->output_manager_v1 =
133 wlr_output_manager_v1_create(server->wl_display); 327 wlr_output_manager_v1_create(server->wl_display);
@@ -146,14 +340,35 @@ bool server_init(struct sway_server *server) {
146 &server->output_power_manager_set_mode); 340 &server->output_power_manager_set_mode);
147 server->input_method = wlr_input_method_manager_v2_create(server->wl_display); 341 server->input_method = wlr_input_method_manager_v2_create(server->wl_display);
148 server->text_input = wlr_text_input_manager_v3_create(server->wl_display); 342 server->text_input = wlr_text_input_manager_v3_create(server->wl_display);
343 server->foreign_toplevel_list =
344 wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION);
149 server->foreign_toplevel_manager = 345 server->foreign_toplevel_manager =
150 wlr_foreign_toplevel_manager_v1_create(server->wl_display); 346 wlr_foreign_toplevel_manager_v1_create(server->wl_display);
151 347
152 wlr_export_dmabuf_manager_v1_create(server->wl_display); 348 sway_session_lock_init();
153 wlr_screencopy_manager_v1_create(server->wl_display); 349
154 wlr_data_control_manager_v1_create(server->wl_display); 350#if WLR_HAS_DRM_BACKEND
155 wlr_primary_selection_v1_device_manager_create(server->wl_display); 351 server->drm_lease_manager=
352 wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
353 if (server->drm_lease_manager) {
354 server->drm_lease_request.notify = handle_drm_lease_request;
355 wl_signal_add(&server->drm_lease_manager->events.request,
356 &server->drm_lease_request);
357 } else {
358 sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1");
359 sway_log(SWAY_INFO, "VR will not be available");
360 }
361#endif
362
363 server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display);
364 server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display);
365 server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display);
366 server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display);
156 wlr_viewporter_create(server->wl_display); 367 wlr_viewporter_create(server->wl_display);
368 wlr_single_pixel_buffer_manager_v1_create(server->wl_display);
369 server->content_type_manager_v1 =
370 wlr_content_type_manager_v1_create(server->wl_display, 1);
371 wlr_fractional_scale_manager_v1_create(server->wl_display, 1);
157 372
158 struct wlr_xdg_foreign_registry *foreign_registry = 373 struct wlr_xdg_foreign_registry *foreign_registry =
159 wlr_xdg_foreign_registry_create(server->wl_display); 374 wlr_xdg_foreign_registry_create(server->wl_display);
@@ -165,11 +380,22 @@ bool server_init(struct sway_server *server) {
165 xdg_activation_v1_handle_request_activate; 380 xdg_activation_v1_handle_request_activate;
166 wl_signal_add(&server->xdg_activation_v1->events.request_activate, 381 wl_signal_add(&server->xdg_activation_v1->events.request_activate,
167 &server->xdg_activation_v1_request_activate); 382 &server->xdg_activation_v1_request_activate);
383 server->xdg_activation_v1_new_token.notify =
384 xdg_activation_v1_handle_new_token;
385 wl_signal_add(&server->xdg_activation_v1->events.new_token,
386 &server->xdg_activation_v1_new_token);
387
388 struct wlr_cursor_shape_manager_v1 *cursor_shape_manager =
389 wlr_cursor_shape_manager_v1_create(server->wl_display, 1);
390 server->request_set_cursor_shape.notify = handle_request_set_cursor_shape;
391 wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape);
392
393 wl_list_init(&server->pending_launcher_ctxs);
168 394
169 // Avoid using "wayland-0" as display socket 395 // Avoid using "wayland-0" as display socket
170 char name_candidate[16]; 396 char name_candidate[16];
171 for (int i = 1; i <= 32; ++i) { 397 for (unsigned int i = 1; i <= 32; ++i) {
172 sprintf(name_candidate, "wayland-%d", i); 398 snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i);
173 if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { 399 if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) {
174 server->socket = strdup(name_candidate); 400 server->socket = strdup(name_candidate);
175 break; 401 break;
@@ -182,20 +408,20 @@ bool server_init(struct sway_server *server) {
182 return false; 408 return false;
183 } 409 }
184 410
185 server->noop_backend = wlr_noop_backend_create(server->wl_display); 411 server->headless_backend = wlr_headless_backend_create(server->wl_event_loop);
186
187 struct wlr_output *wlr_output = wlr_noop_add_output(server->noop_backend);
188 root->noop_output = output_create(wlr_output);
189
190 server->headless_backend =
191 wlr_headless_backend_create_with_renderer(server->wl_display, renderer);
192 if (!server->headless_backend) { 412 if (!server->headless_backend) {
193 sway_log(SWAY_INFO, "Failed to create secondary headless backend, " 413 sway_log(SWAY_ERROR, "Failed to create secondary headless backend");
194 "starting without it"); 414 wlr_backend_destroy(server->backend);
415 return false;
195 } else { 416 } else {
196 wlr_multi_backend_add(server->backend, server->headless_backend); 417 wlr_multi_backend_add(server->backend, server->headless_backend);
197 } 418 }
198 419
420 struct wlr_output *wlr_output =
421 wlr_headless_add_output(server->headless_backend, 800, 600);
422 wlr_output_set_name(wlr_output, "FALLBACK");
423 root->fallback_output = output_create(wlr_output);
424
199 // This may have been set already via -Dtxn-timeout 425 // This may have been set already via -Dtxn-timeout
200 if (!server->txn_timeout_ms) { 426 if (!server->txn_timeout_ms) {
201 server->txn_timeout_ms = 200; 427 server->txn_timeout_ms = 200;
@@ -215,6 +441,7 @@ void server_fini(struct sway_server *server) {
215 wlr_xwayland_destroy(server->xwayland.wlr_xwayland); 441 wlr_xwayland_destroy(server->xwayland.wlr_xwayland);
216#endif 442#endif
217 wl_display_destroy_clients(server->wl_display); 443 wl_display_destroy_clients(server->wl_display);
444 wlr_backend_destroy(server->backend);
218 wl_display_destroy(server->wl_display); 445 wl_display_destroy(server->wl_display);
219 list_free(server->dirty_nodes); 446 list_free(server->dirty_nodes);
220} 447}
@@ -245,6 +472,10 @@ bool server_start(struct sway_server *server) {
245 } 472 }
246#endif 473#endif
247 474
475 if (config->primary_selection) {
476 wlr_primary_selection_v1_device_manager_create(server->wl_display);
477 }
478
248 sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", 479 sway_log(SWAY_INFO, "Starting backend on wayland display '%s'",
249 server->socket); 480 server->socket);
250 if (!wlr_backend_start(server->backend)) { 481 if (!wlr_backend_start(server->backend)) {
@@ -252,6 +483,7 @@ bool server_start(struct sway_server *server) {
252 wlr_backend_destroy(server->backend); 483 wlr_backend_destroy(server->backend);
253 return false; 484 return false;
254 } 485 }
486
255 return true; 487 return true;
256} 488}
257 489
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd
index 80d08449..42e59d57 100644
--- a/sway/sway-bar.5.scd
+++ b/sway/sway-bar.5.scd
@@ -40,7 +40,7 @@ runtime.
40*font* <font> 40*font* <font>
41 Specifies the font to be used in the bar. _font_ should be specified as a 41 Specifies the font to be used in the bar. _font_ should be specified as a
42 pango font description. For more information on pango font descriptions, 42 pango font description. For more information on pango font descriptions,
43 see https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string 43 see https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description
44 44
45*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left> 45*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left>
46 Sets the gaps from the edge of the screen for the bar. Gaps can either be 46 Sets the gaps from the edge of the screen for the bar. Gaps can either be
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index 25f6de18..442311bb 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -121,11 +121,16 @@ The following commands may only be used in the configuration file.
121 121
122*input* <identifier> map_from_region <X1xY1> <X2xY2> 122*input* <identifier> map_from_region <X1xY1> <X2xY2>
123 Ignores inputs from this device that do not occur within the specified 123 Ignores inputs from this device that do not occur within the specified
124 region. Can be in millimeters (e.g. 10x20mm 20x40mm) or in terms of 0..1 124 region. Can be in millimeters (e.g. 10x20mm 20x40mm) or the fraction of the
125 (e.g. 0.5x0.5 0.7x0.7). Not all devices support millimeters. Only meaningful 125 full available space in terms of 0..1 (e.g. 0.5x0.5 0.7x0.7). Not all
126 if the device is not a keyboard and provides events in absolute terms (such 126 devices support millimeters. Only meaningful if the device is not a
127 as a drawing tablet or touch screen - most pointers provide events relative 127 keyboard and provides events in absolute terms (such as a drawing tablet
128 to the previous frame). 128 or touch screen - most pointers provide events relative to the previous
129 frame).
130
131 Commonly used to maintain the aspect ratio of the input device and screen.
132 Cropping a 16:10 input region to match a 16:9 display can use 0x0 1x0.9 as
133 the argument.
129 134
130## LIBINPUT CONFIGURATION 135## LIBINPUT CONFIGURATION
131 136
@@ -147,6 +152,10 @@ The following commands may only be used in the configuration file.
147*input* <identifier> dwt enabled|disabled 152*input* <identifier> dwt enabled|disabled
148 Enables or disables disable-while-typing for the specified input device. 153 Enables or disables disable-while-typing for the specified input device.
149 154
155*input* <identifier> dwtp enabled|disabled
156 Enables or disables disable-while-trackpointing for the specified input
157 device.
158
150*input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>] 159*input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>]
151 Enables or disables send_events for specified input device. Disabling 160 Enables or disables send_events for specified input device. Disabling
152 send_events disables the input device. 161 send_events disables the input device.
@@ -171,12 +180,19 @@ The following commands may only be used in the configuration file.
171*input* <identifier> pointer_accel [<-1|1>] 180*input* <identifier> pointer_accel [<-1|1>]
172 Changes the pointer acceleration for the specified input device. 181 Changes the pointer acceleration for the specified input device.
173 182
183*input* <identifier> rotation_angle <angle>
184 Sets the rotation angle of the device to the given clockwise angle in
185 degrees. The angle must be between 0.0 (inclusive) and 360.0 (exclusive).
186
174*input* <identifier> scroll_button disable|button[1-3,8,9]|<event-code-or-name> 187*input* <identifier> scroll_button disable|button[1-3,8,9]|<event-code-or-name>
175 Sets the button used for scroll_method on_button_down. The button can 188 Sets the button used for scroll_method on_button_down. The button can
176 be given as an event name or code, which can be obtained from *libinput 189 be given as an event name or code, which can be obtained from *libinput
177 debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to 190 debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to
178 _disable_, it disables the scroll_method on_button_down. 191 _disable_, it disables the scroll_method on_button_down.
179 192
193*input* <identifier> scroll_button_lock enabled|disabled
194 Enables or disables scroll button lock for specified input device.
195
180*input* <identifier> scroll_factor <floating point value> 196*input* <identifier> scroll_factor <floating point value>
181 Changes the scroll factor for the specified input device. Scroll speed will 197 Changes the scroll factor for the specified input device. Scroll speed will
182 be scaled by the given value, which must be non-negative. 198 be scaled by the given value, which must be non-negative.
@@ -220,6 +236,8 @@ correct seat.
220 absolute coordinates (with respect to the global coordinate space). 236 absolute coordinates (with respect to the global coordinate space).
221 Specifying either value as 0 will not update that coordinate. 237 Specifying either value as 0 will not update that coordinate.
222 238
239 Deprecated: use the virtual-pointer Wayland protocol instead.
240
223*seat* <seat> cursor press|release button[1-9]|<event-name-or-code> 241*seat* <seat> cursor press|release button[1-9]|<event-name-or-code>
224 Simulate pressing (or releasing) the specified mouse button on the 242 Simulate pressing (or releasing) the specified mouse button on the
225 specified seat. The button can either be provided as a button event name or 243 specified seat. The button can either be provided as a button event name or
@@ -228,12 +246,14 @@ correct seat.
228 event will be simulated, however _press_ and _release_ will be ignored and 246 event will be simulated, however _press_ and _release_ will be ignored and
229 both will occur. 247 both will occur.
230 248
249 Deprecated: use the virtual-pointer Wayland protocol instead.
250
231*seat* <name> fallback true|false 251*seat* <name> fallback true|false
232 Set this seat as the fallback seat. A fallback seat will attach any device 252 Set this seat as the fallback seat. A fallback seat will attach any device
233 not explicitly attached to another seat (similar to a "default" seat). 253 not explicitly attached to another seat (similar to a "default" seat).
234 254
235*seat* <name> hide_cursor <timeout>|when-typing [enable|disable] 255*seat* <name> hide_cursor <timeout>|when-typing [enable|disable]
236 Hides the cursor image after the specified event occured. 256 Hides the cursor image after the specified event occurred.
237 257
238 If _timeout_ is specified, then the cursor will be hidden after _timeout_ 258 If _timeout_ is specified, then the cursor will be hidden after _timeout_
239 (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0 259 (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0
@@ -243,18 +263,16 @@ correct seat.
243 If _when-typing_ is enabled, then the cursor will be hidden whenever a key 263 If _when-typing_ is enabled, then the cursor will be hidden whenever a key
244 is pressed. 264 is pressed.
245 265
266 Be aware that this setting can interfere with input handling in games and
267 certain types of software (Gimp, Blender etc) that rely on simultaneous
268 input from mouse and keyboard.
269
246*seat* <name> idle_inhibit <sources...> 270*seat* <name> idle_inhibit <sources...>
247 Sets the set of input event sources which can prevent the seat from 271 Sets the set of input event sources which can prevent the seat from
248 becoming idle, as a space separated list of source names. Valid names are 272 becoming idle, as a space separated list of source names. Valid names are
249 "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", 273 "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool",
250 and "switch". The default behavior is to prevent idle on any event. 274 and "switch". The default behavior is to prevent idle on any event.
251 275
252*seat* <name> idle_wake <sources...>
253 Sets the set of input event sources which can wake the seat from
254 its idle state, as a space separated list of source names. Valid names are
255 "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool",
256 and "switch". The default behavior is to wake from idle on any event.
257
258*seat* <name> keyboard_grouping none|smart 276*seat* <name> keyboard_grouping none|smart
259 Set how the keyboards in the seat are grouped together. Currently, there 277 Set how the keyboards in the seat are grouped together. Currently, there
260 are two options. _none_ will disable all keyboard grouping. This will make 278 are two options. _none_ will disable all keyboard grouping. This will make
@@ -277,7 +295,7 @@ correct seat.
277 whether future inhibitors are honoured by default, i.e. activated 295 whether future inhibitors are honoured by default, i.e. activated
278 automatically, the default being _enable_. When used at runtime, 296 automatically, the default being _enable_. When used at runtime,
279 _disable_ also disables any currently active inhibitors. _activate_, 297 _disable_ also disables any currently active inhibitors. _activate_,
280 _deactivate_ and _toggle_ are only useable at runtime and change the 298 _deactivate_ and _toggle_ are only usable at runtime and change the
281 state of a potentially existing inhibitor on the currently focused 299 state of a potentially existing inhibitor on the currently focused
282 window. This can be used with the current seat alias (_-_) to affect 300 window. This can be used with the current seat alias (_-_) to affect
283 only the currently focused window of the current seat. Subcommand 301 only the currently focused window of the current seat. Subcommand
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd
index 373e9dce..2f697248 100644
--- a/sway/sway-ipc.7.scd
+++ b/sway/sway-ipc.7.scd
@@ -213,7 +213,10 @@ following properties:
213: Whether this output is active/enabled 213: Whether this output is active/enabled
214|- dpms 214|- dpms
215: boolean 215: boolean
216: Whether this output is on/off (via DPMS) 216: (Deprecated, use _power_ instead) Whether this output is on/off (via DPMS)
217|- power
218: boolean
219: Whether this output is on/off
217|- primary 220|- primary
218: boolean 221: boolean
219: For i3 compatibility, this will be false. It does not make sense in Wayland 222: For i3 compatibility, this will be false. It does not make sense in Wayland
@@ -294,7 +297,7 @@ following properties:
294Retrieve a JSON representation of the tree 297Retrieve a JSON representation of the tree
295 298
296*REPLY*++ 299*REPLY*++
297An array of object the represent the current tree. Each object represents one 300An array of objects that represent the current tree. Each object represents one
298node and will have the following properties: 301node and will have the following properties:
299 302
300[- *PROPERTY* 303[- *PROPERTY*
@@ -334,8 +337,9 @@ node and will have the following properties:
334 this, but borders are included. 337 this, but borders are included.
335|- window_rect 338|- window_rect
336: object 339: object
337: The geometry of the contents inside the node. The window decorations are 340: The geometry of the content inside the node. These coordinates are relative
338 excluded from this calculation, but borders are included. 341 to the node itself. Window decorations and borders are outside the
342 _window_rect_
339|- deco_rect 343|- deco_rect
340: object 344: object
341: The geometry of the decorations for the node relative to the parent node 345: The geometry of the decorations for the node relative to the parent node
@@ -370,8 +374,14 @@ node and will have the following properties:
370 that can be used as an aid in submitting reproduction steps for bug reports 374 that can be used as an aid in submitting reproduction steps for bug reports
371|- fullscreen_mode 375|- fullscreen_mode
372: integer 376: integer
373: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means 377: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means
374 full workspace, and 2 means global fullscreen 378 full workspace, and 2 means global fullscreen
379|- floating
380: string
381: Floating state of container. Can be either "auto_off" or "user_on"
382|- scratchpad_state
383: string
384: Whether the window is in the scratchpad. Can be either "none" or "fresh"
375|- app_id 385|- app_id
376: string 386: string
377: (Only views) For an xdg-shell view, the name of the application, if set. 387: (Only views) For an xdg-shell view, the name of the application, if set.
@@ -1036,7 +1046,7 @@ An object with a single string property containing the contents of the config
1036*Example Reply:* 1046*Example Reply:*
1037``` 1047```
1038{ 1048{
1039 "config": "set $mod Mod4\nbindsym $mod+q exit\n" 1049 "config": "set $mod Mod4\\nbindsym $mod+q exit\\n"
1040} 1050}
1041``` 1051```
1042 1052
@@ -1191,9 +1201,16 @@ following properties will be included for devices that support them:
1191: int 1201: int
1192: The scroll button to use when _scroll_method_ is _on_button_down_. This 1202: The scroll button to use when _scroll_method_ is _on_button_down_. This
1193 will be given as an input event code 1203 will be given as an input event code
1204|- scroll_button_lock
1205: string
1206: Whether scroll button lock is enabled. It can be _enabled_ or _disabled_
1194|- dwt 1207|- dwt
1195: string 1208: string
1196: Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ 1209: Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_
1210|- dwtp
1211: string
1212: Whether disable-while-trackpointing is enabled. It can be _enabled_ or
1213 _disabled_
1197|- calibration_matrix 1214|- calibration_matrix
1198: array 1215: array
1199: An array of 6 floats representing the calibration matrix for absolute 1216: An array of 6 floats representing the calibration matrix for absolute
@@ -1233,7 +1250,8 @@ following properties will be included for devices that support them:
1233 "click_method": "button_areas", 1250 "click_method": "button_areas",
1234 "middle_emulation": "disabled", 1251 "middle_emulation": "disabled",
1235 "scroll_method": "edge", 1252 "scroll_method": "edge",
1236 "dwt": "enabled" 1253 "dwt": "enabled",
1254 "dwtp": "enabled"
1237 } 1255 }
1238 }, 1256 },
1239 { 1257 {
@@ -1360,7 +1378,8 @@ one seat. Each object has the following properties:
1360 "click_method": "button_areas", 1378 "click_method": "button_areas",
1361 "middle_emulation": "disabled", 1379 "middle_emulation": "disabled",
1362 "scroll_method": "edge", 1380 "scroll_method": "edge",
1363 "dwt": "enabled" 1381 "dwt": "enabled",
1382 "dwtp": "enabled"
1364 } 1383 }
1365 }, 1384 },
1366 { 1385 {
@@ -1436,6 +1455,9 @@ available:
1436: workspace 1455: workspace
1437:[ Sent whenever an event involving a workspace occurs such as initialization 1456:[ Sent whenever an event involving a workspace occurs such as initialization
1438 of a new workspace or a different workspace gains focus 1457 of a new workspace or a different workspace gains focus
1458|- 0x80000001
1459: output
1460: Sent when outputs are updated
1439|- 0x80000002 1461|- 0x80000002
1440: mode 1462: mode
1441: Sent whenever the binding mode changes 1463: Sent whenever the binding mode changes
@@ -1556,6 +1578,20 @@ The following change types are currently available:
1556} 1578}
1557``` 1579```
1558 1580
1581## 0x80000001. OUTPUT
1582
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
1559## 0x80000002. MODE 1595## 0x80000002. MODE
1560 1596
1561Sent 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 2828c841..7d088d5d 100644
--- a/sway/sway-output.5.scd
+++ b/sway/sway-output.5.scd
@@ -24,7 +24,7 @@ must be separated by one space. For example:
24 24
25# COMMANDS 25# COMMANDS
26 26
27*output* <name> mode|resolution|res [--custom] <WIDTHxHEIGHT>[@<RATE>Hz] 27*output* <name> mode|resolution|res [--custom] <width>x<height>[@<rate>Hz]
28 Configures the specified output to use the given mode. Modes are a 28 Configures the specified output to use the given mode. Modes are a
29 combination of width and height (in pixels) and a refresh rate that your 29 combination of width and height (in pixels) and a refresh rate that your
30 display can be configured to use. For a list of available modes for each 30 display can be configured to use. For a list of available modes for each
@@ -40,6 +40,16 @@ must be separated by one space. For example:
40 40
41 output HDMI-A-1 mode 1920x1080@60Hz 41 output HDMI-A-1 mode 1920x1080@60Hz
42 42
43*output* <name> modeline <clock> <hdisplay> <hsync_start> <hsync_end> <htotal> <vdisplay> <vsync_start> <vsync_end> <vtotal> <hsync> <vsync>
44 Configures the specified output to use the given modeline. It can be
45 generated using *cvt*(1) and *gtf*(1) commands. See *xorg.conf*(5).
46 Only supported on DRM backend.
47
48 Example:
49
50 output HDMI-A-1 modeline 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync
51
52
43*output* <name> position|pos <X> <Y> 53*output* <name> position|pos <X> <Y>
44 Places the specified output at the specific position in the global 54 Places the specified output at the specific position in the global
45 coordinate space. The cursor may only be moved between immediately 55 coordinate space. The cursor may only be moved between immediately
@@ -62,13 +72,11 @@ must be separated by one space. For example:
62 72
63*output* <name> scale <factor> 73*output* <name> scale <factor>
64 Scales the specified output by the specified scale _factor_. An integer is 74 Scales the specified output by the specified scale _factor_. An integer is
65 recommended, but fractional values are also supported. If a fractional 75 recommended, but fractional values are also supported. You may be better
66 value are specified, be warned that it is not possible to faithfully 76 served by setting an integer scale factor and adjusting the font size of
67 represent the contents of your windows - they will be rendered at the next 77 your applications to taste. HiDPI isn't supported with Xwayland clients
68 highest integer scale factor and downscaled. You may be better served by 78 (windows will blur). A fractional scale may be slightly adjusted to match
69 setting an integer scale factor and adjusting the font size of your 79 requirements of the protocol.
70 applications to taste. HiDPI isn't supported with Xwayland clients (windows
71 will blur).
72 80
73*output* <name> scale_filter linear|nearest|smart 81*output* <name> scale_filter linear|nearest|smart
74 Indicates how to scale application buffers that are rendered at a scale 82 Indicates how to scale application buffers that are rendered at a scale
@@ -109,12 +117,20 @@ must be separated by one space. For example:
109 Enables or disables the specified output (all outputs are enabled by 117 Enables or disables the specified output (all outputs are enabled by
110 default). 118 default).
111 119
120 As opposed to the _power_ command, the output will lose its current
121 workspace and windows.
122
112*output* <name> toggle 123*output* <name> toggle
113 Toggle the specified output. 124 Toggle the specified output.
114 125
126*output* <name> power on|off|toggle
127 Turns on or off the specified output.
128
129 As opposed to the _enable_ and _disable_ commands, the output keeps its
130 current workspaces and windows.
131
115*output* <name> dpms on|off|toggle 132*output* <name> dpms on|off|toggle
116 Enables or disables the specified output via DPMS. To turn an output off 133 Deprecated. Alias for _power_.
117 (ie. blank the screen but keep workspaces as-is), one can set DPMS to off.
118 134
119*output* <name> max_render_time off|<msec> 135*output* <name> max_render_time off|<msec>
120 Controls when sway composites the output, as a positive number of 136 Controls when sway composites the output, as a positive number of
@@ -147,6 +163,21 @@ must be separated by one space. For example:
147 adaptive sync can improve latency, but can cause flickering on some 163 adaptive sync can improve latency, but can cause flickering on some
148 hardware. 164 hardware.
149 165
166*output* <name> render_bit_depth 8|10
167 Controls the color channel bit depth at which frames are rendered; the
168 default is currently 8 bits per channel.
169
170 Setting higher values will not have an effect if hardware and software lack
171 support for such bit depths. Successfully increasing the render bit depth
172 will not necessarily increase the bit depth of the frames sent to a display.
173 An increased render bit depth may provide smoother rendering of gradients,
174 and screenshots which can more precisely store the colors of programs
175 which display high bit depth colors.
176
177 Warnings: this can break screenshot/screencast programs which have not been
178 updated to work with different bit depths. This command is experimental,
179 and may be removed or changed in the future.
180
150# SEE ALSO 181# SEE ALSO
151 182
152*sway*(5) *sway-input*(5) 183*sway*(5) *sway-input*(5)
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 8958c7e3..9f823947 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -176,6 +176,12 @@ set|plus|minus|toggle <amount>
176*layout* default|splith|splitv|stacking|tabbed 176*layout* default|splith|splitv|stacking|tabbed
177 Sets the layout mode of the focused container. 177 Sets the layout mode of the focused container.
178 178
179 When using the _stacking_ layout, only the focused window in the container is
180 displayed, with the opened windows' list on the top of the container.
181
182 The _tabbed_ layout is similar to _stacking_, but the windows’ list is vertically
183 split.
184
179*layout* toggle [split|all] 185*layout* toggle [split|all]
180 Cycles the layout mode of the focused container though a preset list of 186 Cycles the layout mode of the focused container though a preset list of
181 layouts. If no argument is given, then it cycles through stacking, tabbed 187 layouts. If no argument is given, then it cycles through stacking, tabbed
@@ -210,15 +216,14 @@ set|plus|minus|toggle <amount>
210 further details. 216 further details.
211 217
212*move* left|right|up|down [<px> px] 218*move* left|right|up|down [<px> px]
213 Moves the focused container in the direction specified. If the container, 219 Moves the focused container in the direction specified. The optional _px_
214 the optional _px_ argument specifies how many pixels to move the container. 220 argument specifies how many pixels to move the container. If unspecified,
215 If unspecified, the default is 10 pixels. Pixels are ignored when moving 221 the default is 10 pixels. Pixels are ignored when moving tiled containers.
216 tiled containers.
217 222
218*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ptt] 223*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt]
219 Moves the focused container to the specified position in the workspace. 224 Moves the focused container to the specified position in the workspace.
220 The position can be specified in pixels or percentage points, omitting 225 The position can be specified in pixels or percentage points, omitting
221 the unit defaults to pixels. If _absolute_ is used, the position is 226 the unit defaults to pixels. If _absolute_ is used, the position is
222 relative to all outputs. _absolute_ can not be used with percentage points. 227 relative to all outputs. _absolute_ can not be used with percentage points.
223 228
224*move* [absolute] position center 229*move* [absolute] position center
@@ -319,8 +324,10 @@ set|plus|minus|toggle <amount>
319 established by the *seat* subcommand of the same name. See 324 established by the *seat* subcommand of the same name. See
320 *sway-input*(5) for more ways to affect inhibitors. 325 *sway-input*(5) for more ways to affect inhibitors.
321 326
322*split* vertical|v|horizontal|h|toggle|t 327*split* vertical|v|horizontal|h|none|n|toggle|t
323 Splits the current container, vertically or horizontally. When _toggle_ is 328 Splits the current container, vertically or horizontally. When _none_ is
329 specified, the effect of a previous split is undone if the current
330 container is the only child of a split parent. When _toggle_ is
324 specified, the current container is split opposite to the parent 331 specified, the current container is split opposite to the parent
325 container's layout. 332 container's layout.
326 333
@@ -382,8 +389,8 @@ runtime.
382 for_window <criteria> move container to output <output> 389 for_window <criteria> move container to output <output>
383 390
384*bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ 391*bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
385[--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [Group<1-4>+]<key combo> \ 392[--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \
386<command> 393[Group<1-4>+]<key combo> <command>
387 Binds _key combo_ to execute the sway command _command_ when pressed. You 394 Binds _key combo_ to execute the sway command _command_ when pressed. You
388 may use XKB key names here (*wev*(1) is a good tool for discovering these). 395 may use XKB key names here (*wev*(1) is a good tool for discovering these).
389 With the flag _--release_, the command is executed when the key combo is 396 With the flag _--release_, the command is executed when the key combo is
@@ -393,6 +400,12 @@ runtime.
393 only be available for that group. By default, if you overwrite a binding, 400 only be available for that group. By default, if you overwrite a binding,
394 swaynag will give you a warning. To silence this, use the _--no-warn_ flag. 401 swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
395 402
403 For specifying modifier keys, you can use the XKB modifier names _Shift_,
404 _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock),
405 _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for
406 AltGr). In addition, you can use the aliases _Ctrl_ (for Control), _Alt_
407 (for Alt), and _Super_ (for the Logo key).
408
396 Unless the flag _--locked_ is set, the command will not be run when a 409 Unless the flag _--locked_ is set, the command will not be run when a
397 screen locking program is active. If there is a matching binding with 410 screen locking program is active. If there is a matching binding with
398 and without _--locked_, the one with will be preferred when locked and the 411 and without _--locked_, the one with will be preferred when locked and the
@@ -404,7 +417,7 @@ runtime.
404 a keyboard shortcuts inhibitor is active for the currently focused 417 a keyboard shortcuts inhibitor is active for the currently focused
405 window. Such inhibitors are usually requested by remote desktop and 418 window. Such inhibitors are usually requested by remote desktop and
406 virtualization software to enable the user to send keyboard shortcuts 419 virtualization software to enable the user to send keyboard shortcuts
407 to the remote or virtual session. The _--inhibited_ flag allows to 420 to the remote or virtual session. The _--inhibited_ flag allows one to
408 define bindings which will be exempt from pass-through to such 421 define bindings which will be exempt from pass-through to such
409 software. The same preference logic as for _--locked_ applies. 422 software. The same preference logic as for _--locked_ applies.
410 423
@@ -447,7 +460,8 @@ runtime.
447``` 460```
448 461
449 *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ 462 *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \
450[--locked] [--input-device=<device>] [--no-warn] [Group<1-4>+]<code> <command> 463[--locked] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \
464[Group<1-4>+]<code> <command>
451 is also available for binding with key/button codes instead of key/button names. 465 is also available for binding with key/button codes instead of key/button names.
452 466
453*bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> 467*bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command>
@@ -480,6 +494,62 @@ runtime.
480 bindswitch lid:toggle exec echo "Lid moved" 494 bindswitch lid:toggle exec echo "Lid moved"
481``` 495```
482 496
497*bindgesture* [--exact] [--input-device=<device>] [--no-warn] \
498<gesture>[:<fingers>][:directions] <command>
499 Binds _gesture_ to execute the sway command _command_ when detected.
500 Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally
501 can be limited to bind to a certain number of _fingers_ or, for a
502 _pinch_ or _swipe_ gesture, to certain _directions_.
503
504[[ *type*
505:[ *fingers*
506:< *direction*
507| hold
508:- 1 - 5
509: none
510| swipe
511: 3 - 5
512: up, down, left, right
513| pinch
514: 2 - 5
515: all above + inward, outward, clockwise, counterclockwise
516
517 The _fingers_ can be limited to any sensible number or left empty to accept
518 any finger counts.
519 Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_,
520 _outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture.
521 Multiple directions can be combined by a plus.
522
523 If a _input-device_ is given, the binding will only be executed for
524 that input device and will be executed instead of any binding that is
525 generic to all devices. By default, if you overwrite a binding,
526 swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
527
528 The _--exact_ flag can be used to ensure a binding only matches when exactly
529 all specified directions are matched and nothing more. If there is matching
530 binding with _--exact_, it will be preferred.
531
532 The priority for matching bindings is as follows: input device, then
533 exact matches followed by matches with the highest number of matching
534 directions.
535
536 Gestures executed while the pointer is above a bar are not handled by sway.
537 See the respective documentation, e.g. *bindgesture* in *sway-bar*(5).
538
539 Example:
540```
541 # Allow switching between workspaces with left and right swipes
542 bindgesture swipe:right workspace prev
543 bindgesture swipe:left workspace next
544
545 # Allow container movements by pinching them
546 bindgesture pinch:inward+up move up
547 bindgesture pinch:inward+down move down
548 bindgesture pinch:inward+left move left
549 bindgesture pinch:inward+right move right
550
551```
552
483*client.background* <color> 553*client.background* <color>
484 This command is ignored and is only present for i3 compatibility. 554 This command is ignored and is only present for i3 compatibility.
485 555
@@ -497,6 +567,12 @@ runtime.
497 *client.focused_inactive* 567 *client.focused_inactive*
498 The most recently focused view within a container which is not focused. 568 The most recently focused view within a container which is not focused.
499 569
570 *client.focused_tab_title*
571 A view that has focused descendant container.
572 Tab or stack container title that is the parent of the focused container
573 but is not directly focused. Defaults to focused_inactive if not
574 specified and does not use the indicator and child_border colors.
575
500 *client.placeholder* 576 *client.placeholder*
501 Ignored (present for i3 compatibility). 577 Ignored (present for i3 compatibility).
502 578
@@ -552,6 +628,12 @@ The default colors are:
552: #ffffff 628: #ffffff
553: #484e50 629: #484e50
554: #5f676a 630: #5f676a
631| *focused_tab_title*
632: #333333
633: #5f676a
634: #ffffff
635: n/a
636: n/a
555| *unfocused* 637| *unfocused*
556: #333333 638: #333333
557: #222222 639: #222222
@@ -573,7 +655,8 @@ The default colors are:
573 655
574 656
575*default_border* normal|none|pixel [<n>] 657*default_border* normal|none|pixel [<n>]
576 Set default border style for new tiled windows. 658 Set default border style for new tiled windows. Config reload won't affect
659 existing windows, only newly created ones after the reload.
577 660
578*default_floating_border* normal|none|pixel [<n>] 661*default_floating_border* normal|none|pixel [<n>]
579 Set default border style for new floating windows. This only applies to 662 Set default border style for new floating windows. This only applies to
@@ -606,11 +689,11 @@ The default colors are:
606 after switching between workspaces. 689 after switching between workspaces.
607 690
608*focus_on_window_activation* smart|urgent|focus|none 691*focus_on_window_activation* smart|urgent|focus|none
609 This option determines what to do when an xwayland client requests 692 This option determines what to do when a client requests window activation.
610 window activation. If set to _urgent_, the urgent state will be set 693 If set to _urgent_, the urgent state will be set for that window. If set to
611 for that window. If set to _focus_, the window will become focused. 694 _focus_, the window will become focused. If set to _smart_, the window will
612 If set to _smart_, the window will become focused only if it is already 695 become focused only if it is already visible, otherwise the urgent state
613 visible, otherwise the urgent state will be set. Default is _urgent_. 696 will be set. Default is _urgent_.
614 697
615*focus_wrapping* yes|no|force|workspace 698*focus_wrapping* yes|no|force|workspace
616 This option determines what to do when attempting to focus over the edge 699 This option determines what to do when attempting to focus over the edge
@@ -630,14 +713,14 @@ The default colors are:
630 should be used instead. Regardless of whether pango markup is enabled, 713 should be used instead. Regardless of whether pango markup is enabled,
631 _font_ should be specified as a pango font description. For more 714 _font_ should be specified as a pango font description. For more
632 information on pango font descriptions, see 715 information on pango font descriptions, see
633 https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string 716 https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description
634 717
635*force_display_urgency_hint* <timeout> [ms] 718*force_display_urgency_hint* <timeout> [ms]
636 If an application on another workspace sets an urgency hint, switching to this 719 If an application on another workspace sets an urgency hint, switching to this
637 workspace may lead to immediate focus of the application, which also means the 720 workspace may lead to immediate focus of the application, which also means the
638 window decoration color would be immediately resetted to *client.focused*. This 721 window decoration color would be immediately reset to *client.focused*. This
639 may make it unnecessarily hard to tell which window originally raised the 722 may make it unnecessarily hard to tell which window originally raised the
640 event. This option allows to set a _timeout_ in ms to delay the urgency hint reset. 723 event. This option allows one to set a _timeout_ in ms to delay the urgency hint reset.
641 724
642*titlebar_border_thickness* <thickness> 725*titlebar_border_thickness* <thickness>
643 Thickness of the titlebar border in pixels 726 Thickness of the titlebar border in pixels
@@ -690,9 +773,10 @@ The default colors are:
690 borders will only be enabled if the workspace has more than one visible 773 borders will only be enabled if the workspace has more than one visible
691 child and gaps equal to zero. 774 child and gaps equal to zero.
692 775
693*smart_gaps* on|off 776*smart_gaps* on|off|toggle|inverse_outer
694 If smart_gaps are _on_ gaps will only be enabled if a workspace has more 777 If smart_gaps are _on_ gaps will only be enabled if a workspace has more
695 than one child. 778 than one child. If smart_gaps are _inverse_outer_ outer gaps will only
779 be enabled if a workspace has exactly one child.
696 780
697*mark* --add|--replace [--toggle] <identifier> 781*mark* --add|--replace [--toggle] <identifier>
698 Marks are arbitrary labels that can be used to identify certain windows and 782 Marks are arbitrary labels that can be used to identify certain windows and
@@ -731,6 +815,10 @@ The default colors are:
731 dialog will not be rendered. If _leave_fullscreen_, the view will exit 815 dialog will not be rendered. If _leave_fullscreen_, the view will exit
732 fullscreen mode and the dialog will be rendered. 816 fullscreen mode and the dialog will be rendered.
733 817
818*primary_selection* enabled|disabled
819 Enable or disable the primary selection clipboard. May only be configured
820 at launch. Default is _enabled_.
821
734*set* $<name> <value> 822*set* $<name> <value>
735 Sets variable $_name_ to _value_. You can use the new variable in the 823 Sets variable $_name_ to _value_. You can use the new variable in the
736 arguments of future commands. When the variable is used, it can be escaped 824 arguments of future commands. When the variable is used, it can be escaped
@@ -771,6 +859,11 @@ The default colors are:
771*unbindswitch* <switch>:<state> 859*unbindswitch* <switch>:<state>
772 Removes a binding for when <switch> changes to <state>. 860 Removes a binding for when <switch> changes to <state>.
773 861
862*unbindgesture* [--exact] [--input-device=<device>] \
863<gesture>[:<fingers>][:directions]
864 Removes a binding for the specified _gesture_, _fingers_
865 and _directions_ combination.
866
774*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ 867*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
775[--to-code] [--input-device=<device>] <key combo> 868[--to-code] [--input-device=<device>] <key combo>
776 Removes the binding for _key combo_ that was previously bound with the 869 Removes the binding for _key combo_ that was previously bound with the
@@ -876,6 +969,9 @@ properties in practice for your applications.
876 969
877The following attributes may be matched with: 970The following attributes may be matched with:
878 971
972*all*
973 Matches all windows.
974
879*app_id* 975*app_id*
880 Compare value against the app id. Can be a regular expression. If value is 976 Compare value against the app id. Can be a regular expression. If value is
881 \_\_focused\_\_, then the app id must be the same as that of the currently 977 \_\_focused\_\_, then the app id must be the same as that of the currently
@@ -884,7 +980,8 @@ The following attributes may be matched with:
884*class* 980*class*
885 Compare value against the window class. Can be a regular expression. If 981 Compare value against the window class. Can be a regular expression. If
886 value is \_\_focused\_\_, then the window class must be the same as that of 982 value is \_\_focused\_\_, then the window class must be the same as that of
887 the currently focused window. _class_ are specific to X11 applications. 983 the currently focused window. _class_ are specific to X11 applications and
984 require XWayland.
888 985
889*con_id* 986*con_id*
890 Compare against the internal container ID, which you can find via IPC. If 987 Compare against the internal container ID, which you can find via IPC. If
@@ -898,12 +995,14 @@ The following attributes may be matched with:
898 Matches floating windows. 995 Matches floating windows.
899 996
900*id* 997*id*
901 Compare value against the X11 window ID. Must be numeric. 998 Compare value against the X11 window ID. Must be numeric. id is specific to
999 X11 applications and requires XWayland.
902 1000
903*instance* 1001*instance*
904 Compare value against the window instance. Can be a regular expression. If 1002 Compare value against the window instance. Can be a regular expression. If
905 value is \_\_focused\_\_, then the window instance must be the same as that 1003 value is \_\_focused\_\_, then the window instance must be the same as that
906 of the currently focused window. 1004 of the currently focused window. instance is specific to X11 applications and
1005 requires XWayland.
907 1006
908*pid* 1007*pid*
909 Compare value against the window's process ID. Must be numeric. 1008 Compare value against the window's process ID. Must be numeric.
@@ -928,12 +1027,14 @@ The following attributes may be matched with:
928*window_role* 1027*window_role*
929 Compare against the window role (WM_WINDOW_ROLE). Can be a regular 1028 Compare against the window role (WM_WINDOW_ROLE). Can be a regular
930 expression. If value is \_\_focused\_\_, then the window role must be the 1029 expression. If value is \_\_focused\_\_, then the window role must be the
931 same as that of the currently focused window. 1030 same as that of the currently focused window. window_role is specific to X11
1031 applications and requires XWayland.
932 1032
933*window_type* 1033*window_type*
934 Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values 1034 Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values
935 are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, 1035 are normal, dialog, utility, toolbar, splash, menu, dropdown_menu,
936 popup_menu, tooltip and notification. 1036 popup_menu, tooltip and notification. window_type is specific to X11
1037 applications and requires XWayland.
937 1038
938*workspace* 1039*workspace*
939 Compare against the workspace name for this view. Can be a regular 1040 Compare against the workspace name for this view. Can be a regular
diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c
new file mode 100644
index 00000000..5eba53ba
--- /dev/null
+++ b/sway/sway_text_node.c
@@ -0,0 +1,302 @@
1#include <drm_fourcc.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <wlr/types/wlr_buffer.h>
6#include <wlr/interfaces/wlr_buffer.h>
7#include "cairo_util.h"
8#include "log.h"
9#include "pango.h"
10#include "sway/config.h"
11#include "sway/sway_text_node.h"
12
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 ba582989..bc5e23ea 100644
--- a/sway/swaynag.c
+++ b/sway/swaynag.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <signal.h> 1#include <signal.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -64,6 +63,8 @@ bool swaynag_spawn(const char *swaynag_command,
64 sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); 63 sway_log(SWAY_ERROR, "Failed to create fork for swaynag");
65 goto failed; 64 goto failed;
66 } else if (pid == 0) { 65 } else if (pid == 0) {
66 restore_nofile_limit();
67
67 pid = fork(); 68 pid = fork();
68 if (pid < 0) { 69 if (pid < 0) {
69 sway_log_errno(SWAY_ERROR, "fork failed"); 70 sway_log_errno(SWAY_ERROR, "fork failed");
@@ -143,22 +144,16 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag,
143 144
144 va_list args; 145 va_list args;
145 va_start(args, fmt); 146 va_start(args, fmt);
146 size_t length = vsnprintf(NULL, 0, fmt, args) + 1; 147 char *str = vformat_str(fmt, args);
147 va_end(args); 148 va_end(args);
148 149 if (!str) {
149 char *temp = malloc(length + 1);
150 if (!temp) {
151 sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry."); 150 sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry.");
152 return; 151 return;
153 } 152 }
154 153
155 va_start(args, fmt); 154 write(swaynag->fd[1], str, strlen(str));
156 vsnprintf(temp, length, fmt, args);
157 va_end(args);
158
159 write(swaynag->fd[1], temp, length);
160 155
161 free(temp); 156 free(str);
162} 157}
163 158
164void 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 4aa82c35..d4003fe6 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -264,6 +263,9 @@ void arrange_workspace(struct sway_workspace *workspace) {
264 area->width, area->height, area->x, area->y); 263 area->width, area->height, area->x, area->y);
265 264
266 bool first_arrange = workspace->width == 0 && workspace->height == 0; 265 bool first_arrange = workspace->width == 0 && workspace->height == 0;
266 struct wlr_box prev_box;
267 workspace_get_box(workspace, &prev_box);
268
267 double prev_x = workspace->x - workspace->current_gaps.left; 269 double prev_x = workspace->x - workspace->current_gaps.left;
268 double prev_y = workspace->y - workspace->current_gaps.top; 270 double prev_y = workspace->y - workspace->current_gaps.top;
269 workspace->width = area->width; 271 workspace->width = area->width;
@@ -277,13 +279,14 @@ void arrange_workspace(struct sway_workspace *workspace) {
277 if (!first_arrange && (diff_x != 0 || diff_y != 0)) { 279 if (!first_arrange && (diff_x != 0 || diff_y != 0)) {
278 for (int i = 0; i < workspace->floating->length; ++i) { 280 for (int i = 0; i < workspace->floating->length; ++i) {
279 struct sway_container *floater = workspace->floating->items[i]; 281 struct sway_container *floater = workspace->floating->items[i];
280 container_floating_translate(floater, diff_x, diff_y);
281 double center_x = floater->pending.x + floater->pending.width / 2;
282 double center_y = floater->pending.y + floater->pending.height / 2;
283 struct wlr_box workspace_box; 282 struct wlr_box workspace_box;
284 workspace_get_box(workspace, &workspace_box); 283 workspace_get_box(workspace, &workspace_box);
285 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { 284 floating_fix_coordinates(floater, &prev_box, &workspace_box);
286 container_floating_move_to_center(floater); 285 // Set transformation for scratchpad windows.
286 if (floater->scratchpad) {
287 struct wlr_box output_box;
288 output_get_box(output, &output_box);
289 floater->transform = output_box;
287 } 290 }
288 } 291 }
289 } 292 }
@@ -311,12 +314,13 @@ void arrange_output(struct sway_output *output) {
311 if (config->reloading) { 314 if (config->reloading) {
312 return; 315 return;
313 } 316 }
314 const struct wlr_box *output_box = wlr_output_layout_get_box( 317 struct wlr_box output_box;
315 root->output_layout, output->wlr_output); 318 wlr_output_layout_get_box(root->output_layout,
316 output->lx = output_box->x; 319 output->wlr_output, &output_box);
317 output->ly = output_box->y; 320 output->lx = output_box.x;
318 output->width = output_box->width; 321 output->ly = output_box.y;
319 output->height = output_box->height; 322 output->width = output_box.width;
323 output->height = output_box.height;
320 324
321 for (int i = 0; i < output->workspaces->length; ++i) { 325 for (int i = 0; i < output->workspaces->length; ++i) {
322 struct sway_workspace *workspace = output->workspaces->items[i]; 326 struct sway_workspace *workspace = output->workspaces->items[i];
@@ -328,12 +332,12 @@ void arrange_root(void) {
328 if (config->reloading) { 332 if (config->reloading) {
329 return; 333 return;
330 } 334 }
331 const struct wlr_box *layout_box = 335 struct wlr_box layout_box;
332 wlr_output_layout_get_box(root->output_layout, NULL); 336 wlr_output_layout_get_box(root->output_layout, NULL, &layout_box);
333 root->x = layout_box->x; 337 root->x = layout_box.x;
334 root->y = layout_box->y; 338 root->y = layout_box.y;
335 root->width = layout_box->width; 339 root->width = layout_box.width;
336 root->height = layout_box->height; 340 root->height = layout_box.height;
337 341
338 if (root->fullscreen_global) { 342 if (root->fullscreen_global) {
339 struct sway_container *fs = root->fullscreen_global; 343 struct sway_container *fs = root->fullscreen_global;
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 1e84e603..9224b4fb 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1,29 +1,77 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <drm_fourcc.h> 2#include <drm_fourcc.h>
4#include <stdint.h> 3#include <stdint.h>
5#include <stdlib.h> 4#include <stdlib.h>
6#include <string.h>
7#include <strings.h>
8#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
7#include <wlr/types/wlr_linux_dmabuf_v1.h>
9#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
10#include "cairo_util.h" 9#include <wlr/types/wlr_subcompositor.h>
11#include "pango.h" 10#include "linux-dmabuf-unstable-v1-protocol.h"
12#include "sway/config.h" 11#include "sway/config.h"
13#include "sway/desktop.h"
14#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
15#include "sway/input/input-manager.h" 13#include "sway/input/input-manager.h"
16#include "sway/input/seat.h" 14#include "sway/input/seat.h"
17#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
16#include "sway/scene_descriptor.h"
17#include "sway/sway_text_node.h"
18#include "sway/output.h" 18#include "sway/output.h"
19#include "sway/server.h" 19#include "sway/server.h"
20#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
21#include "sway/tree/view.h" 21#include "sway/tree/view.h"
22#include "sway/tree/workspace.h" 22#include "sway/tree/workspace.h"
23#include "sway/xdg_decoration.h"
23#include "list.h" 24#include "list.h"
24#include "log.h" 25#include "log.h"
25#include "stringop.h" 26#include "stringop.h"
26 27
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
27struct sway_container *container_create(struct sway_view *view) { 75struct sway_container *container_create(struct sway_view *view) {
28 struct sway_container *c = calloc(1, sizeof(struct sway_container)); 76 struct sway_container *c = calloc(1, sizeof(struct sway_container));
29 if (!c) { 77 if (!c) {
@@ -31,23 +79,411 @@ struct sway_container *container_create(struct sway_view *view) {
31 return NULL; 79 return NULL;
32 } 80 }
33 node_init(&c->node, N_CONTAINER, c); 81 node_init(&c->node, N_CONTAINER, c);
34 c->pending.layout = L_NONE; 82
35 c->view = view; 83 // Container tree structure
36 c->alpha = 1.0f; 84 // - scene tree
85 // - title bar
86 // - border
87 // - background
88 // - title text
89 // - marks text
90 // - border
91 // - border top/bottom/left/right
92 // - content_tree (we put the content node here so when we disable the
93 // border everything gets disabled. We only render the content iff there
94 // is a border as well)
95 // - buffer used for output enter/leave events for foreign_toplevel
96 bool failed = false;
97 c->scene_tree = alloc_scene_tree(root->staging, &failed);
98
99 c->title_bar.tree = alloc_scene_tree(c->scene_tree, &failed);
100 c->title_bar.border = alloc_scene_tree(c->title_bar.tree, &failed);
101 c->title_bar.background = alloc_scene_tree(c->title_bar.tree, &failed);
102
103 // for opacity purposes we need to carfully create the scene such that
104 // none of our rect nodes as well as text buffers don't overlap. To do
105 // this we have to create rects such that they go around text buffers
106 for (int i = 0; i < 4; i++) {
107 alloc_rect_node(c->title_bar.border, &failed);
108 }
109
110 for (int i = 0; i < 5; i++) {
111 alloc_rect_node(c->title_bar.background, &failed);
112 }
113
114 c->border.tree = alloc_scene_tree(c->scene_tree, &failed);
115 c->content_tree = alloc_scene_tree(c->border.tree, &failed);
116
117 if (view) {
118 // only containers with views can have borders
119 c->border.top = alloc_rect_node(c->border.tree, &failed);
120 c->border.bottom = alloc_rect_node(c->border.tree, &failed);
121 c->border.left = alloc_rect_node(c->border.tree, &failed);
122 c->border.right = alloc_rect_node(c->border.tree, &failed);
123
124 c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL);
125 if (!c->output_handler) {
126 sway_log(SWAY_ERROR, "Failed to allocate a scene node");
127 failed = true;
128 }
129
130 if (!failed) {
131 c->output_enter.notify = handle_output_enter;
132 wl_signal_add(&c->output_handler->events.output_enter,
133 &c->output_enter);
134 c->output_leave.notify = handle_output_leave;
135 wl_signal_add(&c->output_handler->events.output_leave,
136 &c->output_leave);
137 c->output_handler->point_accepts_input = handle_point_accepts_input;
138 }
139 }
140
141 if (!failed && !scene_descriptor_assign(&c->scene_tree->node,
142 SWAY_SCENE_DESC_CONTAINER, c)) {
143 failed = true;
144 }
145
146 if (failed) {
147 wlr_scene_node_destroy(&c->scene_tree->node);
148 free(c);
149 return NULL;
150 }
37 151
38 if (!view) { 152 if (!view) {
39 c->pending.children = create_list(); 153 c->pending.children = create_list();
40 c->current.children = create_list(); 154 c->current.children = create_list();
41 } 155 }
156
157 c->pending.layout = L_NONE;
158 c->view = view;
159 c->alpha = 1.0f;
42 c->marks = create_list(); 160 c->marks = create_list();
43 c->outputs = create_list();
44 161
45 wl_signal_init(&c->events.destroy); 162 wl_signal_init(&c->events.destroy);
46 wl_signal_emit(&root->events.new_node, &c->node); 163 wl_signal_emit_mutable(&root->events.new_node, &c->node);
164
165 container_update(c);
47 166
48 return c; 167 return c;
49} 168}
50 169
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
51void container_destroy(struct sway_container *con) { 487void container_destroy(struct sway_container *con) {
52 if (!sway_assert(con->node.destroying, 488 if (!sway_assert(con->node.destroying,
53 "Tried to free container which wasn't marked as destroying")) { 489 "Tried to free container which wasn't marked as destroying")) {
@@ -59,29 +495,21 @@ void container_destroy(struct sway_container *con) {
59 } 495 }
60 free(con->title); 496 free(con->title);
61 free(con->formatted_title); 497 free(con->formatted_title);
62 wlr_texture_destroy(con->title_focused);
63 wlr_texture_destroy(con->title_focused_inactive);
64 wlr_texture_destroy(con->title_unfocused);
65 wlr_texture_destroy(con->title_urgent);
66 list_free(con->pending.children); 498 list_free(con->pending.children);
67 list_free(con->current.children); 499 list_free(con->current.children);
68 list_free(con->outputs);
69 500
70 list_free_items_and_destroy(con->marks); 501 list_free_items_and_destroy(con->marks);
71 wlr_texture_destroy(con->marks_focused);
72 wlr_texture_destroy(con->marks_focused_inactive);
73 wlr_texture_destroy(con->marks_unfocused);
74 wlr_texture_destroy(con->marks_urgent);
75 502
76 if (con->view) { 503 if (con->view && con->view->container == con) {
77 if (con->view->container == con) { 504 con->view->container = NULL;
78 con->view->container = NULL; 505 wlr_scene_node_destroy(&con->output_handler->node);
79 }
80 if (con->view->destroying) { 506 if (con->view->destroying) {
81 view_destroy(con->view); 507 view_destroy(con->view);
82 } 508 }
83 } 509 }
84 510
511 scene_node_disown_children(con->content_tree);
512 wlr_scene_node_destroy(&con->scene_tree->node);
85 free(con); 513 free(con);
86} 514}
87 515
@@ -98,7 +526,7 @@ void container_begin_destroy(struct sway_container *con) {
98 container_fullscreen_disable(con); 526 container_fullscreen_disable(con);
99 } 527 }
100 528
101 wl_signal_emit(&con->node.events.destroy, &con->node); 529 wl_signal_emit_mutable(&con->node.events.destroy, &con->node);
102 530
103 container_end_mouse_operation(con); 531 container_end_mouse_operation(con);
104 532
@@ -168,267 +596,6 @@ struct sway_container *container_find_child(struct sway_container *container,
168 return NULL; 596 return NULL;
169} 597}
170 598
171static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly,
172 struct wlr_surface **surface, double *sx, double *sy) {
173 if (!sway_assert(con->view, "Expected a view")) {
174 return NULL;
175 }
176 struct sway_view *view = con->view;
177 double view_sx = lx - con->surface_x + view->geometry.x;
178 double view_sy = ly - con->surface_y + view->geometry.y;
179
180 double _sx, _sy;
181 struct wlr_surface *_surface = NULL;
182 switch (view->type) {
183#if HAVE_XWAYLAND
184 case SWAY_VIEW_XWAYLAND:
185 _surface = wlr_surface_surface_at(view->surface,
186 view_sx, view_sy, &_sx, &_sy);
187 break;
188#endif
189 case SWAY_VIEW_XDG_SHELL:
190 _surface = wlr_xdg_surface_surface_at(
191 view->wlr_xdg_surface,
192 view_sx, view_sy, &_sx, &_sy);
193 break;
194 }
195 if (_surface) {
196 *sx = _sx;
197 *sy = _sy;
198 *surface = _surface;
199 return con;
200 }
201 return NULL;
202}
203
204/**
205 * container_at for a container with layout L_TABBED.
206 */
207static struct sway_container *container_at_tabbed(struct sway_node *parent,
208 double lx, double ly,
209 struct wlr_surface **surface, double *sx, double *sy) {
210 struct wlr_box box;
211 node_get_box(parent, &box);
212 if (lx < box.x || lx > box.x + box.width ||
213 ly < box.y || ly > box.y + box.height) {
214 return NULL;
215 }
216 struct sway_seat *seat = input_manager_current_seat();
217 list_t *children = node_get_children(parent);
218 if (!children->length) {
219 return NULL;
220 }
221
222 // Tab titles
223 int title_height = container_titlebar_height();
224 if (ly < box.y + title_height) {
225 int tab_width = box.width / children->length;
226 int child_index = (lx - box.x) / tab_width;
227 if (child_index >= children->length) {
228 child_index = children->length - 1;
229 }
230 struct sway_container *child = children->items[child_index];
231 return child;
232 }
233
234 // Surfaces
235 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
236 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
237}
238
239/**
240 * container_at for a container with layout L_STACKED.
241 */
242static struct sway_container *container_at_stacked(struct sway_node *parent,
243 double lx, double ly,
244 struct wlr_surface **surface, double *sx, double *sy) {
245 struct wlr_box box;
246 node_get_box(parent, &box);
247 if (lx < box.x || lx > box.x + box.width ||
248 ly < box.y || ly > box.y + box.height) {
249 return NULL;
250 }
251 struct sway_seat *seat = input_manager_current_seat();
252 list_t *children = node_get_children(parent);
253
254 // Title bars
255 int title_height = container_titlebar_height();
256 if (title_height > 0) {
257 int child_index = (ly - box.y) / title_height;
258 if (child_index < children->length) {
259 struct sway_container *child = children->items[child_index];
260 return child;
261 }
262 }
263
264 // Surfaces
265 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
266 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
267}
268
269/**
270 * container_at for a container with layout L_HORIZ or L_VERT.
271 */
272static struct sway_container *container_at_linear(struct sway_node *parent,
273 double lx, double ly,
274 struct wlr_surface **surface, double *sx, double *sy) {
275 list_t *children = node_get_children(parent);
276 for (int i = 0; i < children->length; ++i) {
277 struct sway_container *child = children->items[i];
278 struct sway_container *container =
279 tiling_container_at(&child->node, lx, ly, surface, sx, sy);
280 if (container) {
281 return container;
282 }
283 }
284 return NULL;
285}
286
287static struct sway_container *floating_container_at(double lx, double ly,
288 struct wlr_surface **surface, double *sx, double *sy) {
289 // For outputs with floating containers that overhang the output bounds,
290 // those at the end of the output list appear on top of floating
291 // containers from other outputs, so iterate the list in reverse.
292 for (int i = root->outputs->length - 1; i >= 0; --i) {
293 struct sway_output *output = root->outputs->items[i];
294 for (int j = 0; j < output->workspaces->length; ++j) {
295 struct sway_workspace *ws = output->workspaces->items[j];
296 if (!workspace_is_visible(ws)) {
297 continue;
298 }
299 // Items at the end of the list are on top, so iterate the list in
300 // reverse.
301 for (int k = ws->floating->length - 1; k >= 0; --k) {
302 struct sway_container *floater = ws->floating->items[k];
303 struct sway_container *container =
304 tiling_container_at(&floater->node, lx, ly, surface, sx, sy);
305 if (container) {
306 return container;
307 }
308 }
309 }
310 }
311 return NULL;
312}
313
314static struct sway_container *view_container_content_at(struct sway_node *parent,
315 double lx, double ly,
316 struct wlr_surface **surface, double *sx, double *sy) {
317 if (!sway_assert(node_is_view(parent), "Expected a view")) {
318 return NULL;
319 }
320
321 struct sway_container *container = parent->sway_container;
322 struct wlr_box box = {
323 .x = container->pending.content_x,
324 .y = container->pending.content_y,
325 .width = container->pending.content_width,
326 .height = container->pending.content_height,
327 };
328
329 if (wlr_box_contains_point(&box, lx, ly)) {
330 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
331 return container;
332 }
333
334 return NULL;
335}
336
337static struct sway_container *view_container_at(struct sway_node *parent,
338 double lx, double ly,
339 struct wlr_surface **surface, double *sx, double *sy) {
340 if (!sway_assert(node_is_view(parent), "Expected a view")) {
341 return NULL;
342 }
343
344 struct sway_container *container = parent->sway_container;
345 struct wlr_box box = {
346 .x = container->pending.x,
347 .y = container->pending.y,
348 .width = container->pending.width,
349 .height = container->pending.height,
350 };
351
352 if (wlr_box_contains_point(&box, lx, ly)) {
353 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
354 return container;
355 }
356
357 return NULL;
358}
359
360struct sway_container *tiling_container_at(struct sway_node *parent,
361 double lx, double ly,
362 struct wlr_surface **surface, double *sx, double *sy) {
363 if (node_is_view(parent)) {
364 return view_container_at(parent, lx, ly, surface, sx, sy);
365 }
366 if (!node_get_children(parent)) {
367 return NULL;
368 }
369 switch (node_get_layout(parent)) {
370 case L_HORIZ:
371 case L_VERT:
372 return container_at_linear(parent, lx, ly, surface, sx, sy);
373 case L_TABBED:
374 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
375 case L_STACKED:
376 return container_at_stacked(parent, lx, ly, surface, sx, sy);
377 case L_NONE:
378 return NULL;
379 }
380 return NULL;
381}
382
383static bool surface_is_popup(struct wlr_surface *surface) {
384 if (wlr_surface_is_xdg_surface(surface)) {
385 struct wlr_xdg_surface *xdg_surface =
386 wlr_xdg_surface_from_wlr_surface(surface);
387 while (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) {
388 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
389 return true;
390 }
391 xdg_surface = xdg_surface->toplevel->parent;
392 }
393 return false;
394 }
395
396 return false;
397}
398
399struct sway_container *container_at(struct sway_workspace *workspace,
400 double lx, double ly,
401 struct wlr_surface **surface, double *sx, double *sy) {
402 struct sway_container *c;
403
404 struct sway_seat *seat = input_manager_current_seat();
405 struct sway_container *focus = seat_get_focused_container(seat);
406 bool is_floating = focus && container_is_floating_or_child(focus);
407 // Focused view's popups
408 if (focus && focus->view) {
409 c = surface_at_view(focus, lx, ly, surface, sx, sy);
410 if (c && surface_is_popup(*surface)) {
411 return c;
412 }
413 *surface = NULL;
414 }
415 // Floating
416 if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) {
417 return c;
418 }
419 // Tiling (focused)
420 if (focus && focus->view && !is_floating) {
421 if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) {
422 return c;
423 }
424 }
425 // Tiling (non-focused)
426 if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) {
427 return c;
428 }
429 return NULL;
430}
431
432void container_for_each_child(struct sway_container *container, 599void container_for_each_child(struct sway_container *container,
433 void (*f)(struct sway_container *container, void *data), 600 void (*f)(struct sway_container *container, void *data),
434 void *data) { 601 void *data) {
@@ -474,123 +641,6 @@ bool container_has_ancestor(struct sway_container *descendant,
474 return false; 641 return false;
475} 642}
476 643
477void container_damage_whole(struct sway_container *container) {
478 for (int i = 0; i < root->outputs->length; ++i) {
479 struct sway_output *output = root->outputs->items[i];
480 output_damage_whole_container(output, container);
481 }
482}
483
484/**
485 * Return the output which will be used for scale purposes.
486 * This is the most recently entered output.
487 */
488struct sway_output *container_get_effective_output(struct sway_container *con) {
489 if (con->outputs->length == 0) {
490 return NULL;
491 }
492 return con->outputs->items[con->outputs->length - 1];
493}
494
495static void update_title_texture(struct sway_container *con,
496 struct wlr_texture **texture, struct border_colors *class) {
497 struct sway_output *output = container_get_effective_output(con);
498 if (!output) {
499 return;
500 }
501 if (*texture) {
502 wlr_texture_destroy(*texture);
503 *texture = NULL;
504 }
505 if (!con->formatted_title) {
506 return;
507 }
508
509 double scale = output->wlr_output->scale;
510 int width = 0;
511 int height = con->title_height * scale;
512
513 // We must use a non-nil cairo_t for cairo_set_font_options to work.
514 // Therefore, we cannot use cairo_create(NULL).
515 cairo_surface_t *dummy_surface = cairo_image_surface_create(
516 CAIRO_FORMAT_ARGB32, 0, 0);
517 cairo_t *c = cairo_create(dummy_surface);
518 cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
519 cairo_font_options_t *fo = cairo_font_options_create();
520 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
521 if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
522 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
523 } else {
524 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
525 cairo_font_options_set_subpixel_order(fo,
526 to_cairo_subpixel_order(output->wlr_output->subpixel));
527 }
528 cairo_set_font_options(c, fo);
529 get_text_size(c, config->font, &width, NULL, NULL, scale,
530 config->pango_markup, "%s", con->formatted_title);
531 cairo_surface_destroy(dummy_surface);
532 cairo_destroy(c);
533
534 if (width == 0 || height == 0) {
535 return;
536 }
537
538 cairo_surface_t *surface = cairo_image_surface_create(
539 CAIRO_FORMAT_ARGB32, width, height);
540 cairo_t *cairo = cairo_create(surface);
541 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
542 cairo_set_font_options(cairo, fo);
543 cairo_font_options_destroy(fo);
544 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
545 class->background[2], class->background[3]);
546 cairo_paint(cairo);
547 PangoContext *pango = pango_cairo_create_context(cairo);
548 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
549 class->text[2], class->text[3]);
550 cairo_move_to(cairo, 0, 0);
551
552 pango_printf(cairo, config->font, scale, config->pango_markup,
553 "%s", con->formatted_title);
554
555 cairo_surface_flush(surface);
556 unsigned char *data = cairo_image_surface_get_data(surface);
557 int stride = cairo_image_surface_get_stride(surface);
558 struct wlr_renderer *renderer = wlr_backend_get_renderer(
559 output->wlr_output->backend);
560 *texture = wlr_texture_from_pixels(
561 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
562 cairo_surface_destroy(surface);
563 g_object_unref(pango);
564 cairo_destroy(cairo);
565}
566
567void container_update_title_textures(struct sway_container *container) {
568 update_title_texture(container, &container->title_focused,
569 &config->border_colors.focused);
570 update_title_texture(container, &container->title_focused_inactive,
571 &config->border_colors.focused_inactive);
572 update_title_texture(container, &container->title_unfocused,
573 &config->border_colors.unfocused);
574 update_title_texture(container, &container->title_urgent,
575 &config->border_colors.urgent);
576 container_damage_whole(container);
577}
578
579void container_calculate_title_height(struct sway_container *container) {
580 if (!container->formatted_title) {
581 container->title_height = 0;
582 return;
583 }
584 cairo_t *cairo = cairo_create(NULL);
585 int height;
586 int baseline;
587 get_text_size(cairo, config->font, NULL, &height, &baseline, 1,
588 config->pango_markup, "%s", container->formatted_title);
589 cairo_destroy(cairo);
590 container->title_height = height;
591 container->title_baseline = baseline;
592}
593
594/** 644/**
595 * Calculate and return the length of the tree representation. 645 * Calculate and return the length of the tree representation.
596 * An example tree representation is: V[Terminal, Firefox] 646 * An example tree representation is: V[Terminal, Firefox]
@@ -656,8 +706,13 @@ void container_update_representation(struct sway_container *con) {
656 } 706 }
657 container_build_representation(con->pending.layout, con->pending.children, 707 container_build_representation(con->pending.layout, con->pending.children,
658 con->formatted_title); 708 con->formatted_title);
659 container_calculate_title_height(con); 709
660 container_update_title_textures(con); 710 if (con->title_bar.title_text) {
711 sway_text_node_set_text(con->title_bar.title_text, con->formatted_title);
712 container_arrange_title_bar(con);
713 } else {
714 container_update_title_bar(con);
715 }
661 } 716 }
662 if (con->pending.parent) { 717 if (con->pending.parent) {
663 container_update_representation(con->pending.parent); 718 container_update_representation(con->pending.parent);
@@ -688,12 +743,13 @@ void floating_calculate_constraints(int *min_width, int *max_width,
688 *min_height = config->floating_minimum_height; 743 *min_height = config->floating_minimum_height;
689 } 744 }
690 745
691 struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); 746 struct wlr_box box;
747 wlr_output_layout_get_box(root->output_layout, NULL, &box);
692 748
693 if (config->floating_maximum_width == -1) { // no maximum 749 if (config->floating_maximum_width == -1) { // no maximum
694 *max_width = INT_MAX; 750 *max_width = INT_MAX;
695 } else if (config->floating_maximum_width == 0) { // automatic 751 } else if (config->floating_maximum_width == 0) { // automatic
696 *max_width = box->width; 752 *max_width = box.width;
697 } else { 753 } else {
698 *max_width = config->floating_maximum_width; 754 *max_width = config->floating_maximum_width;
699 } 755 }
@@ -701,13 +757,28 @@ void floating_calculate_constraints(int *min_width, int *max_width,
701 if (config->floating_maximum_height == -1) { // no maximum 757 if (config->floating_maximum_height == -1) { // no maximum
702 *max_height = INT_MAX; 758 *max_height = INT_MAX;
703 } else if (config->floating_maximum_height == 0) { // automatic 759 } else if (config->floating_maximum_height == 0) { // automatic
704 *max_height = box->height; 760 *max_height = box.height;
705 } else { 761 } else {
706 *max_height = config->floating_maximum_height; 762 *max_height = config->floating_maximum_height;
707 } 763 }
708 764
709} 765}
710 766
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
711static void floating_natural_resize(struct sway_container *con) { 782static void floating_natural_resize(struct sway_container *con) {
712 int min_width, max_width, min_height, max_height; 783 int min_width, max_width, min_height, max_height;
713 floating_calculate_constraints(&min_width, &max_width, 784 floating_calculate_constraints(&min_width, &max_width,
@@ -733,9 +804,9 @@ void container_floating_resize_and_center(struct sway_container *con) {
733 return; 804 return;
734 } 805 }
735 806
736 struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, 807 struct wlr_box ob;
737 ws->output->wlr_output); 808 wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output, &ob);
738 if (!ob) { 809 if (wlr_box_empty(&ob)) {
739 // On NOOP output. Will be called again when moved to an output 810 // On NOOP output. Will be called again when moved to an output
740 con->pending.x = 0; 811 con->pending.x = 0;
741 con->pending.y = 0; 812 con->pending.y = 0;
@@ -747,8 +818,8 @@ void container_floating_resize_and_center(struct sway_container *con) {
747 floating_natural_resize(con); 818 floating_natural_resize(con);
748 if (!con->view) { 819 if (!con->view) {
749 if (con->pending.width > ws->width || con->pending.height > ws->height) { 820 if (con->pending.width > ws->width || con->pending.height > ws->height) {
750 con->pending.x = ob->x + (ob->width - con->pending.width) / 2; 821 con->pending.x = ob.x + (ob.width - con->pending.width) / 2;
751 con->pending.y = ob->y + (ob->height - con->pending.height) / 2; 822 con->pending.y = ob.y + (ob.height - con->pending.height) / 2;
752 } else { 823 } else {
753 con->pending.x = ws->x + (ws->width - con->pending.width) / 2; 824 con->pending.x = ws->x + (ws->width - con->pending.width) / 2;
754 con->pending.y = ws->y + (ws->height - con->pending.height) / 2; 825 con->pending.y = ws->y + (ws->height - con->pending.height) / 2;
@@ -756,8 +827,8 @@ void container_floating_resize_and_center(struct sway_container *con) {
756 } else { 827 } else {
757 if (con->pending.content_width > ws->width 828 if (con->pending.content_width > ws->width
758 || con->pending.content_height > ws->height) { 829 || con->pending.content_height > ws->height) {
759 con->pending.content_x = ob->x + (ob->width - con->pending.content_width) / 2; 830 con->pending.content_x = ob.x + (ob.width - con->pending.content_width) / 2;
760 con->pending.content_y = ob->y + (ob->height - con->pending.content_height) / 2; 831 con->pending.content_y = ob.y + (ob.height - con->pending.content_height) / 2;
761 } else { 832 } else {
762 con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2; 833 con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2;
763 con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2; 834 con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2;
@@ -779,11 +850,11 @@ void container_floating_set_default_size(struct sway_container *con) {
779 int min_width, max_width, min_height, max_height; 850 int min_width, max_width, min_height, max_height;
780 floating_calculate_constraints(&min_width, &max_width, 851 floating_calculate_constraints(&min_width, &max_width,
781 &min_height, &max_height); 852 &min_height, &max_height);
782 struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); 853 struct wlr_box box;
783 workspace_get_box(con->pending.workspace, box); 854 workspace_get_box(con->pending.workspace, &box);
784 855
785 double width = fmax(min_width, fmin(box->width * 0.5, max_width)); 856 double width = fmax(min_width, fmin(box.width * 0.5, max_width));
786 double height = fmax(min_height, fmin(box->height * 0.75, max_height)); 857 double height = fmax(min_height, fmin(box.height * 0.75, max_height));
787 if (!con->view) { 858 if (!con->view) {
788 con->pending.width = width; 859 con->pending.width = width;
789 con->pending.height = height; 860 con->pending.height = height;
@@ -792,8 +863,6 @@ void container_floating_set_default_size(struct sway_container *con) {
792 con->pending.content_height = height; 863 con->pending.content_height = height;
793 container_set_geometry_from_content(con); 864 container_set_geometry_from_content(con);
794 } 865 }
795
796 free(box);
797} 866}
798 867
799 868
@@ -835,7 +904,13 @@ void container_set_floating(struct sway_container *container, bool enable) {
835 if (container->view) { 904 if (container->view) {
836 view_set_tiled(container->view, false); 905 view_set_tiled(container->view, false);
837 if (container->view->using_csd) { 906 if (container->view->using_csd) {
907 container->saved_border = container->pending.border;
838 container->pending.border = B_CSD; 908 container->pending.border = B_CSD;
909 if (container->view->xdg_decoration) {
910 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
911 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
912 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
913 }
839 } 914 }
840 } 915 }
841 container_floating_set_default_size(container); 916 container_floating_set_default_size(container);
@@ -873,6 +948,11 @@ void container_set_floating(struct sway_container *container, bool enable) {
873 view_set_tiled(container->view, true); 948 view_set_tiled(container->view, true);
874 if (container->view->using_csd) { 949 if (container->view->using_csd) {
875 container->pending.border = container->saved_border; 950 container->pending.border = container->saved_border;
951 if (container->view->xdg_decoration) {
952 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
953 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
954 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
955 }
876 } 956 }
877 } 957 }
878 container->width_fraction = 0; 958 container->width_fraction = 0;
@@ -918,17 +998,6 @@ bool container_is_floating(struct sway_container *container) {
918 return false; 998 return false;
919} 999}
920 1000
921bool container_is_current_floating(struct sway_container *container) {
922 if (!container->current.parent && container->current.workspace &&
923 list_find(container->current.workspace->floating, container) != -1) {
924 return true;
925 }
926 if (container->scratchpad) {
927 return true;
928 }
929 return false;
930}
931
932void container_get_box(struct sway_container *container, struct wlr_box *box) { 1001void container_get_box(struct sway_container *container, struct wlr_box *box) {
933 box->x = container->pending.x; 1002 box->x = container->pending.x;
934 box->y = container->pending.y; 1003 box->y = container->pending.y;
@@ -1012,6 +1081,13 @@ void container_floating_move_to(struct sway_container *con,
1012 workspace_add_floating(new_workspace, con); 1081 workspace_add_floating(new_workspace, con);
1013 arrange_workspace(old_workspace); 1082 arrange_workspace(old_workspace);
1014 arrange_workspace(new_workspace); 1083 arrange_workspace(new_workspace);
1084 // If the moved container was a visible scratchpad container, then
1085 // update its transform.
1086 if (con->scratchpad) {
1087 struct wlr_box output_box;
1088 output_get_box(new_output, &output_box);
1089 con->transform = output_box;
1090 }
1015 workspace_detect_urgent(old_workspace); 1091 workspace_detect_urgent(old_workspace);
1016 workspace_detect_urgent(new_workspace); 1092 workspace_detect_urgent(new_workspace);
1017 } 1093 }
@@ -1223,72 +1299,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) {
1223 return false; 1299 return false;
1224} 1300}
1225 1301
1226static void surface_send_enter_iterator(struct wlr_surface *surface,
1227 int x, int y, void *data) {
1228 struct wlr_output *wlr_output = data;
1229 wlr_surface_send_enter(surface, wlr_output);
1230}
1231
1232static void surface_send_leave_iterator(struct wlr_surface *surface,
1233 int x, int y, void *data) {
1234 struct wlr_output *wlr_output = data;
1235 wlr_surface_send_leave(surface, wlr_output);
1236}
1237
1238void container_discover_outputs(struct sway_container *con) {
1239 struct wlr_box con_box = {
1240 .x = con->current.x,
1241 .y = con->current.y,
1242 .width = con->current.width,
1243 .height = con->current.height,
1244 };
1245 struct sway_output *old_output = container_get_effective_output(con);
1246
1247 for (int i = 0; i < root->outputs->length; ++i) {
1248 struct sway_output *output = root->outputs->items[i];
1249 struct wlr_box output_box;
1250 output_get_box(output, &output_box);
1251 struct wlr_box intersection;
1252 bool intersects =
1253 wlr_box_intersection(&intersection, &con_box, &output_box);
1254 int index = list_find(con->outputs, output);
1255
1256 if (intersects && index == -1) {
1257 // Send enter
1258 sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output);
1259 if (con->view) {
1260 view_for_each_surface(con->view,
1261 surface_send_enter_iterator, output->wlr_output);
1262 if (con->view->foreign_toplevel) {
1263 wlr_foreign_toplevel_handle_v1_output_enter(
1264 con->view->foreign_toplevel, output->wlr_output);
1265 }
1266 }
1267 list_add(con->outputs, output);
1268 } else if (!intersects && index != -1) {
1269 // Send leave
1270 sway_log(SWAY_DEBUG, "Container %p left output %p", con, output);
1271 if (con->view) {
1272 view_for_each_surface(con->view,
1273 surface_send_leave_iterator, output->wlr_output);
1274 if (con->view->foreign_toplevel) {
1275 wlr_foreign_toplevel_handle_v1_output_leave(
1276 con->view->foreign_toplevel, output->wlr_output);
1277 }
1278 }
1279 list_del(con->outputs, index);
1280 }
1281 }
1282 struct sway_output *new_output = container_get_effective_output(con);
1283 double old_scale = old_output && old_output->enabled ?
1284 old_output->wlr_output->scale : -1;
1285 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1286 if (old_scale != new_scale) {
1287 container_update_title_textures(con);
1288 container_update_marks_textures(con);
1289 }
1290}
1291
1292enum sway_container_layout container_parent_layout(struct sway_container *con) { 1302enum sway_container_layout container_parent_layout(struct sway_container *con) {
1293 if (con->pending.parent) { 1303 if (con->pending.parent) {
1294 return con->pending.parent->pending.layout; 1304 return con->pending.parent->pending.layout;
@@ -1299,19 +1309,11 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) {
1299 return L_NONE; 1309 return L_NONE;
1300} 1310}
1301 1311
1302enum sway_container_layout container_current_parent_layout(
1303 struct sway_container *con) {
1304 if (con->current.parent) {
1305 return con->current.parent->current.layout;
1306 }
1307 return con->current.workspace->current.layout;
1308}
1309
1310list_t *container_get_siblings(struct sway_container *container) { 1312list_t *container_get_siblings(struct sway_container *container) {
1311 if (container->pending.parent) { 1313 if (container->pending.parent) {
1312 return container->pending.parent->pending.children; 1314 return container->pending.parent->pending.children;
1313 } 1315 }
1314 if (container_is_scratchpad_hidden(container)) { 1316 if (!container->pending.workspace) {
1315 return NULL; 1317 return NULL;
1316 } 1318 }
1317 if (list_find(container->pending.workspace->tiling, container) != -1) { 1319 if (list_find(container->pending.workspace->tiling, container) != -1) {
@@ -1324,13 +1326,6 @@ int container_sibling_index(struct sway_container *child) {
1324 return list_find(container_get_siblings(child), child); 1326 return list_find(container_get_siblings(child), child);
1325} 1327}
1326 1328
1327list_t *container_get_current_siblings(struct sway_container *container) {
1328 if (container->current.parent) {
1329 return container->current.parent->current.children;
1330 }
1331 return container->current.workspace->current.tiling;
1332}
1333
1334void container_handle_fullscreen_reparent(struct sway_container *con) { 1329void container_handle_fullscreen_reparent(struct sway_container *con) {
1335 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || 1330 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||
1336 con->pending.workspace->fullscreen == con) { 1331 con->pending.workspace->fullscreen == con) {
@@ -1545,7 +1540,7 @@ bool container_find_and_unmark(char *mark) {
1545 if (strcmp(con_mark, mark) == 0) { 1540 if (strcmp(con_mark, mark) == 0) {
1546 free(con_mark); 1541 free(con_mark);
1547 list_del(con->marks, i); 1542 list_del(con->marks, i);
1548 container_update_marks_textures(con); 1543 container_update_marks(con);
1549 ipc_event_window(con, "mark"); 1544 ipc_event_window(con, "mark");
1550 return true; 1545 return true;
1551 } 1546 }
@@ -1576,103 +1571,15 @@ void container_add_mark(struct sway_container *con, char *mark) {
1576 ipc_event_window(con, "mark"); 1571 ipc_event_window(con, "mark");
1577} 1572}
1578 1573
1579static void update_marks_texture(struct sway_container *con,
1580 struct wlr_texture **texture, struct border_colors *class) {
1581 struct sway_output *output = container_get_effective_output(con);
1582 if (!output) {
1583 return;
1584 }
1585 if (*texture) {
1586 wlr_texture_destroy(*texture);
1587 *texture = NULL;
1588 }
1589 if (!con->marks->length) {
1590 return;
1591 }
1592
1593 size_t len = 0;
1594 for (int i = 0; i < con->marks->length; ++i) {
1595 char *mark = con->marks->items[i];
1596 if (mark[0] != '_') {
1597 len += strlen(mark) + 2;
1598 }
1599 }
1600 char *buffer = calloc(len + 1, 1);
1601 char *part = malloc(len + 1);
1602
1603 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
1604 free(buffer);
1605 return;
1606 }
1607
1608 for (int i = 0; i < con->marks->length; ++i) {
1609 char *mark = con->marks->items[i];
1610 if (mark[0] != '_') {
1611 sprintf(part, "[%s]", mark);
1612 strcat(buffer, part);
1613 }
1614 }
1615 free(part);
1616
1617 double scale = output->wlr_output->scale;
1618 int width = 0;
1619 int height = con->title_height * scale;
1620
1621 cairo_t *c = cairo_create(NULL);
1622 get_text_size(c, config->font, &width, NULL, NULL, scale, false,
1623 "%s", buffer);
1624 cairo_destroy(c);
1625
1626 if (width == 0 || height == 0) {
1627 return;
1628 }
1629
1630 cairo_surface_t *surface = cairo_image_surface_create(
1631 CAIRO_FORMAT_ARGB32, width, height);
1632 cairo_t *cairo = cairo_create(surface);
1633 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
1634 class->background[2], class->background[3]);
1635 cairo_paint(cairo);
1636 PangoContext *pango = pango_cairo_create_context(cairo);
1637 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
1638 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
1639 class->text[2], class->text[3]);
1640 cairo_move_to(cairo, 0, 0);
1641
1642 pango_printf(cairo, config->font, scale, false, "%s", buffer);
1643
1644 cairo_surface_flush(surface);
1645 unsigned char *data = cairo_image_surface_get_data(surface);
1646 int stride = cairo_image_surface_get_stride(surface);
1647 struct wlr_renderer *renderer = wlr_backend_get_renderer(
1648 output->wlr_output->backend);
1649 *texture = wlr_texture_from_pixels(
1650 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
1651 cairo_surface_destroy(surface);
1652 g_object_unref(pango);
1653 cairo_destroy(cairo);
1654 free(buffer);
1655}
1656
1657void container_update_marks_textures(struct sway_container *con) {
1658 if (!config->show_marks) {
1659 return;
1660 }
1661 update_marks_texture(con, &con->marks_focused,
1662 &config->border_colors.focused);
1663 update_marks_texture(con, &con->marks_focused_inactive,
1664 &config->border_colors.focused_inactive);
1665 update_marks_texture(con, &con->marks_unfocused,
1666 &config->border_colors.unfocused);
1667 update_marks_texture(con, &con->marks_urgent,
1668 &config->border_colors.urgent);
1669 container_damage_whole(con);
1670}
1671
1672void container_raise_floating(struct sway_container *con) { 1574void container_raise_floating(struct sway_container *con) {
1673 // Bring container to front by putting it at the end of the floating list. 1575 // Bring container to front by putting it at the end of the floating list.
1674 struct sway_container *floater = container_toplevel_ancestor(con); 1576 struct sway_container *floater = container_toplevel_ancestor(con);
1675 if (container_is_floating(floater) && floater->pending.workspace) { 1577 if (container_is_floating(floater) && floater->pending.workspace) {
1578 // it's okay to just raise the scene directly instead of waiting
1579 // for the transaction to go through. We won't be reconfiguring
1580 // surfaces
1581 wlr_scene_node_raise_to_top(&floater->scene_tree->node);
1582
1676 list_move_to_end(floater->pending.workspace->floating, floater); 1583 list_move_to_end(floater->pending.workspace->floating, floater);
1677 node_set_dirty(&floater->pending.workspace->node); 1584 node_set_dirty(&floater->pending.workspace->node);
1678 } 1585 }
@@ -1756,3 +1663,177 @@ int container_squash(struct sway_container *con) {
1756 } 1663 }
1757 return change; 1664 return change;
1758} 1665}
1666
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 c095dce0..2d11195e 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -1,14 +1,13 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <ctype.h> 2#include <ctype.h>
4#include <string.h> 3#include <string.h>
5#include <strings.h> 4#include <strings.h>
6#include <wlr/types/wlr_output_damage.h>
7#include "sway/ipc-server.h" 5#include "sway/ipc-server.h"
8#include "sway/layers.h" 6#include "sway/layers.h"
9#include "sway/output.h" 7#include "sway/output.h"
10#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
11#include "sway/tree/workspace.h" 9#include "sway/tree/workspace.h"
10#include "sway/server.h"
12#include "log.h" 11#include "log.h"
13#include "util.h" 12#include "util.h"
14 13
@@ -56,8 +55,8 @@ static void restore_workspaces(struct sway_output *output) {
56 } 55 }
57 56
58 // Saved workspaces 57 // Saved workspaces
59 while (root->noop_output->workspaces->length) { 58 while (root->fallback_output->workspaces->length) {
60 struct sway_workspace *ws = root->noop_output->workspaces->items[0]; 59 struct sway_workspace *ws = root->fallback_output->workspaces->items[0];
61 workspace_detach(ws); 60 workspace_detach(ws);
62 output_add_workspace(output, ws); 61 output_add_workspace(output, ws);
63 62
@@ -87,26 +86,63 @@ static void restore_workspaces(struct sway_output *output) {
87 output_sort_workspaces(output); 86 output_sort_workspaces(output);
88} 87}
89 88
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;
96 output->scale_filter = SCALE_FILTER_NEAREST; 137 output->scale_filter = SCALE_FILTER_NEAREST;
97 138
98 wl_signal_init(&output->events.destroy); 139 wl_signal_init(&output->events.disable);
99 140
100 wl_list_insert(&root->all_outputs, &output->link); 141 wl_list_insert(&root->all_outputs, &output->link);
101 142
102 output->workspaces = create_list(); 143 output->workspaces = create_list();
103 output->current.workspaces = create_list(); 144 output->current.workspaces = create_list();
104 145
105 size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
106 for (size_t i = 0; i < len; ++i) {
107 wl_list_init(&output->layers[i]);
108 }
109
110 return output; 146 return output;
111} 147}
112 148
@@ -146,7 +182,7 @@ void output_enable(struct sway_output *output) {
146 182
147 input_manager_configure_xcursor(); 183 input_manager_configure_xcursor();
148 184
149 wl_signal_emit(&root->events.new_node, &output->node); 185 wl_signal_emit_mutable(&root->events.new_node, &output->node);
150 186
151 arrange_layers(output); 187 arrange_layers(output);
152 arrange_root(); 188 arrange_root();
@@ -192,7 +228,7 @@ static void output_evacuate(struct sway_output *output) {
192 new_output = fallback_output; 228 new_output = fallback_output;
193 } 229 }
194 if (!new_output) { 230 if (!new_output) {
195 new_output = root->noop_output; 231 new_output = root->fallback_output;
196 } 232 }
197 233
198 struct sway_workspace *new_output_ws = 234 struct sway_workspace *new_output_ws =
@@ -238,20 +274,14 @@ void output_destroy(struct sway_output *output) {
238 "which is still referenced by transactions")) { 274 "which is still referenced by transactions")) {
239 return; 275 return;
240 } 276 }
277
278 destroy_scene_layers(output);
241 list_free(output->workspaces); 279 list_free(output->workspaces);
242 list_free(output->current.workspaces); 280 list_free(output->current.workspaces);
243 wl_event_source_remove(output->repaint_timer); 281 wl_event_source_remove(output->repaint_timer);
244 free(output); 282 free(output);
245} 283}
246 284
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.destroy, output); 295 wl_signal_emit_mutable(&output->events.disable, output);
266 296
267 output_evacuate(output); 297 output_evacuate(output);
268 298
269 root_for_each_container(untrack_output, output);
270
271 list_del(root->outputs, index); 299 list_del(root->outputs, index);
272 300
273 output->enabled = false; 301 output->enabled = false;
274 output->current_mode = NULL;
275 302
276 arrange_root(); 303 arrange_root();
277 304
278 // Reconfigure all devices, since devices with map_to_output directives for 305 // Reconfigure all devices, since devices with map_to_output directives for
279 // an output that goes offline should stop sending events as long as the 306 // an output that goes offline should stop sending events as long as the
280 // output remains offline. 307 // output remains offline.
281 input_manager_configure_all_inputs(); 308 input_manager_configure_all_input_mappings();
282} 309}
283 310
284void output_begin_destroy(struct sway_output *output) { 311void output_begin_destroy(struct sway_output *output) {
@@ -286,13 +313,10 @@ void output_begin_destroy(struct sway_output *output) {
286 return; 313 return;
287 } 314 }
288 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); 315 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name);
316 wl_signal_emit_mutable(&output->node.events.destroy, &output->node);
289 317
290 output->node.destroying = true; 318 output->node.destroying = true;
291 node_set_dirty(&output->node); 319 node_set_dirty(&output->node);
292
293 wl_list_remove(&output->link);
294 output->wlr_output->data = NULL;
295 output->wlr_output = NULL;
296} 320}
297 321
298struct sway_output *output_from_wlr_output(struct wlr_output *output) { 322struct sway_output *output_from_wlr_output(struct wlr_output *output) {
@@ -304,10 +328,10 @@ struct sway_output *output_get_in_direction(struct sway_output *reference,
304 if (!sway_assert(direction, "got invalid direction: %d", direction)) { 328 if (!sway_assert(direction, "got invalid direction: %d", direction)) {
305 return NULL; 329 return NULL;
306 } 330 }
307 struct wlr_box *output_box = 331 struct wlr_box output_box;
308 wlr_output_layout_get_box(root->output_layout, reference->wlr_output); 332 wlr_output_layout_get_box(root->output_layout, reference->wlr_output, &output_box);
309 int lx = output_box->x + output_box->width / 2; 333 int lx = output_box.x + output_box.width / 2;
310 int ly = output_box->y + output_box->height / 2; 334 int ly = output_box.y + output_box.height / 2;
311 struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( 335 struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output(
312 root->output_layout, direction, reference->wlr_output, lx, ly); 336 root->output_layout, direction, reference->wlr_output, lx, ly);
313 if (!wlr_adjacent) { 337 if (!wlr_adjacent) {
@@ -393,6 +417,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) {
393 box->height = output->height; 417 box->height = output->height;
394} 418}
395 419
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
396enum sway_container_layout output_get_default_layout( 447enum sway_container_layout output_get_default_layout(
397 struct sway_output *output) { 448 struct sway_output *output) {
398 if (config->default_orientation != L_NONE) { 449 if (config->default_orientation != L_NONE) {
diff --git a/sway/tree/root.c b/sway/tree/root.c
index dd4d8e33..ae3c3cb2 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -1,12 +1,14 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
5#include <wlr/types/wlr_output_layout.h> 4#include <wlr/types/wlr_output_layout.h>
5#include <wlr/types/wlr_scene.h>
6#include <wlr/util/transform.h>
6#include "sway/desktop/transaction.h" 7#include "sway/desktop/transaction.h"
7#include "sway/input/seat.h" 8#include "sway/input/seat.h"
8#include "sway/ipc-server.h" 9#include "sway/ipc-server.h"
9#include "sway/output.h" 10#include "sway/output.h"
11#include "sway/scene_descriptor.h"
10#include "sway/tree/arrange.h" 12#include "sway/tree/arrange.h"
11#include "sway/tree/container.h" 13#include "sway/tree/container.h"
12#include "sway/tree/root.h" 14#include "sway/tree/root.h"
@@ -23,21 +25,60 @@ static void output_layout_handle_change(struct wl_listener *listener,
23 transaction_commit_dirty(); 25 transaction_commit_dirty();
24} 26}
25 27
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 sprintf(file_name, "/proc/%d/stat", child);
213
214 if ((stat = fopen(file_name, "r"))) {
215 if (getline(&buffer, &buf_size, stat) != -1) {
216 strtok(buffer, sep); // pid
217 strtok(NULL, sep); // executable name
218 strtok(NULL, sep); // state
219 char *token = strtok(NULL, sep); // parent pid
220 parent = strtol(token, NULL, 10);
221 }
222 free(buffer);
223 fclose(stat);
224 }
225
226 if (parent) {
227 return (parent == child) ? -1 : parent;
228 }
229
230 return -1;
231}
232
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) {
@@ -374,8 +267,8 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data),
374 } 267 }
375 268
376 // Saved workspaces 269 // Saved workspaces
377 for (int i = 0; i < root->noop_output->workspaces->length; ++i) { 270 for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
378 struct sway_workspace *ws = root->noop_output->workspaces->items[i]; 271 struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
379 workspace_for_each_container(ws, f, data); 272 workspace_for_each_container(ws, f, data);
380 } 273 }
381} 274}
@@ -427,8 +320,8 @@ struct sway_container *root_find_container(
427 } 320 }
428 321
429 // Saved workspaces 322 // Saved workspaces
430 for (int i = 0; i < root->noop_output->workspaces->length; ++i) { 323 for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
431 struct sway_workspace *ws = root->noop_output->workspaces->items[i]; 324 struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
432 if ((result = workspace_find_container(ws, test, data))) { 325 if ((result = workspace_find_container(ws, test, data))) {
433 return result; 326 return result;
434 } 327 }
@@ -443,17 +336,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) {
443 box->width = root->width; 336 box->width = root->width;
444 box->height = root->height; 337 box->height = root->height;
445} 338}
446
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 fcdd06f7..35b4b73f 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -1,11 +1,13 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <strings.h> 2#include <strings.h>
4#include <wayland-server-core.h> 3#include <wayland-server-core.h>
5#include <wlr/render/wlr_renderer.h> 4#include <wlr/render/wlr_renderer.h>
6#include <wlr/types/wlr_buffer.h> 5#include <wlr/types/wlr_buffer.h>
6#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
7#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
7#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
8#include <wlr/types/wlr_server_decoration.h> 9#include <wlr/types/wlr_server_decoration.h>
10#include <wlr/types/wlr_subcompositor.h>
9#include <wlr/types/wlr_xdg_decoration_v1.h> 11#include <wlr/types/wlr_xdg_decoration_v1.h>
10#include "config.h" 12#include "config.h"
11#if HAVE_XWAYLAND 13#if HAVE_XWAYLAND
@@ -15,14 +17,16 @@
15#include "log.h" 17#include "log.h"
16#include "sway/criteria.h" 18#include "sway/criteria.h"
17#include "sway/commands.h" 19#include "sway/commands.h"
18#include "sway/desktop.h"
19#include "sway/desktop/transaction.h" 20#include "sway/desktop/transaction.h"
20#include "sway/desktop/idle_inhibit_v1.h" 21#include "sway/desktop/idle_inhibit_v1.h"
22#include "sway/desktop/launcher.h"
21#include "sway/input/cursor.h" 23#include "sway/input/cursor.h"
22#include "sway/ipc-server.h" 24#include "sway/ipc-server.h"
23#include "sway/output.h" 25#include "sway/output.h"
24#include "sway/input/seat.h" 26#include "sway/input/seat.h"
27#include "sway/scene_descriptor.h"
25#include "sway/server.h" 28#include "sway/server.h"
29#include "sway/sway_text_node.h"
26#include "sway/tree/arrange.h" 30#include "sway/tree/arrange.h"
27#include "sway/tree/container.h" 31#include "sway/tree/container.h"
28#include "sway/tree/view.h" 32#include "sway/tree/view.h"
@@ -32,15 +36,29 @@
32#include "pango.h" 36#include "pango.h"
33#include "stringop.h" 37#include "stringop.h"
34 38
35void view_init(struct sway_view *view, enum sway_view_type type, 39bool view_init(struct sway_view *view, enum sway_view_type type,
36 const struct sway_view_impl *impl) { 40 const struct sway_view_impl *impl) {
41 bool failed = false;
42 view->scene_tree = alloc_scene_tree(root->staging, &failed);
43 view->content_tree = alloc_scene_tree(view->scene_tree, &failed);
44
45 if (!failed && !scene_descriptor_assign(&view->scene_tree->node,
46 SWAY_SCENE_DESC_VIEW, view)) {
47 failed = true;
48 }
49
50 if (failed) {
51 wlr_scene_node_destroy(&view->scene_tree->node);
52 return false;
53 }
54
37 view->type = type; 55 view->type = type;
38 view->impl = impl; 56 view->impl = impl;
39 view->executed_criteria = create_list(); 57 view->executed_criteria = create_list();
40 wl_list_init(&view->saved_buffers);
41 view->allow_request_urgent = true; 58 view->allow_request_urgent = true;
42 view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; 59 view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
43 wl_signal_init(&view->events.unmap); 60 wl_signal_init(&view->events.unmap);
61 return true;
44} 62}
45 63
46void view_destroy(struct sway_view *view) { 64void view_destroy(struct sway_view *view) {
@@ -57,11 +75,10 @@ void view_destroy(struct sway_view *view) {
57 return; 75 return;
58 } 76 }
59 wl_list_remove(&view->events.unmap.listener_list); 77 wl_list_remove(&view->events.unmap.listener_list);
60 if (!wl_list_empty(&view->saved_buffers)) {
61 view_remove_saved_buffer(view);
62 }
63 list_free(view->executed_criteria); 78 list_free(view->executed_criteria);
64 79
80 view_assign_ctx(view, NULL);
81 wlr_scene_node_destroy(&view->scene_tree->node);
65 free(view->title_format); 82 free(view->title_format);
66 83
67 if (view->impl->destroy) { 84 if (view->impl->destroy) {
@@ -362,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) {
362 } 379 }
363} 380}
364 381
365void view_request_activate(struct sway_view *view) { 382void view_request_activate(struct sway_view *view, struct sway_seat *seat) {
366 struct sway_workspace *ws = view->container->pending.workspace; 383 struct sway_workspace *ws = view->container->pending.workspace;
367 if (!ws) { // hidden scratchpad container 384 if (!seat) {
368 return; 385 seat = input_manager_current_seat();
369 } 386 }
370 struct sway_seat *seat = input_manager_current_seat();
371 387
372 switch (config->focus_on_window_activation) { 388 switch (config->focus_on_window_activation) {
373 case FOWA_SMART: 389 case FOWA_SMART:
374 if (workspace_is_visible(ws)) { 390 if (ws && workspace_is_visible(ws)) {
375 seat_set_focus_container(seat, view->container); 391 seat_set_focus_container(seat, view->container);
392 container_raise_floating(view->container);
376 } else { 393 } else {
377 view_set_urgent(view, true); 394 view_set_urgent(view, true);
378 } 395 }
@@ -381,11 +398,23 @@ void view_request_activate(struct sway_view *view) {
381 view_set_urgent(view, true); 398 view_set_urgent(view, true);
382 break; 399 break;
383 case FOWA_FOCUS: 400 case FOWA_FOCUS:
384 seat_set_focus_container(seat, view->container); 401 if (container_is_scratchpad_hidden_or_child(view->container)) {
402 root_scratchpad_show(view->container);
403 } else {
404 seat_set_focus_container(seat, view->container);
405 container_raise_floating(view->container);
406 }
385 break; 407 break;
386 case FOWA_NONE: 408 case FOWA_NONE:
387 break; 409 break;
388 } 410 }
411 transaction_commit_dirty();
412}
413
414void view_request_urgent(struct sway_view *view) {
415 if (config->focus_on_window_activation != FOWA_NONE) {
416 view_set_urgent(view, true);
417 }
389} 418}
390 419
391void view_set_csd_from_server(struct sway_view *view, bool enabled) { 420void view_set_csd_from_server(struct sway_view *view, bool enabled) {
@@ -432,52 +461,6 @@ void view_close_popups(struct sway_view *view) {
432 } 461 }
433} 462}
434 463
435void view_damage_from(struct sway_view *view) {
436 for (int i = 0; i < root->outputs->length; ++i) {
437 struct sway_output *output = root->outputs->items[i];
438 output_damage_from_view(output, view);
439 }
440}
441
442void view_for_each_surface(struct sway_view *view,
443 wlr_surface_iterator_func_t iterator, void *user_data) {
444 if (!view->surface) {
445 return;
446 }
447 if (view->impl->for_each_surface) {
448 view->impl->for_each_surface(view, iterator, user_data);
449 } else {
450 wlr_surface_for_each_surface(view->surface, iterator, user_data);
451 }
452}
453
454void view_for_each_popup_surface(struct sway_view *view,
455 wlr_surface_iterator_func_t iterator, void *user_data) {
456 if (!view->surface) {
457 return;
458 }
459 if (view->impl->for_each_popup_surface) {
460 view->impl->for_each_popup_surface(view, iterator, user_data);
461 }
462}
463
464static void view_subsurface_create(struct sway_view *view,
465 struct wlr_subsurface *subsurface);
466
467static void view_init_subsurfaces(struct sway_view *view,
468 struct wlr_surface *surface);
469
470static void view_child_init_subsurfaces(struct sway_view_child *view_child,
471 struct wlr_surface *surface);
472
473static void view_handle_surface_new_subsurface(struct wl_listener *listener,
474 void *data) {
475 struct sway_view *view =
476 wl_container_of(listener, view, surface_new_subsurface);
477 struct wlr_subsurface *subsurface = data;
478 view_subsurface_create(view, subsurface);
479}
480
481static bool view_has_executed_criteria(struct sway_view *view, 464static bool view_has_executed_criteria(struct sway_view *view,
482 struct criteria *criteria) { 465 struct criteria *criteria) {
483 for (int i = 0; i < view->executed_criteria->length; ++i) { 466 for (int i = 0; i < view->executed_criteria->length; ++i) {
@@ -519,7 +502,7 @@ static void view_populate_pid(struct sway_view *view) {
519#if HAVE_XWAYLAND 502#if HAVE_XWAYLAND
520 case SWAY_VIEW_XWAYLAND:; 503 case SWAY_VIEW_XWAYLAND:;
521 struct wlr_xwayland_surface *surf = 504 struct wlr_xwayland_surface *surf =
522 wlr_xwayland_surface_from_wlr_surface(view->surface); 505 wlr_xwayland_surface_try_from_wlr_surface(view->surface);
523 pid = surf->pid; 506 pid = surf->pid;
524 break; 507 break;
525#endif 508#endif
@@ -532,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) {
532 view->pid = pid; 515 view->pid = pid;
533} 516}
534 517
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
535static struct sway_workspace *select_workspace(struct sway_view *view) { 532static struct sway_workspace *select_workspace(struct sway_view *view) {
536 struct sway_seat *seat = input_manager_current_seat(); 533 struct sway_seat *seat = input_manager_current_seat();
537 534
@@ -567,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
567 } 564 }
568 list_free(criterias); 565 list_free(criterias);
569 if (ws) { 566 if (ws) {
570 root_remove_workspace_pid(view->pid); 567 view_assign_ctx(view, NULL);
571 return ws; 568 return ws;
572 } 569 }
573 570
574 // Check if there's a PID mapping 571 // Check if there's a PID mapping
575 ws = root_workspace_for_pid(view->pid); 572 ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL;
576 if (ws) { 573 if (ws) {
574 view_assign_ctx(view, NULL);
577 return ws; 575 return ws;
578 } 576 }
579 577
@@ -591,6 +589,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
591 return NULL; 589 return NULL;
592} 590}
593 591
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
594static bool should_focus(struct sway_view *view) { 600static bool should_focus(struct sway_view *view) {
595 struct sway_seat *seat = input_manager_current_seat(); 601 struct sway_seat *seat = input_manager_current_seat();
596 struct sway_container *prev_con = seat_get_focused_container(seat); 602 struct sway_container *prev_con = seat_get_focused_container(seat);
@@ -716,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
716 view_populate_pid(view); 722 view_populate_pid(view);
717 view->container = container_create(view); 723 view->container = container_create(view);
718 724
725 if (view->ctx == NULL) {
726 struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid);
727 if (ctx != NULL) {
728 view_assign_ctx(view, ctx);
729 }
730 }
731
719 // If there is a request to be opened fullscreen on a specific output, try 732 // If there is a request to be opened fullscreen on a specific output, try
720 // to honor that request. Otherwise, fallback to assigns, pid mappings, 733 // to honor that request. Otherwise, fallback to assigns, pid mappings,
721 // focused workspace, etc 734 // focused workspace, etc
@@ -729,10 +742,36 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
729 } 742 }
730 743
731 struct sway_seat *seat = input_manager_current_seat(); 744 struct sway_seat *seat = input_manager_current_seat();
732 struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) 745 struct sway_node *node =
733 : seat_get_focus_inactive(seat, &root->node); 746 seat_get_focus_inactive(seat, ws ? &ws->node : &root->node);
734 struct sway_container *target_sibling = node->type == N_CONTAINER ? 747 struct sway_container *target_sibling = NULL;
735 node->sway_container : NULL; 748 if (node && node->type == N_CONTAINER) {
749 if (container_is_floating(node->sway_container)) {
750 // If we're about to launch the view into the floating container, then
751 // launch it as a tiled view instead.
752 if (ws) {
753 target_sibling = seat_get_focus_inactive_tiling(seat, ws);
754 if (target_sibling) {
755 struct sway_container *con =
756 seat_get_focus_inactive_view(seat, &target_sibling->node);
757 if (con) {
758 target_sibling = con;
759 }
760 }
761 } else {
762 ws = seat_get_last_known_workspace(seat);
763 }
764 } else {
765 target_sibling = node->sway_container;
766 }
767 }
768
769 struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
770 .app_id = view_get_app_id(view),
771 .title = view_get_title(view),
772 };
773 view->ext_foreign_toplevel =
774 wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state);
736 775
737 view->foreign_toplevel = 776 view->foreign_toplevel =
738 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); 777 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);
@@ -749,13 +788,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
749 wl_signal_add(&view->foreign_toplevel->events.destroy, 788 wl_signal_add(&view->foreign_toplevel->events.destroy,
750 &view->foreign_destroy); 789 &view->foreign_destroy);
751 790
752 // If we're about to launch the view into the floating container, then
753 // launch it as a tiled view in the root of the workspace instead.
754 if (target_sibling && container_is_floating(target_sibling)) {
755 target_sibling = NULL;
756 ws = seat_get_last_known_workspace(seat);
757 }
758
759 struct sway_container *container = view->container; 791 struct sway_container *container = view->container;
760 if (target_sibling) { 792 if (target_sibling) {
761 container_add_sibling(target_sibling, container, 1); 793 container_add_sibling(target_sibling, container, 1);
@@ -764,11 +796,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
764 } 796 }
765 ipc_event_window(view->container, "new"); 797 ipc_event_window(view->container, "new");
766 798
767 view_init_subsurfaces(view, wlr_surface);
768 wl_signal_add(&wlr_surface->events.new_subsurface,
769 &view->surface_new_subsurface);
770 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
771
772 if (decoration) { 799 if (decoration) {
773 view_update_csd_from_client(view, decoration); 800 view_update_csd_from_client(view, decoration);
774 } 801 }
@@ -812,9 +839,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
812 bool set_focus = should_focus(view); 839 bool set_focus = should_focus(view);
813 840
814#if HAVE_XWAYLAND 841#if HAVE_XWAYLAND
815 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 842 struct wlr_xwayland_surface *xsurface;
816 struct wlr_xwayland_surface *xsurface = 843 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
817 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
818 set_focus &= wlr_xwayland_icccm_input_model(xsurface) != 844 set_focus &= wlr_xwayland_icccm_input_model(xsurface) !=
819 WLR_ICCCM_INPUT_MODEL_NONE; 845 WLR_ICCCM_INPUT_MODEL_NONE;
820 } 846 }
@@ -824,6 +850,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
824 input_manager_set_focus(&view->container->node); 850 input_manager_set_focus(&view->container->node);
825 } 851 }
826 852
853 if (view->ext_foreign_toplevel) {
854 update_ext_foreign_toplevel(view);
855 }
856
827 const char *app_id; 857 const char *app_id;
828 const char *class; 858 const char *class;
829 if ((app_id = view_get_app_id(view)) != NULL) { 859 if ((app_id = view_get_app_id(view)) != NULL) {
@@ -834,15 +864,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
834} 864}
835 865
836void view_unmap(struct sway_view *view) { 866void view_unmap(struct sway_view *view) {
837 wl_signal_emit(&view->events.unmap, view); 867 wl_signal_emit_mutable(&view->events.unmap, view);
838 868
839 wl_list_remove(&view->surface_new_subsurface.link); 869 view->executed_criteria->length = 0;
840 870
841 if (view->urgent_timer) { 871 if (view->urgent_timer) {
842 wl_event_source_remove(view->urgent_timer); 872 wl_event_source_remove(view->urgent_timer);
843 view->urgent_timer = NULL; 873 view->urgent_timer = NULL;
844 } 874 }
845 875
876 if (view->ext_foreign_toplevel) {
877 wlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel);
878 view->ext_foreign_toplevel = NULL;
879 }
880
846 if (view->foreign_toplevel) { 881 if (view->foreign_toplevel) {
847 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); 882 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel);
848 view->foreign_toplevel = NULL; 883 view->foreign_toplevel = NULL;
@@ -889,293 +924,47 @@ void view_update_size(struct sway_view *view) {
889 container_set_geometry_from_content(con); 924 container_set_geometry_from_content(con);
890} 925}
891 926
892void view_center_surface(struct sway_view *view) { 927void view_center_and_clip_surface(struct sway_view *view) {
893 struct sway_container *con = view->container; 928 struct sway_container *con = view->container;
894 // We always center the current coordinates rather than the next, as the
895 // geometry immediately affects the currently active rendering.
896 con->surface_x = fmax(con->current.content_x, con->current.content_x +
897 (con->current.content_width - view->geometry.width) / 2);
898 con->surface_y = fmax(con->current.content_y, con->current.content_y +
899 (con->current.content_height - view->geometry.height) / 2);
900}
901
902static const struct sway_view_child_impl subsurface_impl;
903 929
904static void subsurface_get_root_coords(struct sway_view_child *child, 930 if (container_is_floating(con)) {
905 int *root_sx, int *root_sy) { 931 // We always center the current coordinates rather than the next, as the
906 struct wlr_surface *surface = child->surface; 932 // geometry immediately affects the currently active rendering.
907 *root_sx = -child->view->geometry.x; 933 int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2);
908 *root_sy = -child->view->geometry.y; 934 int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2);
909 935
910 if (child->parent && child->parent->impl && 936 wlr_scene_node_set_position(&view->content_tree->node, x, y);
911 child->parent->impl->get_root_coords) {
912 int sx, sy;
913 child->parent->impl->get_root_coords(child->parent, &sx, &sy);
914 *root_sx += sx;
915 *root_sy += sy;
916 } else { 937 } else {
917 while (surface && wlr_surface_is_subsurface(surface)) { 938 wlr_scene_node_set_position(&view->content_tree->node, 0, 0);
918 struct wlr_subsurface *subsurface =
919 wlr_subsurface_from_wlr_surface(surface);
920 if (subsurface == NULL) {
921 break;
922 }
923 *root_sx += subsurface->current.x;
924 *root_sy += subsurface->current.y;
925 surface = subsurface->parent;
926 }
927 } 939 }
928}
929 940
930static void subsurface_destroy(struct sway_view_child *child) { 941 // only make sure to clip the content if there is content to clip
931 if (!sway_assert(child->impl == &subsurface_impl, 942 if (!wl_list_empty(&con->view->content_tree->children)) {
932 "Expected a subsurface")) { 943 wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){
933 return; 944 .x = con->view->geometry.x,
934 } 945 .y = con->view->geometry.y,
935 struct sway_subsurface *subsurface = (struct sway_subsurface *)child; 946 .width = con->current.content_width,
936 wl_list_remove(&subsurface->destroy.link); 947 .height = con->current.content_height,
937 free(subsurface); 948 });
938}
939
940static const struct sway_view_child_impl subsurface_impl = {
941 .get_root_coords = subsurface_get_root_coords,
942 .destroy = subsurface_destroy,
943};
944
945static void subsurface_handle_destroy(struct wl_listener *listener,
946 void *data) {
947 struct sway_subsurface *subsurface =
948 wl_container_of(listener, subsurface, destroy);
949 struct sway_view_child *child = &subsurface->child;
950 view_child_destroy(child);
951}
952
953static void view_child_damage(struct sway_view_child *child, bool whole);
954
955static void view_subsurface_create(struct sway_view *view,
956 struct wlr_subsurface *wlr_subsurface) {
957 struct sway_subsurface *subsurface =
958 calloc(1, sizeof(struct sway_subsurface));
959 if (subsurface == NULL) {
960 sway_log(SWAY_ERROR, "Allocation failed");
961 return;
962 }
963 view_child_init(&subsurface->child, &subsurface_impl, view,
964 wlr_subsurface->surface);
965
966 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
967 subsurface->destroy.notify = subsurface_handle_destroy;
968
969 subsurface->child.mapped = true;
970
971 view_child_damage(&subsurface->child, true);
972}
973
974static void view_child_subsurface_create(struct sway_view_child *child,
975 struct wlr_subsurface *wlr_subsurface) {
976 struct sway_subsurface *subsurface =
977 calloc(1, sizeof(struct sway_subsurface));
978 if (subsurface == NULL) {
979 sway_log(SWAY_ERROR, "Allocation failed");
980 return;
981 }
982 subsurface->child.parent = child;
983 wl_list_insert(&child->children, &subsurface->child.link);
984 view_child_init(&subsurface->child, &subsurface_impl, child->view,
985 wlr_subsurface->surface);
986
987 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
988 subsurface->destroy.notify = subsurface_handle_destroy;
989
990 subsurface->child.mapped = true;
991
992 view_child_damage(&subsurface->child, true);
993}
994
995static bool view_child_is_mapped(struct sway_view_child *child) {
996 while (child) {
997 if (!child->mapped) {
998 return false;
999 }
1000 child = child->parent;
1001 }
1002 return true;
1003}
1004
1005static void view_child_damage(struct sway_view_child *child, bool whole) {
1006 if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) {
1007 return;
1008 }
1009 int sx, sy;
1010 child->impl->get_root_coords(child, &sx, &sy);
1011 desktop_damage_surface(child->surface,
1012 child->view->container->pending.content_x + sx,
1013 child->view->container->pending.content_y + sy, whole);
1014}
1015
1016static void view_child_handle_surface_commit(struct wl_listener *listener,
1017 void *data) {
1018 struct sway_view_child *child =
1019 wl_container_of(listener, child, surface_commit);
1020 view_child_damage(child, false);
1021}
1022
1023static void view_child_handle_surface_new_subsurface(
1024 struct wl_listener *listener, void *data) {
1025 struct sway_view_child *child =
1026 wl_container_of(listener, child, surface_new_subsurface);
1027 struct wlr_subsurface *subsurface = data;
1028 view_child_subsurface_create(child, subsurface);
1029}
1030
1031static void view_child_handle_surface_destroy(struct wl_listener *listener,
1032 void *data) {
1033 struct sway_view_child *child =
1034 wl_container_of(listener, child, surface_destroy);
1035 view_child_destroy(child);
1036}
1037
1038static void view_init_subsurfaces(struct sway_view *view,
1039 struct wlr_surface *surface) {
1040 struct wlr_subsurface *subsurface;
1041 wl_list_for_each(subsurface, &surface->subsurfaces_below, parent_link) {
1042 view_subsurface_create(view, subsurface);
1043 }
1044 wl_list_for_each(subsurface, &surface->subsurfaces_above, parent_link) {
1045 view_subsurface_create(view, subsurface);
1046 }
1047}
1048
1049static void view_child_init_subsurfaces(struct sway_view_child *view_child,
1050 struct wlr_surface *surface) {
1051 struct wlr_subsurface *subsurface;
1052 wl_list_for_each(subsurface, &surface->subsurfaces_below, parent_link) {
1053 view_child_subsurface_create(view_child, subsurface);
1054 }
1055 wl_list_for_each(subsurface, &surface->subsurfaces_above, parent_link) {
1056 view_child_subsurface_create(view_child, subsurface);
1057 }
1058}
1059
1060static void view_child_handle_surface_map(struct wl_listener *listener,
1061 void *data) {
1062 struct sway_view_child *child =
1063 wl_container_of(listener, child, surface_map);
1064 child->mapped = true;
1065 view_child_damage(child, true);
1066}
1067
1068static void view_child_handle_surface_unmap(struct wl_listener *listener,
1069 void *data) {
1070 struct sway_view_child *child =
1071 wl_container_of(listener, child, surface_unmap);
1072 view_child_damage(child, true);
1073 child->mapped = false;
1074}
1075
1076static void view_child_handle_view_unmap(struct wl_listener *listener,
1077 void *data) {
1078 struct sway_view_child *child =
1079 wl_container_of(listener, child, view_unmap);
1080 view_child_damage(child, true);
1081 child->mapped = false;
1082}
1083
1084void view_child_init(struct sway_view_child *child,
1085 const struct sway_view_child_impl *impl, struct sway_view *view,
1086 struct wlr_surface *surface) {
1087 child->impl = impl;
1088 child->view = view;
1089 child->surface = surface;
1090 wl_list_init(&child->children);
1091
1092 wl_signal_add(&surface->events.commit, &child->surface_commit);
1093 child->surface_commit.notify = view_child_handle_surface_commit;
1094 wl_signal_add(&surface->events.new_subsurface,
1095 &child->surface_new_subsurface);
1096 child->surface_new_subsurface.notify =
1097 view_child_handle_surface_new_subsurface;
1098 wl_signal_add(&surface->events.destroy, &child->surface_destroy);
1099 child->surface_destroy.notify = view_child_handle_surface_destroy;
1100
1101 // Not all child views have a map/unmap event
1102 child->surface_map.notify = view_child_handle_surface_map;
1103 wl_list_init(&child->surface_map.link);
1104 child->surface_unmap.notify = view_child_handle_surface_unmap;
1105 wl_list_init(&child->surface_unmap.link);
1106
1107 wl_signal_add(&view->events.unmap, &child->view_unmap);
1108 child->view_unmap.notify = view_child_handle_view_unmap;
1109
1110 struct sway_workspace *workspace = child->view->container->pending.workspace;
1111 if (workspace) {
1112 wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
1113 }
1114
1115 view_child_init_subsurfaces(child, surface);
1116}
1117
1118void view_child_destroy(struct sway_view_child *child) {
1119 if (view_child_is_mapped(child) && child->view->container != NULL) {
1120 view_child_damage(child, true);
1121 }
1122
1123 if (child->parent != NULL) {
1124 wl_list_remove(&child->link);
1125 child->parent = NULL;
1126 }
1127
1128 struct sway_view_child *subchild, *tmpchild;
1129 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) {
1130 wl_list_remove(&subchild->link);
1131 subchild->parent = NULL;
1132 // The subchild lost its parent link, so it cannot see that the parent
1133 // is unmapped. Unmap it directly.
1134 subchild->mapped = false;
1135 }
1136
1137 wl_list_remove(&child->surface_commit.link);
1138 wl_list_remove(&child->surface_destroy.link);
1139 wl_list_remove(&child->surface_map.link);
1140 wl_list_remove(&child->surface_unmap.link);
1141 wl_list_remove(&child->view_unmap.link);
1142 wl_list_remove(&child->surface_new_subsurface.link);
1143
1144 if (child->impl && child->impl->destroy) {
1145 child->impl->destroy(child);
1146 } else {
1147 free(child);
1148 } 949 }
1149} 950}
1150 951
1151struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { 952struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
1152 if (wlr_surface_is_xdg_surface(wlr_surface)) { 953 struct wlr_xdg_surface *xdg_surface;
1153 struct wlr_xdg_surface *xdg_surface = 954 if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) {
1154 wlr_xdg_surface_from_wlr_surface(wlr_surface);
1155 if (xdg_surface == NULL) {
1156 return NULL;
1157 }
1158 return view_from_wlr_xdg_surface(xdg_surface); 955 return view_from_wlr_xdg_surface(xdg_surface);
1159 } 956 }
1160#if HAVE_XWAYLAND 957#if HAVE_XWAYLAND
1161 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 958 struct wlr_xwayland_surface *xsurface;
1162 struct wlr_xwayland_surface *xsurface = 959 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
1163 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
1164 if (xsurface == NULL) {
1165 return NULL;
1166 }
1167 return view_from_wlr_xwayland_surface(xsurface); 960 return view_from_wlr_xwayland_surface(xsurface);
1168 } 961 }
1169#endif 962#endif
1170 if (wlr_surface_is_subsurface(wlr_surface)) { 963 struct wlr_subsurface *subsurface;
1171 struct wlr_subsurface *subsurface = 964 if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) {
1172 wlr_subsurface_from_wlr_surface(wlr_surface);
1173 if (subsurface == NULL) {
1174 return NULL;
1175 }
1176 return view_from_wlr_surface(subsurface->parent); 965 return view_from_wlr_surface(subsurface->parent);
1177 } 966 }
1178 if (wlr_surface_is_layer_surface(wlr_surface)) { 967 if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) {
1179 return NULL; 968 return NULL;
1180 } 969 }
1181 970
@@ -1256,6 +1045,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
1256 return len; 1045 return len;
1257} 1046}
1258 1047
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
1259void view_update_title(struct sway_view *view, bool force) { 1060void view_update_title(struct sway_view *view, bool force) {
1260 const char *title = view_get_title(view); 1061 const char *title = view_get_title(view);
1261 1062
@@ -1271,31 +1072,41 @@ void view_update_title(struct sway_view *view, bool force) {
1271 1072
1272 free(view->container->title); 1073 free(view->container->title);
1273 free(view->container->formatted_title); 1074 free(view->container->formatted_title);
1274 if (title) { 1075
1275 size_t len = parse_title_format(view, NULL); 1076 size_t len = parse_title_format(view, NULL);
1077
1078 if (len) {
1276 char *buffer = calloc(len + 1, sizeof(char)); 1079 char *buffer = calloc(len + 1, sizeof(char));
1277 if (!sway_assert(buffer, "Unable to allocate title string")) { 1080 if (!sway_assert(buffer, "Unable to allocate title string")) {
1278 return; 1081 return;
1279 } 1082 }
1280 parse_title_format(view, buffer);
1281 1083
1282 view->container->title = strdup(title); 1084 parse_title_format(view, buffer);
1283 view->container->formatted_title = buffer; 1085 view->container->formatted_title = buffer;
1284 } else { 1086 } else {
1285 view->container->title = NULL;
1286 view->container->formatted_title = NULL; 1087 view->container->formatted_title = NULL;
1287 } 1088 }
1288 container_calculate_title_height(view->container); 1089
1289 config_update_font_height(false); 1090 view->container->title = title ? strdup(title) : NULL;
1290 1091
1291 // Update title after the global font height is updated 1092 // Update title after the global font height is updated
1292 container_update_title_textures(view->container); 1093 if (view->container->title_bar.title_text && len) {
1094 sway_text_node_set_text(view->container->title_bar.title_text,
1095 view->container->formatted_title);
1096 container_arrange_title_bar(view->container);
1097 } else {
1098 container_update_title_bar(view->container);
1099 }
1293 1100
1294 ipc_event_window(view->container, "title"); 1101 ipc_event_window(view->container, "title");
1295 1102
1296 if (view->foreign_toplevel && title) { 1103 if (view->foreign_toplevel && title) {
1297 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); 1104 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title);
1298 } 1105 }
1106
1107 if (view->ext_foreign_toplevel) {
1108 update_ext_foreign_toplevel(view);
1109 }
1299} 1110}
1300 1111
1301bool view_is_visible(struct sway_view *view) { 1112bool view_is_visible(struct sway_view *view) {
@@ -1356,6 +1167,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1356 return; 1167 return;
1357 } 1168 }
1358 clock_gettime(CLOCK_MONOTONIC, &view->urgent); 1169 clock_gettime(CLOCK_MONOTONIC, &view->urgent);
1170 container_update_itself_and_parents(view->container);
1359 } else { 1171 } else {
1360 view->urgent = (struct timespec){ 0 }; 1172 view->urgent = (struct timespec){ 0 };
1361 if (view->urgent_timer) { 1173 if (view->urgent_timer) {
@@ -1363,7 +1175,6 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1363 view->urgent_timer = NULL; 1175 view->urgent_timer = NULL;
1364 } 1176 }
1365 } 1177 }
1366 container_damage_whole(view->container);
1367 1178
1368 ipc_event_window(view->container, "urgent"); 1179 ipc_event_window(view->container, "urgent");
1369 1180
@@ -1377,40 +1188,54 @@ bool view_is_urgent(struct sway_view *view) {
1377} 1188}
1378 1189
1379void view_remove_saved_buffer(struct sway_view *view) { 1190void view_remove_saved_buffer(struct sway_view *view) {
1380 if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { 1191 if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) {
1381 return; 1192 return;
1382 } 1193 }
1383 struct sway_saved_buffer *saved_buf, *tmp; 1194
1384 wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { 1195 wlr_scene_node_destroy(&view->saved_surface_tree->node);
1385 wlr_buffer_unlock(&saved_buf->buffer->base); 1196 view->saved_surface_tree = NULL;
1386 wl_list_remove(&saved_buf->link); 1197 wlr_scene_node_set_enabled(&view->content_tree->node, true);
1387 free(saved_buf);
1388 }
1389} 1198}
1390 1199
1391static void view_save_buffer_iterator(struct wlr_surface *surface, 1200static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer,
1392 int sx, int sy, void *data) { 1201 int sx, int sy, void *data) {
1393 struct sway_view *view = data; 1202 struct wlr_scene_tree *tree = data;
1394 1203
1395 if (surface && wlr_surface_has_buffer(surface)) { 1204 struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL);
1396 wlr_buffer_lock(&surface->buffer->base); 1205 if (!sbuf) {
1397 struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); 1206 sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface");
1398 saved_buffer->buffer = surface->buffer; 1207 return;
1399 saved_buffer->width = surface->current.width;
1400 saved_buffer->height = surface->current.height;
1401 saved_buffer->x = view->container->surface_x + sx;
1402 saved_buffer->y = view->container->surface_y + sy;
1403 saved_buffer->transform = surface->current.transform;
1404 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box);
1405 wl_list_insert(&view->saved_buffers, &saved_buffer->link);
1406 } 1208 }
1209
1210 wlr_scene_buffer_set_dest_size(sbuf,
1211 buffer->dst_width, buffer->dst_height);
1212 wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region);
1213 wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box);
1214 wlr_scene_node_set_position(&sbuf->node, sx, sy);
1215 wlr_scene_buffer_set_transform(sbuf, buffer->transform);
1216 wlr_scene_buffer_set_buffer(sbuf, buffer->buffer);
1407} 1217}
1408 1218
1409void view_save_buffer(struct sway_view *view) { 1219void view_save_buffer(struct sway_view *view) {
1410 if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { 1220 if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) {
1411 view_remove_saved_buffer(view); 1221 view_remove_saved_buffer(view);
1412 } 1222 }
1413 view_for_each_surface(view, view_save_buffer_iterator, view); 1223
1224 view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree);
1225 if (!view->saved_surface_tree) {
1226 sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface");
1227 return;
1228 }
1229
1230 // Enable and disable the saved surface tree like so to atomitaclly update
1231 // the tree. This will prevent over damaging or other weirdness.
1232 wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false);
1233
1234 wlr_scene_node_for_each_buffer(&view->content_tree->node,
1235 view_save_buffer_iterator, view->saved_surface_tree);
1236
1237 wlr_scene_node_set_enabled(&view->content_tree->node, false);
1238 wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true);
1414} 1239}
1415 1240
1416bool view_is_transient_for(struct sway_view *child, 1241bool view_is_transient_for(struct sway_view *child,
@@ -1418,3 +1243,19 @@ bool view_is_transient_for(struct sway_view *child,
1418 return child->impl->is_transient_for && 1243 return child->impl->is_transient_for &&
1419 child->impl->is_transient_for(child, ancestor); 1244 child->impl->is_transient_for(child, ancestor);
1420} 1245}
1246
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 8dd7789d..a68dc927 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <ctype.h> 1#include <ctype.h>
3#include <limits.h> 2#include <limits.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -50,12 +49,14 @@ struct sway_output *workspace_get_initial_output(const char *name) {
50 } else if (focus && focus->type == N_CONTAINER) { 49 } else if (focus && focus->type == N_CONTAINER) {
51 return focus->sway_container->pending.workspace->output; 50 return focus->sway_container->pending.workspace->output;
52 } 51 }
53 // Fallback to the first output or noop output for headless 52 // Fallback to the first output or the headless output
54 return root->outputs->length ? root->outputs->items[0] : root->noop_output; 53 return root->outputs->length ? root->outputs->items[0] : root->fallback_output;
55} 54}
56 55
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
@@ -844,24 +844,36 @@ struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,
844 return con; 844 return con;
845} 845}
846 846
847bool workspace_has_single_visible_container(struct sway_workspace *ws) {
848 struct sway_seat *seat = input_manager_get_default_seat();
849 struct sway_container *focus =
850 seat_get_focus_inactive_tiling(seat, ws);
851 if (focus && !focus->view) {
852 focus = seat_get_focus_inactive_view(seat, &focus->node);
853 }
854 return (focus && focus->view && view_ancestor_is_only_visible(focus->view));
855}
856
847void workspace_add_gaps(struct sway_workspace *ws) { 857void workspace_add_gaps(struct sway_workspace *ws) {
848 if (config->smart_gaps) { 858 if (config->smart_gaps == SMART_GAPS_ON
849 struct sway_seat *seat = input_manager_get_default_seat(); 859 && workspace_has_single_visible_container(ws)) {
850 struct sway_container *focus = 860 ws->current_gaps.top = 0;
851 seat_get_focus_inactive_tiling(seat, ws); 861 ws->current_gaps.right = 0;
852 if (focus && !focus->view) { 862 ws->current_gaps.bottom = 0;
853 focus = seat_get_focus_inactive_view(seat, &focus->node); 863 ws->current_gaps.left = 0;
854 } 864 return;
855 if (focus && focus->view && view_ancestor_is_only_visible(focus->view)) { 865 }
856 ws->current_gaps.top = 0; 866
857 ws->current_gaps.right = 0; 867 if (config->smart_gaps == SMART_GAPS_INVERSE_OUTER
858 ws->current_gaps.bottom = 0; 868 && !workspace_has_single_visible_container(ws)) {
859 ws->current_gaps.left = 0; 869 ws->current_gaps.top = 0;
860 return; 870 ws->current_gaps.right = 0;
861 } 871 ws->current_gaps.bottom = 0;
872 ws->current_gaps.left = 0;
873 } else {
874 ws->current_gaps = ws->gaps_outer;
862 } 875 }
863 876
864 ws->current_gaps = ws->gaps_outer;
865 // Add inner gaps and make sure we don't turn out negative 877 // Add inner gaps and make sure we don't turn out negative
866 ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); 878 ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner);
867 ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); 879 ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner);
diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c
index 6c70c785..b7c80dd4 100644
--- a/sway/xdg_activation_v1.c
+++ b/sway/xdg_activation_v1.c
@@ -1,20 +1,65 @@
1#include <wlr/types/wlr_xdg_activation_v1.h> 1#include <wlr/types/wlr_xdg_activation_v1.h>
2#include <wlr/types/wlr_xdg_shell.h>
3#include "sway/desktop/launcher.h"
2#include "sway/tree/view.h" 4#include "sway/tree/view.h"
5#include "sway/tree/workspace.h"
3 6
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 e7c3ea73..fa8c6279 100644
--- a/sway/xdg_decoration.c
+++ b/sway/xdg_decoration.c
@@ -10,7 +10,7 @@ static void xdg_decoration_handle_destroy(struct wl_listener *listener,
10 void *data) { 10 void *data) {
11 struct sway_xdg_decoration *deco = 11 struct sway_xdg_decoration *deco =
12 wl_container_of(listener, deco, destroy); 12 wl_container_of(listener, deco, destroy);
13 if(deco->view) { 13 if (deco->view) {
14 deco->view->xdg_decoration = NULL; 14 deco->view->xdg_decoration = NULL;
15 } 15 }
16 wl_list_remove(&deco->destroy.link); 16 wl_list_remove(&deco->destroy.link);
@@ -23,13 +23,12 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener,
23 void *data) { 23 void *data) {
24 struct sway_xdg_decoration *deco = 24 struct sway_xdg_decoration *deco =
25 wl_container_of(listener, deco, request_mode); 25 wl_container_of(listener, deco, request_mode);
26 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, 26 set_xdg_decoration_mode(deco);
27 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
28} 27}
29 28
30void handle_xdg_decoration(struct wl_listener *listener, void *data) { 29void handle_xdg_decoration(struct wl_listener *listener, void *data) {
31 struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; 30 struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data;
32 struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->surface->data; 31 struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->toplevel->base->data;
33 32
34 struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); 33 struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco));
35 if (deco == NULL) { 34 if (deco == NULL) {
@@ -48,16 +47,46 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data) {
48 47
49 wl_list_insert(&server.xdg_decorations, &deco->link); 48 wl_list_insert(&server.xdg_decorations, &deco->link);
50 49
51 xdg_decoration_handle_request_mode(&deco->request_mode, wlr_deco); 50 set_xdg_decoration_mode(deco);
52} 51}
53 52
54struct sway_xdg_decoration *xdg_decoration_from_surface( 53struct sway_xdg_decoration *xdg_decoration_from_surface(
55 struct wlr_surface *surface) { 54 struct wlr_surface *surface) {
56 struct sway_xdg_decoration *deco; 55 struct sway_xdg_decoration *deco;
57 wl_list_for_each(deco, &server.xdg_decorations, link) { 56 wl_list_for_each(deco, &server.xdg_decorations, link) {
58 if (deco->wlr_xdg_decoration->surface->surface == surface) { 57 if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) {
59 return deco; 58 return deco;
60 } 59 }
61 } 60 }
62 return NULL; 61 return NULL;
63} 62}
63
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 15eab782..5b1213a8 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <errno.h> 2#include <errno.h>
4#include <fcntl.h> 3#include <fcntl.h>
@@ -51,10 +50,6 @@ static void swaybar_output_free(struct swaybar_output *output) {
51 if (output->surface != NULL) { 50 if (output->surface != NULL) {
52 wl_surface_destroy(output->surface); 51 wl_surface_destroy(output->surface);
53 } 52 }
54 if (output->input_region != NULL) {
55 wl_region_destroy(output->input_region);
56 }
57 zxdg_output_v1_destroy(output->xdg_output);
58 wl_output_destroy(output->output); 53 wl_output_destroy(output->output);
59 destroy_buffer(&output->buffers[0]); 54 destroy_buffer(&output->buffers[0]);
60 destroy_buffer(&output->buffers[1]); 55 destroy_buffer(&output->buffers[1]);
@@ -114,10 +109,9 @@ static void add_layer_surface(struct swaybar_output *output) {
114 109
115 if (overlay) { 110 if (overlay) {
116 // Empty input region 111 // Empty input region
117 output->input_region = wl_compositor_create_region(bar->compositor); 112 struct wl_region *region = wl_compositor_create_region(bar->compositor);
118 assert(output->input_region); 113 wl_surface_set_input_region(output->surface, region);
119 114 wl_region_destroy(region);
120 wl_surface_set_input_region(output->surface, output->input_region);
121 } 115 }
122 116
123 zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position); 117 zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position);
@@ -172,7 +166,7 @@ bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) {
172 if (bar->status) { 166 if (bar->status) {
173 sway_log(SWAY_DEBUG, "Sending %s signal to status command", 167 sway_log(SWAY_DEBUG, "Sending %s signal to status command",
174 visible ? "cont" : "stop"); 168 visible ? "cont" : "stop");
175 kill(bar->status->pid, visible ? 169 kill(-bar->status->pid, visible ?
176 bar->status->cont_signal : bar->status->stop_signal); 170 bar->status->cont_signal : bar->status->stop_signal);
177 } 171 }
178 } 172 }
@@ -367,6 +361,9 @@ static void handle_global(void *data, struct wl_registry *registry,
367 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { 361 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
368 bar->xdg_output_manager = wl_registry_bind(registry, name, 362 bar->xdg_output_manager = wl_registry_bind(registry, name,
369 &zxdg_output_manager_v1_interface, 2); 363 &zxdg_output_manager_v1_interface, 2);
364 } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
365 bar->cursor_shape_manager = wl_registry_bind(registry, name,
366 &wp_cursor_shape_manager_v1_interface, 1);
370 } 367 }
371} 368}
372 369
@@ -430,15 +427,17 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) {
430 // Second roundtrip for xdg-output 427 // Second roundtrip for xdg-output
431 wl_display_roundtrip(bar->display); 428 wl_display_roundtrip(bar->display);
432 429
433 struct swaybar_seat *seat; 430 if (!bar->cursor_shape_manager) {
434 wl_list_for_each(seat, &bar->seats, link) { 431 struct swaybar_seat *seat;
435 struct swaybar_pointer *pointer = &seat->pointer; 432 wl_list_for_each(seat, &bar->seats, link) {
436 if (!pointer) { 433 struct swaybar_pointer *pointer = &seat->pointer;
437 continue; 434 if (!pointer) {
435 continue;
436 }
437 pointer->cursor_surface =
438 wl_compositor_create_surface(bar->compositor);
439 assert(pointer->cursor_surface);
438 } 440 }
439 pointer->cursor_surface =
440 wl_compositor_create_surface(bar->compositor);
441 assert(pointer->cursor_surface);
442 } 441 }
443 442
444 if (bar->config->status_command) { 443 if (bar->config->status_command) {
diff --git a/swaybar/config.c b/swaybar/config.c
index abedaec0..55bfcb72 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <string.h> 2#include <string.h>
4#include "swaybar/config.h" 3#include "swaybar/config.h"
@@ -26,7 +25,7 @@ struct swaybar_config *init_config(void) {
26 config->status_command = NULL; 25 config->status_command = NULL;
27 config->pango_markup = false; 26 config->pango_markup = false;
28 config->position = parse_position("bottom"); 27 config->position = parse_position("bottom");
29 config->font = strdup("monospace 10"); 28 config->font_description = pango_font_description_from_string("monospace 10");
30 config->mode = strdup("dock"); 29 config->mode = strdup("dock");
31 config->hidden_state = strdup("hide"); 30 config->hidden_state = strdup("hide");
32 config->sep_symbol = NULL; 31 config->sep_symbol = NULL;
@@ -105,7 +104,7 @@ void free_tray_binding(struct tray_binding *binding) {
105 104
106void free_config(struct swaybar_config *config) { 105void free_config(struct swaybar_config *config) {
107 free(config->status_command); 106 free(config->status_command);
108 free(config->font); 107 pango_font_description_free(config->font_description);
109 free(config->mode); 108 free(config->mode);
110 free(config->hidden_state); 109 free(config->hidden_state);
111 free(config->sep_symbol); 110 free(config->sep_symbol);
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index 4bcd5843..62c22d43 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <json.h> 1#include <json.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <ctype.h> 3#include <ctype.h>
@@ -28,6 +27,19 @@ void i3bar_block_unref(struct i3bar_block *block) {
28 } 27 }
29} 28}
30 29
30static bool i3bar_parse_json_color(json_object *json, uint32_t *color) {
31 if (!json) {
32 return false;
33 }
34
35 const char *hexstring = json_object_get_string(json);
36 bool color_set = parse_color(hexstring, color);
37 if (!color_set) {
38 sway_log(SWAY_ERROR, "Ignoring invalid block hexadecimal color string: %s", hexstring);
39 }
40 return color_set;
41}
42
31static void i3bar_parse_json(struct status_line *status, 43static void i3bar_parse_json(struct status_line *status,
32 struct json_object *json_array) { 44 struct json_object *json_array) {
33 struct i3bar_block *block, *tmp; 45 struct i3bar_block *block, *tmp;
@@ -68,13 +80,7 @@ static void i3bar_parse_json(struct status_line *status,
68 strdup(json_object_get_string(full_text)) : NULL; 80 strdup(json_object_get_string(full_text)) : NULL;
69 block->short_text = short_text ? 81 block->short_text = short_text ?
70 strdup(json_object_get_string(short_text)) : NULL; 82 strdup(json_object_get_string(short_text)) : NULL;
71 if (color) { 83 block->color_set = i3bar_parse_json_color(color, &block->color);
72 const char *hexstring = json_object_get_string(color);
73 block->color_set = parse_color(hexstring, &block->color);
74 if (!block->color_set) {
75 sway_log(SWAY_ERROR, "Invalid block color: %s", hexstring);
76 }
77 }
78 if (min_width) { 84 if (min_width) {
79 json_type type = json_object_get_type(min_width); 85 json_type type = json_object_get_type(min_width);
80 if (type == json_type_int) { 86 if (type == json_type_int) {
@@ -100,14 +106,8 @@ static void i3bar_parse_json(struct status_line *status,
100 block->separator_block_width = separator_block_width ? 106 block->separator_block_width = separator_block_width ?
101 json_object_get_int(separator_block_width) : 9; 107 json_object_get_int(separator_block_width) : 9;
102 // Airblader features 108 // Airblader features
103 const char *hex = background ? json_object_get_string(background) : NULL; 109 i3bar_parse_json_color(background, &block->background);
104 if (hex && !parse_color(hex, &block->background)) { 110 block->border_set = i3bar_parse_json_color(border, &block->border);
105 sway_log(SWAY_ERROR, "Ignoring invalid block background: %s", hex);
106 }
107 hex = border ? json_object_get_string(border) : NULL;
108 if (hex && !parse_color(hex, &block->border)) {
109 sway_log(SWAY_ERROR, "Ignoring invalid block border: %s", hex);
110 }
111 block->border_top = border_top ? json_object_get_int(border_top) : 1; 111 block->border_top = border_top ? json_object_get_int(border_top) : 1;
112 block->border_bottom = border_bottom ? 112 block->border_bottom = border_bottom ?
113 json_object_get_int(border_bottom) : 1; 113 json_object_get_int(border_bottom) : 1;
@@ -268,11 +268,16 @@ bool i3bar_handle_readable(struct status_line *status) {
268 268
269enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, 269enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
270 struct i3bar_block *block, double x, double y, double rx, double ry, 270 struct i3bar_block *block, double x, double y, double rx, double ry,
271 double w, double h, int scale, uint32_t button) { 271 double w, double h, int scale, uint32_t button, bool released) {
272 sway_log(SWAY_DEBUG, "block %s clicked", block->name); 272 sway_log(SWAY_DEBUG, "block %s clicked", block->name);
273 if (!block->name || !status->click_events) { 273 if (!block->name || !status->click_events) {
274 return HOTSPOT_PROCESS; 274 return HOTSPOT_PROCESS;
275 } 275 }
276 if (released) {
277 // Since we handle the pressed event, also handle the released event
278 // to block it from falling through to a binding in the bar
279 return HOTSPOT_IGNORE;
280 }
276 281
277 struct json_object *event_json = json_object_new_object(); 282 struct json_object *event_json = json_object_new_object();
278 json_object_object_add(event_json, "name", 283 json_object_object_add(event_json, "name",
diff --git a/common/background-image.c b/swaybar/image.c
index 994a0805..ed24b9f9 100644
--- a/common/background-image.c
+++ b/swaybar/image.c
@@ -1,29 +1,12 @@
1#include <assert.h> 1#include <assert.h>
2#include "background-image.h" 2#include "config.h"
3#include "cairo_util.h"
4#include "log.h" 3#include "log.h"
4#include "swaybar/image.h"
5
5#if HAVE_GDK_PIXBUF 6#if HAVE_GDK_PIXBUF
6#include <gdk-pixbuf/gdk-pixbuf.h> 7#include <gdk-pixbuf/gdk-pixbuf.h>
7#endif 8#endif
8 9
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 6e13f177..ada4bc86 100644
--- a/swaybar/input.c
+++ b/swaybar/input.c
@@ -81,8 +81,16 @@ void update_cursor(struct swaybar_seat *seat) {
81 int scale = pointer->current ? pointer->current->scale : 1; 81 int scale = pointer->current ? pointer->current->scale : 1;
82 pointer->cursor_theme = wl_cursor_theme_load( 82 pointer->cursor_theme = wl_cursor_theme_load(
83 cursor_theme, cursor_size * scale, seat->bar->shm); 83 cursor_theme, cursor_size * scale, seat->bar->shm);
84 if (!pointer->cursor_theme) {
85 sway_log(SWAY_ERROR, "Failed to load cursor theme");
86 return;
87 }
84 struct wl_cursor *cursor; 88 struct wl_cursor *cursor;
85 cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); 89 cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default");
90 if (!cursor) {
91 sway_log(SWAY_ERROR, "Failed to get default cursor from theme");
92 return;
93 }
86 pointer->cursor_image = cursor->images[0]; 94 pointer->cursor_image = cursor->images[0];
87 wl_surface_set_buffer_scale(pointer->cursor_surface, scale); 95 wl_surface_set_buffer_scale(pointer->cursor_surface, scale);
88 wl_surface_attach(pointer->cursor_surface, 96 wl_surface_attach(pointer->cursor_surface,
@@ -103,7 +111,7 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
103 struct swaybar_pointer *pointer = &seat->pointer; 111 struct swaybar_pointer *pointer = &seat->pointer;
104 seat->pointer.x = wl_fixed_to_double(surface_x); 112 seat->pointer.x = wl_fixed_to_double(surface_x);
105 seat->pointer.y = wl_fixed_to_double(surface_y); 113 seat->pointer.y = wl_fixed_to_double(surface_y);
106 pointer->serial = serial; 114
107 struct swaybar_output *output; 115 struct swaybar_output *output;
108 wl_list_for_each(output, &seat->bar->outputs, link) { 116 wl_list_for_each(output, &seat->bar->outputs, link) {
109 if (output->surface == surface) { 117 if (output->surface == surface) {
@@ -111,7 +119,18 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
111 break; 119 break;
112 } 120 }
113 } 121 }
114 update_cursor(seat); 122
123 if (seat->bar->cursor_shape_manager) {
124 struct wp_cursor_shape_device_v1 *device =
125 wp_cursor_shape_manager_v1_get_pointer(
126 seat->bar->cursor_shape_manager, wl_pointer);
127 wp_cursor_shape_device_v1_set_shape(device, serial,
128 WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
129 wp_cursor_shape_device_v1_destroy(device);
130 } else {
131 pointer->serial = serial;
132 update_cursor(seat);
133 }
115} 134}
116 135
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,16 +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) {
145 double px = x * output->scale; 164 bool released = state == WL_POINTER_BUTTON_STATE_RELEASED;
146 double py = y * output->scale;
147 struct swaybar_hotspot *hotspot; 165 struct swaybar_hotspot *hotspot;
148 wl_list_for_each(hotspot, &output->hotspots, link) { 166 wl_list_for_each(hotspot, &output->hotspots, link) {
149 if (px >= hotspot->x && py >= hotspot->y 167 if (x >= hotspot->x && y >= hotspot->y
150 && px < hotspot->x + hotspot->width 168 && x < hotspot->x + hotspot->width
151 && py < hotspot->y + hotspot->height) { 169 && y < hotspot->y + hotspot->height) {
152 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, 170 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y,
153 button, hotspot->data)) { 171 button, released, hotspot->data)) {
154 return true; 172 return true;
155 } 173 }
156 } 174 }
@@ -168,14 +186,11 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
168 return; 186 return;
169 } 187 }
170 188
171 if (check_bindings(seat->bar, button, state)) { 189 if (process_hotspots(output, pointer->x, pointer->y, button, state)) {
172 return; 190 return;
173 } 191 }
174 192
175 if (state != WL_POINTER_BUTTON_STATE_PRESSED) { 193 check_bindings(seat->bar, button, state);
176 return;
177 }
178 process_hotspots(output, pointer->x, pointer->y, button);
179} 194}
180 195
181static void workspace_next(struct swaybar *bar, struct swaybar_output *output, 196static void workspace_next(struct swaybar *bar, struct swaybar_output *output,
@@ -211,7 +226,7 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output,
211 } 226 }
212 } 227 }
213 228
214 if (new) { 229 if (new && new != active) {
215 ipc_send_workspace_command(bar, new->name); 230 ipc_send_workspace_command(bar, new->name);
216 231
217 // Since we're asking Sway to switch to 'new', it should become visible. 232 // Since we're asking Sway to switch to 'new', it should become visible.
@@ -224,15 +239,15 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output,
224static void process_discrete_scroll(struct swaybar_seat *seat, 239static void process_discrete_scroll(struct swaybar_seat *seat,
225 struct swaybar_output *output, struct swaybar_pointer *pointer, 240 struct swaybar_output *output, struct swaybar_pointer *pointer,
226 uint32_t axis, wl_fixed_t value) { 241 uint32_t axis, wl_fixed_t value) {
227 // If there is a button press binding, execute it, skip default behavior,
228 // and check button release bindings
229 uint32_t button = wl_axis_to_button(axis, value); 242 uint32_t button = wl_axis_to_button(axis, value);
230 if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) { 243 if (process_hotspots(output, pointer->x, pointer->y, button, WL_POINTER_BUTTON_STATE_PRESSED)) {
231 check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED); 244 // (Currently hotspots don't do anything on release events, so no need to emit one)
232 return; 245 return;
233 } 246 }
234 247
235 if (process_hotspots(output, pointer->x, pointer->y, button)) { 248 // If there is a button press binding, execute it, and check button release bindings
249 if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) {
250 check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED);
236 return; 251 return;
237 } 252 }
238 253
@@ -405,7 +420,8 @@ static void wl_touch_up(void *data, struct wl_touch *wl_touch,
405 } 420 }
406 if (time - slot->time < 500) { 421 if (time - slot->time < 500) {
407 // Tap, treat it like a pointer click 422 // Tap, treat it like a pointer click
408 process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT); 423 process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
424 // (Currently hotspots don't do anything on release events, so no need to emit one)
409 } 425 }
410 slot->output = NULL; 426 slot->output = NULL;
411} 427}
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index a64aa1ab..03500bdf 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <limits.h> 1#include <limits.h>
3#include <poll.h> 2#include <poll.h>
4#include <stdio.h> 3#include <stdio.h>
@@ -147,8 +146,10 @@ static bool ipc_parse_config(
147 146
148 json_object *font = json_object_object_get(bar_config, "font"); 147 json_object *font = json_object_object_get(bar_config, "font");
149 if (font) { 148 if (font) {
150 free(config->font); 149 pango_font_description_free(config->font_description);
151 config->font = parse_font(json_object_get_string(font)); 150 char *font_value = parse_font(json_object_get_string(font));
151 config->font_description = pango_font_description_from_string(font_value);
152 free(font_value);
152 } 153 }
153 154
154 json_object *gaps = json_object_object_get(bar_config, "gaps"); 155 json_object *gaps = json_object_object_get(bar_config, "gaps");
@@ -424,12 +425,9 @@ bool ipc_initialize(struct swaybar *bar) {
424 } 425 }
425 free(res); 426 free(res);
426 427
427 struct swaybar_config *config = bar->config; 428 char *subscribe =
428 char subscribe[128]; // suitably large buffer 429 "[ \"barconfig_update\", \"bar_state_update\", \"mode\", \"workspace\" ]";
429 len = snprintf(subscribe, 128, 430 len = strlen(subscribe);
430 "[ \"barconfig_update\" , \"bar_state_update\" %s %s ]",
431 config->binding_mode_indicator ? ", \"mode\"" : "",
432 config->workspace_buttons ? ", \"workspace\"" : "");
433 free(ipc_single_command(bar->ipc_event_socketfd, 431 free(ipc_single_command(bar->ipc_event_socketfd,
434 IPC_SUBSCRIBE, subscribe, &len)); 432 IPC_SUBSCRIBE, subscribe, &len));
435 return true; 433 return true;
@@ -485,8 +483,7 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload,
485 destroy_layer_surface(output); 483 destroy_layer_surface(output);
486 wl_list_remove(&output->link); 484 wl_list_remove(&output->link);
487 wl_list_insert(&bar->unused_outputs, &output->link); 485 wl_list_insert(&bar->unused_outputs, &output->link);
488 } else if (!oldcfg->font || !newcfg->font || 486 } else if (!pango_font_description_equal(oldcfg->font_description, newcfg->font_description)) {
489 strcmp(oldcfg->font, newcfg->font) != 0) {
490 output->height = 0; // force update height 487 output->height = 0; // force update height
491 } 488 }
492 } 489 }
@@ -550,7 +547,7 @@ bool handle_ipc_readable(struct swaybar *bar) {
550 // The default depth of 32 is too small to represent some nested layouts, but 547 // The default depth of 32 is too small to represent some nested layouts, but
551 // we can't pass INT_MAX here because json-c (as of this writing) prefaults 548 // we can't pass INT_MAX here because json-c (as of this writing) prefaults
552 // all the memory for its stack. 549 // all the memory for its stack.
553 json_tokener *tok = json_tokener_new_ex(256); 550 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
554 if (!tok) { 551 if (!tok) {
555 sway_log_errno(SWAY_ERROR, "failed to create tokener"); 552 sway_log_errno(SWAY_ERROR, "failed to create tokener");
556 free_ipc_response(resp); 553 free_ipc_response(resp);
diff --git a/swaybar/main.c b/swaybar/main.c
index a44c1e63..3dc67233 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
diff --git a/swaybar/meson.build b/swaybar/meson.build
index 9feb3cd2..34bbdeea 100644
--- a/swaybar/meson.build
+++ b/swaybar/meson.build
@@ -8,7 +8,6 @@ tray_files = have_tray ? [
8 8
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 fcc8be1d..879a4e42 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <limits.h> 3#include <limits.h>
@@ -14,6 +13,7 @@
14#include "swaybar/ipc.h" 13#include "swaybar/ipc.h"
15#include "swaybar/render.h" 14#include "swaybar/render.h"
16#include "swaybar/status_line.h" 15#include "swaybar/status_line.h"
16#include "log.h"
17#if HAVE_TRAY 17#if HAVE_TRAY
18#include "swaybar/tray/tray.h" 18#include "swaybar/tray/tray.h"
19#endif 19#endif
@@ -53,22 +53,21 @@ static uint32_t render_status_line_error(struct render_context *ctx, double *x)
53 return 0; 53 return 0;
54 } 54 }
55 55
56 uint32_t height = output->height * output->scale; 56 uint32_t height = output->height;
57 57
58 cairo_t *cairo = ctx->cairo; 58 cairo_t *cairo = ctx->cairo;
59 cairo_set_source_u32(cairo, 0xFF0000FF); 59 cairo_set_source_u32(cairo, 0xFF0000FF);
60 60
61 int margin = 3 * output->scale; 61 int margin = 3;
62 double ws_vertical_padding = 62 double ws_vertical_padding = output->bar->config->status_padding;
63 output->bar->config->status_padding * output->scale;
64 63
65 char *font = output->bar->config->font; 64 PangoFontDescription *font = output->bar->config->font_description;
66 int text_width, text_height; 65 int text_width, text_height;
67 get_text_size(cairo, font, &text_width, &text_height, NULL, 66 get_text_size(cairo, font, &text_width, &text_height, NULL,
68 output->scale, false, "%s", error); 67 1, false, "%s", error);
69 68
70 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 69 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
71 uint32_t ideal_surface_height = ideal_height / output->scale; 70 uint32_t ideal_surface_height = ideal_height;
72 if (!output->bar->config->height && 71 if (!output->bar->config->height &&
73 output->height < ideal_surface_height) { 72 output->height < ideal_surface_height) {
74 return ideal_surface_height; 73 return ideal_surface_height;
@@ -78,7 +77,7 @@ static uint32_t render_status_line_error(struct render_context *ctx, double *x)
78 double text_y = height / 2.0 - text_height / 2.0; 77 double text_y = height / 2.0 - text_height / 2.0;
79 cairo_move_to(cairo, *x, (int)floor(text_y)); 78 cairo_move_to(cairo, *x, (int)floor(text_y));
80 choose_text_aa_mode(ctx, 0xFF0000FF); 79 choose_text_aa_mode(ctx, 0xFF0000FF);
81 pango_printf(cairo, font, output->scale, false, "%s", error); 80 render_text(cairo, font, 1, false, "%s", error);
82 *x -= margin; 81 *x -= margin;
83 return output->height; 82 return output->height;
84} 83}
@@ -97,26 +96,25 @@ static uint32_t render_status_line_text(struct render_context *ctx, double *x) {
97 cairo_set_source_u32(cairo, fontcolor); 96 cairo_set_source_u32(cairo, fontcolor);
98 97
99 int text_width, text_height; 98 int text_width, text_height;
100 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 99 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
101 output->scale, config->pango_markup, "%s", text); 100 1, config->pango_markup, "%s", text);
102 101
103 double ws_vertical_padding = config->status_padding * output->scale; 102 double ws_vertical_padding = config->status_padding;
104 int margin = 3 * output->scale; 103 int margin = 3;
105 104
106 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 105 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
107 uint32_t ideal_surface_height = ideal_height / output->scale; 106 uint32_t ideal_surface_height = ideal_height;
108 if (!output->bar->config->height && 107 if (!output->bar->config->height &&
109 output->height < ideal_surface_height) { 108 output->height < ideal_surface_height) {
110 return ideal_surface_height; 109 return ideal_surface_height;
111 } 110 }
112 111
113 *x -= text_width + margin; 112 *x -= text_width + margin;
114 uint32_t height = output->height * output->scale; 113 uint32_t height = output->height;
115 double text_y = height / 2.0 - text_height / 2.0; 114 double text_y = height / 2.0 - text_height / 2.0;
116 cairo_move_to(cairo, *x, (int)floor(text_y)); 115 cairo_move_to(cairo, *x, (int)floor(text_y));
117 choose_text_aa_mode(ctx, fontcolor); 116 choose_text_aa_mode(ctx, fontcolor);
118 pango_printf(cairo, config->font, output->scale, 117 render_text(cairo, config->font_description, 1, config->pango_markup, "%s", text);
119 config->pango_markup, "%s", text);
120 *x -= margin; 118 *x -= margin;
121 return output->height; 119 return output->height;
122} 120}
@@ -161,15 +159,15 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
161 159
162static enum hotspot_event_handling block_hotspot_callback( 160static enum hotspot_event_handling block_hotspot_callback(
163 struct swaybar_output *output, struct swaybar_hotspot *hotspot, 161 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
164 double x, double y, uint32_t button, void *data) { 162 double x, double y, uint32_t button, bool released, void *data) {
165 struct i3bar_block *block = data; 163 struct i3bar_block *block = data;
166 struct status_line *status = output->bar->status; 164 struct status_line *status = output->bar->status;
167 return i3bar_block_send_click(status, block, x, y, 165 return i3bar_block_send_click(status, block, x, y,
168 x - (double)hotspot->x / output->scale, 166 x - (double)hotspot->x,
169 y - (double)hotspot->y / output->scale, 167 y - (double)hotspot->y,
170 (double)hotspot->width / output->scale, 168 (double)hotspot->width,
171 (double)hotspot->height / output->scale, 169 (double)hotspot->height,
172 output->scale, button); 170 output->scale, button, released);
173} 171}
174 172
175static void i3bar_block_unref_callback(void *data) { 173static void i3bar_block_unref_callback(void *data) {
@@ -191,17 +189,17 @@ static uint32_t render_status_block(struct render_context *ctx,
191 struct swaybar_output *output = ctx->output; 189 struct swaybar_output *output = ctx->output;
192 struct swaybar_config *config = output->bar->config; 190 struct swaybar_config *config = output->bar->config;
193 int text_width, text_height; 191 int text_width, text_height;
194 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 192 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,
195 output->scale, block->markup, "%s", text); 193 block->markup, "%s", text);
196 194
197 int margin = 3 * output->scale; 195 int margin = 3;
198 double ws_vertical_padding = config->status_padding * output->scale; 196 double ws_vertical_padding = config->status_padding;
199 197
200 int width = text_width; 198 int width = text_width;
201 if (block->min_width_str) { 199 if (block->min_width_str) {
202 int w; 200 int w;
203 get_text_size(cairo, config->font, &w, NULL, NULL, 201 get_text_size(cairo, config->font_description, &w, NULL, NULL, 1, block->markup,
204 output->scale, block->markup, "%s", block->min_width_str); 202 "%s", block->min_width_str);
205 block->min_width = w; 203 block->min_width = w;
206 } 204 }
207 if (width < block->min_width) { 205 if (width < block->min_width) {
@@ -210,30 +208,30 @@ static uint32_t render_status_block(struct render_context *ctx,
210 208
211 double block_width = width; 209 double block_width = width;
212 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 210 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
213 uint32_t ideal_surface_height = ideal_height / output->scale; 211 uint32_t ideal_surface_height = ideal_height;
214 if (!output->bar->config->height && 212 if (!output->bar->config->height &&
215 output->height < ideal_surface_height) { 213 output->height < ideal_surface_height) {
216 return ideal_surface_height; 214 return ideal_surface_height;
217 } 215 }
218 216
219 *x -= width; 217 *x -= width;
220 if ((block->border || block->urgent) && block->border_left > 0) { 218 if ((block->border_set || block->urgent) && block->border_left > 0) {
221 *x -= (block->border_left * output->scale + margin); 219 *x -= (block->border_left + margin);
222 block_width += block->border_left * output->scale + margin; 220 block_width += block->border_left + margin;
223 } 221 }
224 if ((block->border || block->urgent) && block->border_right > 0) { 222 if ((block->border_set || block->urgent) && block->border_right > 0) {
225 *x -= (block->border_right * output->scale + margin); 223 *x -= (block->border_right + margin);
226 block_width += block->border_right * output->scale + margin; 224 block_width += block->border_right + margin;
227 } 225 }
228 226
229 int sep_width, sep_height; 227 int sep_width, sep_height;
230 int sep_block_width = block->separator_block_width; 228 int sep_block_width = block->separator_block_width;
231 if (!edge) { 229 if (!edge) {
232 if (config->sep_symbol) { 230 if (config->sep_symbol) {
233 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 231 get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL,
234 output->scale, false, "%s", config->sep_symbol); 232 1, false, "%s", config->sep_symbol);
235 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 233 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
236 uint32_t _ideal_surface_height = _ideal_height / output->scale; 234 uint32_t _ideal_surface_height = _ideal_height;
237 if (!output->bar->config->height && 235 if (!output->bar->config->height &&
238 output->height < _ideal_surface_height) { 236 output->height < _ideal_surface_height) {
239 return _ideal_surface_height; 237 return _ideal_surface_height;
@@ -244,10 +242,10 @@ static uint32_t render_status_block(struct render_context *ctx,
244 } 242 }
245 *x -= sep_block_width; 243 *x -= sep_block_width;
246 } else if (config->status_edge_padding) { 244 } else if (config->status_edge_padding) {
247 *x -= config->status_edge_padding * output->scale; 245 *x -= config->status_edge_padding;
248 } 246 }
249 247
250 uint32_t height = output->height * output->scale; 248 uint32_t height = output->height;
251 if (output->bar->status->click_events) { 249 if (output->bar->status->click_events) {
252 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 250 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
253 hotspot->x = *x; 251 hotspot->x = *x;
@@ -275,23 +273,25 @@ static uint32_t render_status_block(struct render_context *ctx,
275 273
276 uint32_t border_color = block->urgent 274 uint32_t border_color = block->urgent
277 ? config->colors.urgent_workspace.border : block->border; 275 ? config->colors.urgent_workspace.border : block->border;
278 if (border_color && block->border_top > 0) { 276 if (block->border_set || block->urgent) {
279 render_sharp_line(cairo, border_color, x_pos, y_pos, 277 if (block->border_top > 0) {
280 block_width, block->border_top * output->scale); 278 render_sharp_line(cairo, border_color, x_pos, y_pos,
281 } 279 block_width, block->border_top);
282 if (border_color && block->border_bottom > 0) { 280 }
283 render_sharp_line(cairo, border_color, x_pos, 281 if (block->border_bottom > 0) {
284 y_pos + render_height - block->border_bottom * output->scale, 282 render_sharp_line(cairo, border_color, x_pos,
285 block_width, block->border_bottom * output->scale); 283 y_pos + render_height - block->border_bottom,
286 } 284 block_width, block->border_bottom);
287 if (border_color && block->border_left > 0) { 285 }
288 render_sharp_line(cairo, border_color, x_pos, y_pos, 286 if (block->border_left > 0) {
289 block->border_left * output->scale, render_height); 287 render_sharp_line(cairo, border_color, x_pos, y_pos,
290 x_pos += block->border_left * output->scale + margin; 288 block->border_left, render_height);
289 }
290 x_pos += block->border_left + margin;
291 } 291 }
292 292
293 double offset = 0; 293 double offset = 0;
294 if (strncmp(block->align, "left", 5) == 0) { 294 if (strncmp(block->align, "left", 4) == 0) {
295 offset = x_pos; 295 offset = x_pos;
296 } else if (strncmp(block->align, "right", 5) == 0) { 296 } else if (strncmp(block->align, "right", 5) == 0) {
297 offset = x_pos + width - text_width; 297 offset = x_pos + width - text_width;
@@ -306,15 +306,16 @@ static uint32_t render_status_block(struct render_context *ctx,
306 color = block->urgent ? config->colors.urgent_workspace.text : color; 306 color = block->urgent ? config->colors.urgent_workspace.text : color;
307 cairo_set_source_u32(cairo, color); 307 cairo_set_source_u32(cairo, color);
308 choose_text_aa_mode(ctx, color); 308 choose_text_aa_mode(ctx, color);
309 pango_printf(cairo, config->font, output->scale, 309 render_text(cairo, config->font_description, 1, block->markup, "%s", text);
310 block->markup, "%s", text);
311 x_pos += width; 310 x_pos += width;
312 311
313 if (block->border && block->border_right > 0) { 312 if (block->border_set || block->urgent) {
314 x_pos += margin; 313 x_pos += margin;
315 render_sharp_line(cairo, border_color, x_pos, y_pos, 314 if (block->border_right > 0) {
316 block->border_right * output->scale, render_height); 315 render_sharp_line(cairo, border_color, x_pos, y_pos,
317 x_pos += block->border_right * output->scale; 316 block->border_right, render_height);
317 }
318 x_pos += block->border_right;
318 } 319 }
319 320
320 if (!edge && block->separator) { 321 if (!edge && block->separator) {
@@ -329,7 +330,7 @@ static uint32_t render_status_block(struct render_context *ctx,
329 double sep_y = height / 2.0 - sep_height / 2.0; 330 double sep_y = height / 2.0 - sep_height / 2.0;
330 cairo_move_to(cairo, offset, (int)floor(sep_y)); 331 cairo_move_to(cairo, offset, (int)floor(sep_y));
331 choose_text_aa_mode(ctx, color); 332 choose_text_aa_mode(ctx, color);
332 pango_printf(cairo, config->font, output->scale, false, 333 render_text(cairo, config->font_description, 1, false,
333 "%s", config->sep_symbol); 334 "%s", config->sep_symbol);
334 } else { 335 } else {
335 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 336 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
@@ -352,18 +353,18 @@ static void predict_status_block_pos(cairo_t *cairo,
352 struct swaybar_config *config = output->bar->config; 353 struct swaybar_config *config = output->bar->config;
353 354
354 int text_width, text_height; 355 int text_width, text_height;
355 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 356 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,
356 output->scale, block->markup, "%s", block->full_text); 357 block->markup, "%s", block->full_text);
357 358
358 int margin = 3 * output->scale; 359 int margin = 3;
359 double ws_vertical_padding = config->status_padding * output->scale; 360 double ws_vertical_padding = config->status_padding;
360 361
361 int width = text_width; 362 int width = text_width;
362 363
363 if (block->min_width_str) { 364 if (block->min_width_str) {
364 int w; 365 int w;
365 get_text_size(cairo, config->font, &w, NULL, NULL, 366 get_text_size(cairo, config->font_description, &w, NULL, NULL,
366 output->scale, block->markup, "%s", block->min_width_str); 367 1, block->markup, "%s", block->min_width_str);
367 block->min_width = w; 368 block->min_width = w;
368 } 369 }
369 if (width < block->min_width) { 370 if (width < block->min_width) {
@@ -371,28 +372,28 @@ static void predict_status_block_pos(cairo_t *cairo,
371 } 372 }
372 373
373 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 374 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
374 uint32_t ideal_surface_height = ideal_height / output->scale; 375 uint32_t ideal_surface_height = ideal_height;
375 if (!output->bar->config->height && 376 if (!output->bar->config->height &&
376 output->height < ideal_surface_height) { 377 output->height < ideal_surface_height) {
377 return; 378 return;
378 } 379 }
379 380
380 *x -= width; 381 *x -= width;
381 if ((block->border || block->urgent) && block->border_left > 0) { 382 if ((block->border_set || block->urgent) && block->border_left > 0) {
382 *x -= (block->border_left * output->scale + margin); 383 *x -= (block->border_left + margin);
383 } 384 }
384 if ((block->border || block->urgent) && block->border_right > 0) { 385 if ((block->border_set || block->urgent) && block->border_right > 0) {
385 *x -= (block->border_right * output->scale + margin); 386 *x -= (block->border_right + margin);
386 } 387 }
387 388
388 int sep_width, sep_height; 389 int sep_width, sep_height;
389 int sep_block_width = block->separator_block_width; 390 int sep_block_width = block->separator_block_width;
390 if (!edge) { 391 if (!edge) {
391 if (config->sep_symbol) { 392 if (config->sep_symbol) {
392 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 393 get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL,
393 output->scale, false, "%s", config->sep_symbol); 394 1, false, "%s", config->sep_symbol);
394 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 395 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
395 uint32_t _ideal_surface_height = _ideal_height / output->scale; 396 uint32_t _ideal_surface_height = _ideal_height;
396 if (!output->bar->config->height && 397 if (!output->bar->config->height &&
397 output->height < _ideal_surface_height) { 398 output->height < _ideal_surface_height) {
398 return; 399 return;
@@ -403,13 +404,13 @@ static void predict_status_block_pos(cairo_t *cairo,
403 } 404 }
404 *x -= sep_block_width; 405 *x -= sep_block_width;
405 } else if (config->status_edge_padding) { 406 } else if (config->status_edge_padding) {
406 *x -= config->status_edge_padding * output->scale; 407 *x -= config->status_edge_padding;
407 } 408 }
408} 409}
409 410
410static double predict_status_line_pos(cairo_t *cairo, 411static double predict_status_line_pos(cairo_t *cairo,
411 struct swaybar_output *output, double x) { 412 struct swaybar_output *output, double x) {
412 bool edge = x == output->width * output->scale; 413 bool edge = x == output->width;
413 struct i3bar_block *block; 414 struct i3bar_block *block;
414 wl_list_for_each(block, &output->bar->status->blocks, link) { 415 wl_list_for_each(block, &output->bar->status->blocks, link) {
415 predict_status_block_pos(cairo, output, block, &x, edge); 416 predict_status_block_pos(cairo, output, block, &x, edge);
@@ -424,24 +425,24 @@ static uint32_t predict_workspace_button_length(cairo_t *cairo,
424 struct swaybar_config *config = output->bar->config; 425 struct swaybar_config *config = output->bar->config;
425 426
426 int text_width, text_height; 427 int text_width, text_height;
427 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 428 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,
428 output->scale, config->pango_markup, "%s", ws->label); 429 config->pango_markup, "%s", ws->label);
429 430
430 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 431 int ws_vertical_padding = WS_VERTICAL_PADDING;
431 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 432 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
432 int border_width = BORDER_WIDTH * output->scale; 433 int border_width = BORDER_WIDTH;
433 434
434 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 435 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
435 + border_width * 2; 436 + border_width * 2;
436 uint32_t ideal_surface_height = ideal_height / output->scale; 437 uint32_t ideal_surface_height = ideal_height;
437 if (!output->bar->config->height && 438 if (!output->bar->config->height &&
438 output->height < ideal_surface_height) { 439 output->height < ideal_surface_height) {
439 return 0; 440 return 0;
440 } 441 }
441 442
442 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 443 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
443 if (width < config->workspace_min_width * output->scale) { 444 if (width < config->workspace_min_width) {
444 width = config->workspace_min_width * output->scale; 445 width = config->workspace_min_width;
445 } 446 }
446 return width; 447 return width;
447} 448}
@@ -472,24 +473,24 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo,
472 } 473 }
473 474
474 int text_width, text_height; 475 int text_width, text_height;
475 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 476 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
476 output->scale, output->bar->mode_pango_markup, 477 1, output->bar->mode_pango_markup,
477 "%s", mode); 478 "%s", mode);
478 479
479 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 480 int ws_vertical_padding = WS_VERTICAL_PADDING;
480 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 481 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
481 int border_width = BORDER_WIDTH * output->scale; 482 int border_width = BORDER_WIDTH;
482 483
483 uint32_t ideal_height = text_height + ws_vertical_padding * 2 484 uint32_t ideal_height = text_height + ws_vertical_padding * 2
484 + border_width * 2; 485 + border_width * 2;
485 uint32_t ideal_surface_height = ideal_height / output->scale; 486 uint32_t ideal_surface_height = ideal_height;
486 if (!output->bar->config->height && 487 if (!output->bar->config->height &&
487 output->height < ideal_surface_height) { 488 output->height < ideal_surface_height) {
488 return 0; 489 return 0;
489 } 490 }
490 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 491 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
491 if (width < config->workspace_min_width * output->scale) { 492 if (width < config->workspace_min_width) {
492 width = config->workspace_min_width * output->scale; 493 width = config->workspace_min_width;
493 } 494 }
494 return width; 495 return width;
495} 496}
@@ -497,7 +498,7 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo,
497static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) { 498static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) {
498 struct swaybar_output *output = ctx->output; 499 struct swaybar_output *output = ctx->output;
499 uint32_t max_height = 0; 500 uint32_t max_height = 0;
500 bool edge = *x == output->width * output->scale; 501 bool edge = *x == output->width;
501 struct i3bar_block *block; 502 struct i3bar_block *block;
502 bool use_short_text = false; 503 bool use_short_text = false;
503 504
@@ -505,7 +506,7 @@ static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x)
505 double reserved_width = 506 double reserved_width =
506 predict_workspace_buttons_length(cairo, output) + 507 predict_workspace_buttons_length(cairo, output) +
507 predict_binding_mode_indicator_length(cairo, output) + 508 predict_binding_mode_indicator_length(cairo, output) +
508 3 * output->scale; // require a bit of space for margin 509 3; // require a bit of space for margin
509 510
510 double predicted_full_pos = 511 double predicted_full_pos =
511 predict_status_line_pos(cairo, output, *x); 512 predict_status_line_pos(cairo, output, *x);
@@ -549,27 +550,27 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx,
549 cairo_t *cairo = ctx->cairo; 550 cairo_t *cairo = ctx->cairo;
550 struct swaybar_config *config = output->bar->config; 551 struct swaybar_config *config = output->bar->config;
551 int text_width, text_height; 552 int text_width, text_height;
552 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 553 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
553 output->scale, output->bar->mode_pango_markup, 554 1, output->bar->mode_pango_markup,
554 "%s", mode); 555 "%s", mode);
555 556
556 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 557 int ws_vertical_padding = WS_VERTICAL_PADDING;
557 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 558 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
558 int border_width = BORDER_WIDTH * output->scale; 559 int border_width = BORDER_WIDTH;
559 560
560 uint32_t ideal_height = text_height + ws_vertical_padding * 2 561 uint32_t ideal_height = text_height + ws_vertical_padding * 2
561 + border_width * 2; 562 + border_width * 2;
562 uint32_t ideal_surface_height = ideal_height / output->scale; 563 uint32_t ideal_surface_height = ideal_height;
563 if (!output->bar->config->height && 564 if (!output->bar->config->height &&
564 output->height < ideal_surface_height) { 565 output->height < ideal_surface_height) {
565 return ideal_surface_height; 566 return ideal_surface_height;
566 } 567 }
567 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 568 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
568 if (width < config->workspace_min_width * output->scale) { 569 if (width < config->workspace_min_width) {
569 width = config->workspace_min_width * output->scale; 570 width = config->workspace_min_width;
570 } 571 }
571 572
572 uint32_t height = output->height * output->scale; 573 uint32_t height = output->height;
573 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 574 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
574 cairo_set_source_u32(cairo, config->colors.binding_mode.background); 575 cairo_set_source_u32(cairo, config->colors.binding_mode.background);
575 ctx->background_color = config->colors.binding_mode.background; 576 ctx->background_color = config->colors.binding_mode.background;
@@ -590,17 +591,22 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx,
590 cairo_set_source_u32(cairo, config->colors.binding_mode.text); 591 cairo_set_source_u32(cairo, config->colors.binding_mode.text);
591 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); 592 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
592 choose_text_aa_mode(ctx, config->colors.binding_mode.text); 593 choose_text_aa_mode(ctx, config->colors.binding_mode.text);
593 pango_printf(cairo, config->font, output->scale, 594 render_text(cairo, config->font_description, 1, output->bar->mode_pango_markup,
594 output->bar->mode_pango_markup, "%s", mode); 595 "%s", mode);
595 return output->height; 596 return output->height;
596} 597}
597 598
598static enum hotspot_event_handling workspace_hotspot_callback( 599static enum hotspot_event_handling workspace_hotspot_callback(
599 struct swaybar_output *output, struct swaybar_hotspot *hotspot, 600 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
600 double x, double y, uint32_t button, void *data) { 601 double x, double y, uint32_t button, bool released, void *data) {
601 if (button != BTN_LEFT) { 602 if (button != BTN_LEFT) {
602 return HOTSPOT_PROCESS; 603 return HOTSPOT_PROCESS;
603 } 604 }
605 if (released) {
606 // Since we handle the pressed event, also handle the released event
607 // to block it from falling through to a binding in the bar
608 return HOTSPOT_IGNORE;
609 }
604 ipc_send_workspace_command(output->bar, (const char *)data); 610 ipc_send_workspace_command(output->bar, (const char *)data);
605 return HOTSPOT_IGNORE; 611 return HOTSPOT_IGNORE;
606} 612}
@@ -620,28 +626,28 @@ static uint32_t render_workspace_button(struct render_context *ctx,
620 box_colors = config->colors.inactive_workspace; 626 box_colors = config->colors.inactive_workspace;
621 } 627 }
622 628
623 uint32_t height = output->height * output->scale; 629 uint32_t height = output->height;
624 630
625 cairo_t *cairo = ctx->cairo; 631 cairo_t *cairo = ctx->cairo;
626 int text_width, text_height; 632 int text_width, text_height;
627 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 633 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
628 output->scale, config->pango_markup, "%s", ws->label); 634 1, config->pango_markup, "%s", ws->label);
629 635
630 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 636 int ws_vertical_padding = WS_VERTICAL_PADDING;
631 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 637 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
632 int border_width = BORDER_WIDTH * output->scale; 638 int border_width = BORDER_WIDTH;
633 639
634 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 640 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
635 + border_width * 2; 641 + border_width * 2;
636 uint32_t ideal_surface_height = ideal_height / output->scale; 642 uint32_t ideal_surface_height = ideal_height;
637 if (!output->bar->config->height && 643 if (!output->bar->config->height &&
638 output->height < ideal_surface_height) { 644 output->height < ideal_surface_height) {
639 return ideal_surface_height; 645 return ideal_surface_height;
640 } 646 }
641 647
642 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 648 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
643 if (width < config->workspace_min_width * output->scale) { 649 if (width < config->workspace_min_width) {
644 width = config->workspace_min_width * output->scale; 650 width = config->workspace_min_width;
645 } 651 }
646 652
647 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 653 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
@@ -664,7 +670,7 @@ static uint32_t render_workspace_button(struct render_context *ctx,
664 cairo_set_source_u32(cairo, box_colors.text); 670 cairo_set_source_u32(cairo, box_colors.text);
665 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); 671 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y));
666 choose_text_aa_mode(ctx, box_colors.text); 672 choose_text_aa_mode(ctx, box_colors.text);
667 pango_printf(cairo, config->font, output->scale, config->pango_markup, 673 render_text(cairo, config->font_description, 1, config->pango_markup,
668 "%s", ws->label); 674 "%s", ws->label);
669 675
670 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 676 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
@@ -686,19 +692,10 @@ static uint32_t render_to_cairo(struct render_context *ctx) {
686 struct swaybar_output *output = ctx->output; 692 struct swaybar_output *output = ctx->output;
687 struct swaybar *bar = output->bar; 693 struct swaybar *bar = output->bar;
688 struct swaybar_config *config = bar->config; 694 struct swaybar_config *config = bar->config;
689 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
690 if (output->focused) {
691 ctx->background_color = config->colors.focused_background;
692 } else {
693 ctx->background_color = config->colors.background;
694 }
695
696 cairo_set_source_u32(cairo, ctx->background_color);
697 cairo_paint(cairo);
698 695
699 int th; 696 int th;
700 get_text_size(cairo, config->font, NULL, &th, NULL, output->scale, false, ""); 697 get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, "");
701 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4) / output->scale; 698 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4);
702 /* 699 /*
703 * Each render_* function takes the actual height of the bar, and returns 700 * Each render_* function takes the actual height of the bar, and returns
704 * the ideal height. If the actual height is too short, the render function 701 * the ideal height. If the actual height is too short, the render function
@@ -706,7 +703,7 @@ static uint32_t render_to_cairo(struct render_context *ctx) {
706 * height is too tall, the render function should adapt its drawing to 703 * height is too tall, the render function should adapt its drawing to
707 * utilize the available space. 704 * utilize the available space.
708 */ 705 */
709 double x = output->width * output->scale; 706 double x = output->width;
710#if HAVE_TRAY 707#if HAVE_TRAY
711 if (bar->tray) { 708 if (bar->tray) {
712 uint32_t h = render_tray(cairo, output, &x); 709 uint32_t h = render_tray(cairo, output, &x);
@@ -756,34 +753,43 @@ void render_frame(struct swaybar_output *output) {
756 753
757 free_hotspots(&output->hotspots); 754 free_hotspots(&output->hotspots);
758 755
756 uint32_t background_color;
757 if (output->focused) {
758 background_color = output->bar->config->colors.focused_background;
759 } else {
760 background_color = output->bar->config->colors.background;
761 }
762
759 struct render_context ctx = { 0 }; 763 struct render_context ctx = { 0 };
760 ctx.output = output; 764 ctx.output = output;
765 // initial background color used for deciding the best way to antialias text
766 ctx.background_color = background_color;
761 767
762 cairo_surface_t *recorder = cairo_recording_surface_create( 768 cairo_surface_t *recorder = cairo_recording_surface_create(
763 CAIRO_CONTENT_COLOR_ALPHA, NULL); 769 CAIRO_CONTENT_COLOR_ALPHA, NULL);
764 cairo_t *cairo = cairo_create(recorder); 770 cairo_t *cairo = cairo_create(recorder);
771 cairo_scale(cairo, output->scale, output->scale);
765 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 772 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
766 ctx.cairo = cairo; 773 ctx.cairo = cairo;
767 774
768 cairo_font_options_t *fo = cairo_font_options_create(); 775 cairo_font_options_t *fo = cairo_font_options_create();
769 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
770 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); 776 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
771 ctx.textaa_safe = fo; 777 ctx.textaa_safe = fo;
772 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { 778 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
773 ctx.textaa_sharp = ctx.textaa_safe; 779 ctx.textaa_sharp = ctx.textaa_safe;
774 } else { 780 } else {
775 fo = cairo_font_options_create(); 781 fo = cairo_font_options_create();
776 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
777 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); 782 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
778 cairo_font_options_set_subpixel_order(fo, 783 cairo_font_options_set_subpixel_order(fo,
779 to_cairo_subpixel_order(output->subpixel)); 784 to_cairo_subpixel_order(output->subpixel));
780 ctx.textaa_sharp = fo; 785 ctx.textaa_sharp = fo;
781 } 786 }
782 787
783 cairo_save(cairo); 788
784 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 789 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
790 cairo_set_source_u32(cairo, background_color);
785 cairo_paint(cairo); 791 cairo_paint(cairo);
786 cairo_restore(cairo); 792
787 uint32_t height = render_to_cairo(&ctx); 793 uint32_t height = render_to_cairo(&ctx);
788 int config_height = output->bar->config->height; 794 int config_height = output->bar->config->height;
789 if (config_height > 0) { 795 if (config_height > 0) {
@@ -810,9 +816,7 @@ void render_frame(struct swaybar_output *output) {
810 output->width * output->scale, 816 output->width * output->scale,
811 output->height * output->scale); 817 output->height * output->scale);
812 if (!output->current_buffer) { 818 if (!output->current_buffer) {
813 cairo_surface_destroy(recorder); 819 goto cleanup;
814 cairo_destroy(cairo);
815 return;
816 } 820 }
817 cairo_t *shm = output->current_buffer->cairo; 821 cairo_t *shm = output->current_buffer->cairo;
818 822
@@ -830,6 +834,17 @@ void render_frame(struct swaybar_output *output) {
830 wl_surface_damage(output->surface, 0, 0, 834 wl_surface_damage(output->surface, 0, 0,
831 output->width, output->height); 835 output->width, output->height);
832 836
837 uint32_t bg_alpha = background_color & 0xFF;
838 if (bg_alpha == 0xFF) {
839 struct wl_region *region =
840 wl_compositor_create_region(output->bar->compositor);
841 wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
842 wl_surface_set_opaque_region(output->surface, region);
843 wl_region_destroy(region);
844 } else {
845 wl_surface_set_opaque_region(output->surface, NULL);
846 }
847
833 struct wl_callback *frame_callback = wl_surface_frame(output->surface); 848 struct wl_callback *frame_callback = wl_surface_frame(output->surface);
834 wl_callback_add_listener(frame_callback, &output_frame_listener, output); 849 wl_callback_add_listener(frame_callback, &output_frame_listener, output);
835 output->frame_scheduled = true; 850 output->frame_scheduled = true;
@@ -837,6 +852,7 @@ void render_frame(struct swaybar_output *output) {
837 wl_surface_commit(output->surface); 852 wl_surface_commit(output->surface);
838 } 853 }
839 854
855cleanup:
840 if (ctx.textaa_sharp != ctx.textaa_safe) { 856 if (ctx.textaa_sharp != ctx.textaa_safe) {
841 cairo_font_options_destroy(ctx.textaa_sharp); 857 cairo_font_options_destroy(ctx.textaa_sharp);
842 } 858 }
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index ecd91032..e542e606 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <fcntl.h> 2#include <fcntl.h>
4#include <sys/ioctl.h> 3#include <sys/ioctl.h>
@@ -117,11 +116,11 @@ bool status_handle_readable(struct status_line *status) {
117 status->text = status->buffer; 116 status->text = status->buffer;
118 // intentional fall-through 117 // intentional fall-through
119 case PROTOCOL_TEXT: 118 case PROTOCOL_TEXT:
120 errno = 0;
121 while (true) { 119 while (true) {
122 if (status->buffer[read_bytes - 1] == '\n') { 120 if (status->buffer[read_bytes - 1] == '\n') {
123 status->buffer[read_bytes - 1] = '\0'; 121 status->buffer[read_bytes - 1] = '\0';
124 } 122 }
123 errno = 0;
125 read_bytes = getline(&status->buffer, 124 read_bytes = getline(&status->buffer,
126 &status->buffer_size, status->read); 125 &status->buffer_size, status->read);
127 if (errno == EAGAIN) { 126 if (errno == EAGAIN) {
@@ -157,7 +156,12 @@ struct status_line *status_line_init(char *cmd) {
157 assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before " 156 assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before "
158 " starting `status-command`; WAYLAND_SOCKET should not be set"); 157 " starting `status-command`; WAYLAND_SOCKET should not be set");
159 status->pid = fork(); 158 status->pid = fork();
160 if (status->pid == 0) { 159 if (status->pid < 0) {
160 sway_log_errno(SWAY_ERROR, "fork failed");
161 exit(1);
162 } else if (status->pid == 0) {
163 setpgid(0, 0);
164
161 dup2(pipe_read_fd[1], STDOUT_FILENO); 165 dup2(pipe_read_fd[1], STDOUT_FILENO);
162 close(pipe_read_fd[0]); 166 close(pipe_read_fd[0]);
163 close(pipe_read_fd[1]); 167 close(pipe_read_fd[1]);
@@ -185,8 +189,8 @@ struct status_line *status_line_init(char *cmd) {
185 189
186void status_line_free(struct status_line *status) { 190void status_line_free(struct status_line *status) {
187 status_line_close_fds(status); 191 status_line_close_fds(status);
188 kill(status->pid, status->cont_signal); 192 kill(-status->pid, status->cont_signal);
189 kill(status->pid, SIGTERM); 193 kill(-status->pid, SIGTERM);
190 waitpid(status->pid, NULL, 0); 194 waitpid(status->pid, NULL, 0);
191 if (status->protocol == PROTOCOL_I3BAR) { 195 if (status->protocol == PROTOCOL_I3BAR) {
192 struct i3bar_block *block, *tmp; 196 struct i3bar_block *block, *tmp;
diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c
index ddf2416d..79b54606 100644
--- a/swaybar/tray/host.c
+++ b/swaybar/tray/host.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -10,6 +9,7 @@
10#include "swaybar/tray/tray.h" 9#include "swaybar/tray/tray.h"
11#include "list.h" 10#include "list.h"
12#include "log.h" 11#include "log.h"
12#include "stringop.h"
13 13
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 19f4beac..ca6c03ad 100644
--- a/swaybar/tray/item.c
+++ b/swaybar/tray/item.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <arpa/inet.h> 1#include <arpa/inet.h>
3#include <cairo.h> 2#include <cairo.h>
4#include <limits.h> 3#include <limits.h>
@@ -7,12 +6,12 @@
7#include <string.h> 6#include <string.h>
8#include "swaybar/bar.h" 7#include "swaybar/bar.h"
9#include "swaybar/config.h" 8#include "swaybar/config.h"
9#include "swaybar/image.h"
10#include "swaybar/input.h" 10#include "swaybar/input.h"
11#include "swaybar/tray/host.h" 11#include "swaybar/tray/host.h"
12#include "swaybar/tray/icon.h" 12#include "swaybar/tray/icon.h"
13#include "swaybar/tray/item.h" 13#include "swaybar/tray/item.h"
14#include "swaybar/tray/tray.h" 14#include "swaybar/tray/tray.h"
15#include "background-image.h"
16#include "cairo_util.h" 15#include "cairo_util.h"
17#include "list.h" 16#include "list.h"
18#include "log.h" 17#include "log.h"
@@ -385,13 +384,18 @@ static int cmp_sni_id(const void *item, const void *cmp_to) {
385 384
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 }
@@ -466,6 +470,11 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
466 sni->target_size = target_size; 470 sni->target_size = target_size;
467 } 471 }
468 472
473 // Passive
474 if (sni->status && sni->status[0] == 'P') {
475 return 0;
476 }
477
469 int icon_size; 478 int icon_size;
470 cairo_surface_t *icon; 479 cairo_surface_t *icon;
471 if (sni->icon) { 480 if (sni->icon) {
@@ -493,24 +502,36 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
493 cairo_destroy(cairo_icon); 502 cairo_destroy(cairo_icon);
494 } 503 }
495 504
496 int padded_size = icon_size + 2*padding; 505 double descaled_padding = (double)padding / output->scale;
497 *x -= padded_size; 506 double descaled_icon_size = (double)icon_size / output->scale;
498 int y = floor((height - padded_size) / 2.0); 507
508 int size = descaled_icon_size + 2 * descaled_padding;
509 *x -= size;
510 int icon_y = floor((output->height - size) / 2.0);
499 511
500 cairo_operator_t op = cairo_get_operator(cairo); 512 cairo_operator_t op = cairo_get_operator(cairo);
501 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); 513 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
502 cairo_set_source_surface(cairo, icon, *x + padding, y + padding); 514
503 cairo_rectangle(cairo, *x, y, padded_size, padded_size); 515 cairo_matrix_t scale_matrix;
516 cairo_pattern_t *icon_pattern = cairo_pattern_create_for_surface(icon);
517 // TODO: check cairo_pattern_status for "ENOMEM"
518 cairo_matrix_init_scale(&scale_matrix, output->scale, output->scale);
519 cairo_matrix_translate(&scale_matrix, -(*x + descaled_padding), -(icon_y + descaled_padding));
520 cairo_pattern_set_matrix(icon_pattern, &scale_matrix);
521 cairo_set_source(cairo, icon_pattern);
522 cairo_rectangle(cairo, *x, icon_y, size, size);
504 cairo_fill(cairo); 523 cairo_fill(cairo);
524
505 cairo_set_operator(cairo, op); 525 cairo_set_operator(cairo, op);
506 526
527 cairo_pattern_destroy(icon_pattern);
507 cairo_surface_destroy(icon); 528 cairo_surface_destroy(icon);
508 529
509 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 530 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
510 hotspot->x = *x; 531 hotspot->x = *x;
511 hotspot->y = 0; 532 hotspot->y = 0;
512 hotspot->width = height; 533 hotspot->width = size;
513 hotspot->height = height; 534 hotspot->height = output->height;
514 hotspot->callback = icon_hotspot_callback; 535 hotspot->callback = icon_hotspot_callback;
515 hotspot->destroy = free; 536 hotspot->destroy = free;
516 hotspot->data = strdup(sni->watcher_id); 537 hotspot->data = strdup(sni->watcher_id);
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c
index 5fe6f9c3..b0545f4a 100644
--- a/swaybar/tray/tray.c
+++ b/swaybar/tray/tray.c
@@ -116,8 +116,8 @@ uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) {
116 } 116 }
117 } // else display on all 117 } // else display on all
118 118
119 if ((int) output->height*output->scale <= 2*config->tray_padding) { 119 if ((int)(output->height * output->scale) <= 2 * config->tray_padding) {
120 return 2*config->tray_padding + 1; 120 return (2 * config->tray_padding + 1) / output->scale;
121 } 121 }
122 122
123 uint32_t max_height = 0; 123 uint32_t max_height = 0;
diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c
index 16afc27c..3cfea8d8 100644
--- a/swaybar/tray/watcher.c
+++ b/swaybar/tray/watcher.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stddef.h> 2#include <stddef.h>
4#include <stdio.h> 3#include <stdio.h>
@@ -6,6 +5,7 @@
6#include <string.h> 5#include <string.h>
7#include "list.h" 6#include "list.h"
8#include "log.h" 7#include "log.h"
8#include "stringop.h"
9#include "swaybar/tray/watcher.h" 9#include "swaybar/tray/watcher.h"
10 10
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 574d3b75..573a7b16 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -1,4 +1,5 @@
1#define _POSIX_C_SOURCE 200809L 1
2#include <limits.h>
2#include <stdio.h> 3#include <stdio.h>
3#include <stdlib.h> 4#include <stdlib.h>
4#include <string.h> 5#include <string.h>
@@ -58,7 +59,7 @@ static void pretty_print_cmd(json_object *r) {
58 if (!success_object(r)) { 59 if (!success_object(r)) {
59 json_object *error; 60 json_object *error;
60 if (!json_object_object_get_ex(r, "error", &error)) { 61 if (!json_object_object_get_ex(r, "error", &error)) {
61 printf("An unknkown error occurred"); 62 printf("An unknown error occurred");
62 } else { 63 } else {
63 printf("Error: %s\n", json_object_get_string(error)); 64 printf("Error: %s\n", json_object_get_string(error));
64 } 65 }
@@ -183,12 +184,14 @@ static void pretty_print_seat(json_object *i) {
183} 184}
184 185
185static void pretty_print_output(json_object *o) { 186static void pretty_print_output(json_object *o) {
186 json_object *name, *rect, *focused, *active, *ws, *current_mode; 187 json_object *name, *rect, *focused, *active, *power, *ws, *current_mode, *non_desktop;
187 json_object_object_get_ex(o, "name", &name); 188 json_object_object_get_ex(o, "name", &name);
188 json_object_object_get_ex(o, "rect", &rect); 189 json_object_object_get_ex(o, "rect", &rect);
189 json_object_object_get_ex(o, "focused", &focused); 190 json_object_object_get_ex(o, "focused", &focused);
190 json_object_object_get_ex(o, "active", &active); 191 json_object_object_get_ex(o, "active", &active);
192 json_object_object_get_ex(o, "power", &power);
191 json_object_object_get_ex(o, "current_workspace", &ws); 193 json_object_object_get_ex(o, "current_workspace", &ws);
194 json_object_object_get_ex(o, "non_desktop", &non_desktop);
192 json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, 195 json_object *make, *model, *serial, *scale, *scale_filter, *subpixel,
193 *transform, *max_render_time, *adaptive_sync_status; 196 *transform, *max_render_time, *adaptive_sync_status;
194 json_object_object_get_ex(o, "make", &make); 197 json_object_object_get_ex(o, "make", &make);
@@ -211,10 +214,19 @@ static void pretty_print_output(json_object *o) {
211 json_object_object_get_ex(current_mode, "height", &height); 214 json_object_object_get_ex(current_mode, "height", &height);
212 json_object_object_get_ex(current_mode, "refresh", &refresh); 215 json_object_object_get_ex(current_mode, "refresh", &refresh);
213 216
214 if (json_object_get_boolean(active)) { 217 if (json_object_get_boolean(non_desktop)) {
218 printf(
219 "Output %s '%s %s %s' (non-desktop)\n",
220 json_object_get_string(name),
221 json_object_get_string(make),
222 json_object_get_string(model),
223 json_object_get_string(serial)
224 );
225 } else if (json_object_get_boolean(active)) {
215 printf( 226 printf(
216 "Output %s '%s %s %s'%s\n" 227 "Output %s '%s %s %s'%s\n"
217 " Current mode: %dx%d @ %.3f Hz\n" 228 " Current mode: %dx%d @ %.3f Hz\n"
229 " Power: %s\n"
218 " Position: %d,%d\n" 230 " Position: %d,%d\n"
219 " Scale factor: %f\n" 231 " Scale factor: %f\n"
220 " Scale filter: %s\n" 232 " Scale filter: %s\n"
@@ -229,6 +241,7 @@ static void pretty_print_output(json_object *o) {
229 json_object_get_int(width), 241 json_object_get_int(width),
230 json_object_get_int(height), 242 json_object_get_int(height),
231 (double)json_object_get_int(refresh) / 1000, 243 (double)json_object_get_int(refresh) / 1000,
244 json_object_get_boolean(power) ? "on" : "off",
232 json_object_get_int(x), json_object_get_int(y), 245 json_object_get_int(x), json_object_get_int(y),
233 json_object_get_double(scale), 246 json_object_get_double(scale),
234 json_object_get_string(scale_filter), 247 json_object_get_string(scale_filter),
@@ -245,7 +258,7 @@ static void pretty_print_output(json_object *o) {
245 json_object_get_string(adaptive_sync_status)); 258 json_object_get_string(adaptive_sync_status));
246 } else { 259 } else {
247 printf( 260 printf(
248 "Output %s '%s %s %s' (inactive)\n", 261 "Output %s '%s %s %s' (disabled)\n",
249 json_object_get_string(name), 262 json_object_get_string(name),
250 json_object_get_string(make), 263 json_object_get_string(make),
251 json_object_get_string(model), 264 json_object_get_string(model),
@@ -260,14 +273,22 @@ static void pretty_print_output(json_object *o) {
260 for (size_t i = 0; i < modes_len; ++i) { 273 for (size_t i = 0; i < modes_len; ++i) {
261 json_object *mode = json_object_array_get_idx(modes, i); 274 json_object *mode = json_object_array_get_idx(modes, i);
262 275
263 json_object *mode_width, *mode_height, *mode_refresh; 276 json_object *mode_width, *mode_height, *mode_refresh,
277 *mode_picture_aspect_ratio;
264 json_object_object_get_ex(mode, "width", &mode_width); 278 json_object_object_get_ex(mode, "width", &mode_width);
265 json_object_object_get_ex(mode, "height", &mode_height); 279 json_object_object_get_ex(mode, "height", &mode_height);
266 json_object_object_get_ex(mode, "refresh", &mode_refresh); 280 json_object_object_get_ex(mode, "refresh", &mode_refresh);
281 json_object_object_get_ex(mode, "picture_aspect_ratio",
282 &mode_picture_aspect_ratio);
267 283
268 printf(" %dx%d @ %.3f Hz\n", json_object_get_int(mode_width), 284 printf(" %dx%d @ %.3f Hz", json_object_get_int(mode_width),
269 json_object_get_int(mode_height), 285 json_object_get_int(mode_height),
270 (double)json_object_get_int(mode_refresh) / 1000); 286 (double)json_object_get_int(mode_refresh) / 1000);
287 if (mode_picture_aspect_ratio &&
288 strcmp("none", json_object_get_string(mode_picture_aspect_ratio)) != 0) {
289 printf(" (%s)", json_object_get_string(mode_picture_aspect_ratio));
290 }
291 printf("\n");
271 } 292 }
272 } 293 }
273 294
@@ -286,28 +307,83 @@ static void pretty_print_config(json_object *c) {
286 printf("%s\n", json_object_get_string(config)); 307 printf("%s\n", json_object_get_string(config));
287} 308}
288 309
289static void pretty_print(int type, json_object *resp) { 310static void pretty_print_tree(json_object *obj, int indent) {
290 if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && 311 for (int i = 0; i < indent; i++) {
291 type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && 312 printf(" ");
292 type != IPC_GET_VERSION && type != IPC_GET_SEATS &&
293 type != IPC_GET_CONFIG && type != IPC_SEND_TICK) {
294 printf("%s\n", json_object_to_json_string_ext(resp,
295 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));
296 return;
297 } 313 }
298 314
299 if (type == IPC_SEND_TICK) { 315 int id = json_object_get_int(json_object_object_get(obj, "id"));
300 return; 316 const char *name = json_object_get_string(json_object_object_get(obj, "name"));
317 const char *type = json_object_get_string(json_object_object_get(obj, "type"));
318 const char *shell = json_object_get_string(json_object_object_get(obj, "shell"));
319
320 printf("#%d: %s \"%s\"", id, type, name);
321
322 if (shell != NULL) {
323 int pid = json_object_get_int(json_object_object_get(obj, "pid"));
324 const char *app_id = json_object_get_string(json_object_object_get(obj, "app_id"));
325 json_object *window_props_obj = json_object_object_get(obj, "window_properties");
326 const char *instance = json_object_get_string(json_object_object_get(window_props_obj, "instance"));
327 const char *class = json_object_get_string(json_object_object_get(window_props_obj, "class"));
328 int x11_id = json_object_get_int(json_object_object_get(obj, "window"));
329
330 printf(" (%s, pid: %d", shell, pid);
331 if (app_id != NULL) {
332 printf(", app_id: \"%s\"", app_id);
333 }
334 if (instance != NULL) {
335 printf(", instance: \"%s\"", instance);
336 }
337 if (class != NULL) {
338 printf(", class: \"%s\"", class);
339 }
340 if (x11_id != 0) {
341 printf(", X11 window: 0x%X", x11_id);
342 }
343 printf(")");
301 } 344 }
302 345
303 if (type == IPC_GET_VERSION) { 346 printf("\n");
304 pretty_print_version(resp); 347
305 return; 348 json_object *nodes_obj = json_object_object_get(obj, "nodes");
349 size_t len = json_object_array_length(nodes_obj);
350 for (size_t i = 0; i < len; i++) {
351 pretty_print_tree(json_object_array_get_idx(nodes_obj, i), indent + 1);
306 } 352 }
307 353
308 if (type == IPC_GET_CONFIG) { 354 json_object *floating_nodes_obj;
355 json_bool floating_nodes = json_object_object_get_ex(obj, "floating_nodes", &floating_nodes_obj);
356 if (floating_nodes) {
357 size_t len = json_object_array_length(floating_nodes_obj);
358 for (size_t i = 0; i < len; i++) {
359 pretty_print_tree(json_object_array_get_idx(floating_nodes_obj, i), indent + 1);
360 }
361 }
362}
363
364static void pretty_print(int type, json_object *resp) {
365 switch (type) {
366 case IPC_SEND_TICK:
367 return;
368 case IPC_GET_VERSION:
369 pretty_print_version(resp);
370 return;
371 case IPC_GET_CONFIG:
309 pretty_print_config(resp); 372 pretty_print_config(resp);
310 return; 373 return;
374 case IPC_GET_TREE:
375 pretty_print_tree(resp, 0);
376 return;
377 case IPC_COMMAND:
378 case IPC_GET_WORKSPACES:
379 case IPC_GET_INPUTS:
380 case IPC_GET_OUTPUTS:
381 case IPC_GET_SEATS:
382 break;
383 default:
384 printf("%s\n", json_object_to_json_string_ext(resp,
385 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));
386 return;
311 } 387 }
312 388
313 json_object *obj; 389 json_object *obj;
@@ -480,12 +556,20 @@ int main(int argc, char **argv) {
480 char *resp = ipc_single_command(socketfd, type, command, &len); 556 char *resp = ipc_single_command(socketfd, type, command, &len);
481 557
482 // pretty print the json 558 // pretty print the json
483 json_object *obj = json_tokener_parse(resp); 559 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
484 if (obj == NULL) { 560 if (tok == NULL) {
561 if (quiet) {
562 exit(EXIT_FAILURE);
563 }
564 sway_abort("failed allocating json_tokener");
565 }
566 json_object *obj = json_tokener_parse_ex(tok, resp, -1);
567 enum json_tokener_error err = json_tokener_get_error(tok);
568 json_tokener_free(tok);
569 if (obj == NULL || err != json_tokener_success) {
485 if (!quiet) { 570 if (!quiet) {
486 fprintf(stderr, "ERROR: Could not parse json response from ipc. " 571 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
487 "This is a bug in sway."); 572 json_tokener_error_desc(err));
488 printf("%s\n", resp);
489 } 573 }
490 ret = 1; 574 ret = 1;
491 } else { 575 } else {
@@ -517,13 +601,22 @@ int main(int argc, char **argv) {
517 break; 601 break;
518 } 602 }
519 603
520 json_object *obj = json_tokener_parse(reply->payload); 604 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
521 if (obj == NULL) { 605 if (tok == NULL) {
606 if (quiet) {
607 exit(EXIT_FAILURE);
608 }
609 sway_abort("failed allocating json_tokener");
610 }
611 json_object *obj = json_tokener_parse_ex(tok, reply->payload, -1);
612 enum json_tokener_error err = json_tokener_get_error(tok);
613 json_tokener_free(tok);
614 if (obj == NULL || err != json_tokener_success) {
522 if (!quiet) { 615 if (!quiet) {
523 fprintf(stderr, "ERROR: Could not parse json response from" 616 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
524 " ipc. This is a bug in sway."); 617 json_tokener_error_desc(err));
525 ret = 1;
526 } 618 }
619 ret = 1;
527 break; 620 break;
528 } else if (quiet) { 621 } else if (quiet) {
529 json_object_put(obj); 622 json_object_put(obj);
diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd
index b69013b5..abee1bb9 100644
--- a/swaymsg/swaymsg.1.scd
+++ b/swaymsg/swaymsg.1.scd
@@ -21,12 +21,13 @@ _swaymsg_ [options...] [message]
21 21
22*-p, --pretty* 22*-p, --pretty*
23 Use pretty output even when not using a tty. 23 Use pretty output even when not using a tty.
24 Not available for all message types.
24 25
25*-q, --quiet* 26*-q, --quiet*
26 Sends the IPC message but does not print the response from sway. 27 Sends the IPC message but does not print the response from sway.
27 28
28*-r, --raw* 29*-r, --raw*
29 Use raw output even if using a tty. 30 Use raw JSON output even if using a tty.
30 31
31*-s, --socket* <path> 32*-s, --socket* <path>
32 Use the specified socket path. Otherwise, swaymsg will ask sway where the 33 Use the specified socket path. Otherwise, swaymsg will ask sway where the
@@ -46,6 +47,11 @@ _swaymsg_ [options...] [message]
46 47
47 See *sway*(5) for a list of commands. 48 See *sway*(5) for a list of commands.
48 49
50 _swaymsg_ can return pretty printed (standalone-default) or JSON-formatted
51 (*--raw*) output. For detailed documentation on the returned JSON-data of
52 each message type listed below, refer to *sway-ipc*(7). The JSON-format can
53 contain more information than the pretty print.
54
49 Tips: 55 Tips:
50 - Command expansion is performed twice: once by swaymsg, and again by sway. 56 - Command expansion is performed twice: once by swaymsg, and again by sway.
51 If you have quoted multi-word strings in your command, enclose the entire 57 If you have quoted multi-word strings in your command, enclose the entire
@@ -60,20 +66,20 @@ _swaymsg_ [options...] [message]
60 _swaymsg -- mark --add test_ instead of _swaymsg mark --add test_. 66 _swaymsg -- mark --add test_ instead of _swaymsg mark --add test_.
61 67
62*get\_workspaces* 68*get\_workspaces*
63 Gets a JSON-encoded list of workspaces and their status. 69 Gets a list of workspaces and their status.
64 70
65*get\_inputs* 71*get\_inputs*
66 Gets a JSON-encoded list of current inputs. 72 Gets a list of current inputs.
67 73
68*get\_outputs* 74*get\_outputs*
69 Gets a JSON-encoded list of current outputs. 75 Gets a list of current outputs.
70 76
71*get\_tree* 77*get\_tree*
72 Gets a JSON-encoded layout tree of all open windows, containers, outputs, 78 Gets a JSON-encoded layout tree of all open windows, containers, outputs,
73 workspaces, and so on. 79 workspaces, and so on.
74 80
75*get\_seats* 81*get\_seats*
76 Gets a JSON-encoded list of all seats, 82 Gets a list of all seats,
77 its properties and all assigned devices. 83 its properties and all assigned devices.
78 84
79*get\_marks* 85*get\_marks*
@@ -83,7 +89,7 @@ _swaymsg_ [options...] [message]
83 Get a JSON-encoded configuration for swaybar. 89 Get a JSON-encoded configuration for swaybar.
84 90
85*get\_version* 91*get\_version*
86 Get JSON-encoded version information for the running instance of sway. 92 Get version information for the running instance of sway.
87 93
88*get\_binding\_modes* 94*get\_binding\_modes*
89 Gets a JSON-encoded list of currently configured binding modes. 95 Gets a JSON-encoded list of currently configured binding modes.
@@ -92,7 +98,7 @@ _swaymsg_ [options...] [message]
92 Gets JSON-encoded info about the current binding state. 98 Gets JSON-encoded info about the current binding state.
93 99
94*get\_config* 100*get\_config*
95 Gets a JSON-encoded copy of the current configuration. 101 Gets a copy of the current configuration. Doesn't expand includes.
96 102
97*send\_tick* 103*send\_tick*
98 Sends a tick event to all subscribed clients. 104 Sends a tick event to all subscribed clients.
@@ -101,6 +107,8 @@ _swaymsg_ [options...] [message]
101 Subscribe to a list of event types. The argument for this type should be 107 Subscribe to a list of event types. The argument for this type should be
102 provided in the form of a valid JSON array. If any of the types are invalid 108 provided in the form of a valid JSON array. If any of the types are invalid
103 or if a valid JSON array is not provided, this will result in a failure. 109 or if a valid JSON array is not provided, this will result in a failure.
110 For a list of valid event types and the data returned with them refer to
111 *sway-ipc*(7).
104 112
105# RETURN CODES 113# RETURN CODES
106 114
diff --git a/swaynag/config.c b/swaynag/config.c
index 6db7cce5..efd71ce7 100644
--- a/swaynag/config.c
+++ b/swaynag/config.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <getopt.h> 1#include <getopt.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -11,24 +10,40 @@
11#include "util.h" 10#include "util.h"
12#include "wlr-layer-shell-unstable-v1-client-protocol.h" 11#include "wlr-layer-shell-unstable-v1-client-protocol.h"
13 12
14static char *read_from_stdin(void) { 13static char *read_and_trim_stdin(void) {
15 char *buffer = NULL; 14 char *buffer = NULL, *line = NULL;
16 size_t buffer_len = 0; 15 size_t buffer_len = 0, line_size = 0;
17 char *line = NULL; 16 while (1) {
18 size_t line_size = 0; 17 ssize_t nread = getline(&line, &line_size, stdin);
19 ssize_t nread; 18 if (nread == -1) {
20 while ((nread = getline(&line, &line_size, stdin)) != -1) { 19 if (feof(stdin)) {
20 break;
21 } else {
22 perror("getline");
23 goto freeline;
24 }
25 }
21 buffer = realloc(buffer, buffer_len + nread + 1); 26 buffer = realloc(buffer, buffer_len + nread + 1);
22 snprintf(&buffer[buffer_len], nread + 1, "%s", line); 27 if (!buffer) {
28 perror("realloc");
29 goto freebuf;
30 }
31 memcpy(&buffer[buffer_len], line, nread + 1);
23 buffer_len += nread; 32 buffer_len += nread;
24 } 33 }
25 free(line); 34 free(line);
26 35
27 while (buffer && buffer[buffer_len - 1] == '\n') { 36 while (buffer_len && buffer[buffer_len - 1] == '\n') {
28 buffer[--buffer_len] = '\0'; 37 buffer[--buffer_len] = '\0';
29 } 38 }
30 39
31 return buffer; 40 return buffer;
41
42freeline:
43 free(line);
44freebuf:
45 free(buffer);
46 return NULL;
32} 47}
33 48
34int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, 49int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
@@ -150,8 +165,11 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
150 fprintf(stderr, "Missing action for button %s\n", optarg); 165 fprintf(stderr, "Missing action for button %s\n", optarg);
151 return EXIT_FAILURE; 166 return EXIT_FAILURE;
152 } 167 }
153 struct swaynag_button *button; 168 struct swaynag_button *button = calloc(1, sizeof(struct swaynag_button));
154 button = calloc(sizeof(struct swaynag_button), 1); 169 if (!button) {
170 perror("calloc");
171 return EXIT_FAILURE;
172 }
155 button->text = strdup(optarg); 173 button->text = strdup(optarg);
156 button->type = SWAYNAG_ACTION_COMMAND; 174 button->type = SWAYNAG_ACTION_COMMAND;
157 button->action = strdup(argv[optind]); 175 button->action = strdup(argv[optind]);
@@ -207,22 +225,25 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
207 break; 225 break;
208 case 'f': // Font 226 case 'f': // Font
209 if (type) { 227 if (type) {
210 free(type->font); 228 pango_font_description_free(type->font_description);
211 type->font = strdup(optarg); 229 type->font_description = pango_font_description_from_string(optarg);
212 } 230 }
213 break; 231 break;
214 case 'l': // Detailed Message 232 case 'l': // Detailed Message
215 if (swaynag) { 233 if (swaynag) {
216 free(swaynag->details.message); 234 free(swaynag->details.message);
217 swaynag->details.message = read_from_stdin(); 235 swaynag->details.message = read_and_trim_stdin();
236 if (!swaynag->details.message) {
237 return EXIT_FAILURE;
238 }
218 swaynag->details.button_up.text = strdup("â–²"); 239 swaynag->details.button_up.text = strdup("â–²");
219 swaynag->details.button_down.text = strdup("â–¼"); 240 swaynag->details.button_down.text = strdup("â–¼");
220 } 241 }
221 break; 242 break;
222 case 'L': // Detailed Button Text 243 case 'L': // Detailed Button Text
223 if (swaynag) { 244 if (swaynag) {
224 free(swaynag->details.button_details->text); 245 free(swaynag->details.details_text);
225 swaynag->details.button_details->text = strdup(optarg); 246 swaynag->details.details_text = strdup(optarg);
226 } 247 }
227 break; 248 break;
228 case 'm': // Message 249 case 'm': // Message
@@ -239,8 +260,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
239 break; 260 break;
240 case 's': // Dismiss Button Text 261 case 's': // Dismiss Button Text
241 if (swaynag) { 262 if (swaynag) {
242 struct swaynag_button *button_close; 263 struct swaynag_button *button_close = swaynag->buttons->items[0];
243 button_close = swaynag->buttons->items[0];
244 free(button_close->text); 264 free(button_close->text);
245 button_close->text = strdup(optarg); 265 button_close->text = strdup(optarg);
246 } 266 }
@@ -399,23 +419,24 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) {
399 419
400 if (line[0] == '[') { 420 if (line[0] == '[') {
401 char *close = strchr(line, ']'); 421 char *close = strchr(line, ']');
402 if (!close) { 422 if (!close || close != &line[nread - 2] || nread <= 3) {
403 fprintf(stderr, "Closing bracket not found on line %d\n", 423 fprintf(stderr, "Line %d is malformed\n", line_number);
404 line_number);
405 result = 1; 424 result = 1;
406 break; 425 break;
407 } 426 }
408 char *name = calloc(1, close - line); 427 *close = '\0';
409 strncat(name, line + 1, close - line - 1); 428 type = swaynag_type_get(types, &line[1]);
410 type = swaynag_type_get(types, name);
411 if (!type) { 429 if (!type) {
412 type = swaynag_type_new(name); 430 type = swaynag_type_new(&line[1]);
413 list_add(types, type); 431 list_add(types, type);
414 } 432 }
415 free(name);
416 } else { 433 } else {
417 char *flag = malloc(sizeof(char) * (nread + 3)); 434 char *flag = malloc(nread + 3);
418 sprintf(flag, "--%s", line); 435 if (!flag) {
436 perror("calloc");
437 return EXIT_FAILURE;
438 }
439 snprintf(flag, nread + 3, "--%s", line);
419 char *argv[] = {"swaynag", flag}; 440 char *argv[] = {"swaynag", flag};
420 result = swaynag_parse_options(2, argv, swaynag, types, type, 441 result = swaynag_parse_options(2, argv, swaynag, types, type,
421 NULL, NULL); 442 NULL, NULL);
diff --git a/swaynag/main.c b/swaynag/main.c
index 88007818..634bddbf 100644
--- a/swaynag/main.c
+++ b/swaynag/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <signal.h> 2#include <signal.h>
4#include "log.h" 3#include "log.h"
@@ -20,33 +19,27 @@ void sway_terminate(int code) {
20} 19}
21 20
22int main(int argc, char **argv) { 21int main(int argc, char **argv) {
23 int exit_code = EXIT_SUCCESS; 22 int status = EXIT_SUCCESS;
24 23
25 list_t *types = create_list(); 24 list_t *types = create_list();
26 swaynag_types_add_default(types); 25 swaynag_types_add_default(types);
27 26
28 memset(&swaynag, 0, sizeof(swaynag));
29 swaynag.buttons = create_list(); 27 swaynag.buttons = create_list();
30 wl_list_init(&swaynag.outputs); 28 wl_list_init(&swaynag.outputs);
31 wl_list_init(&swaynag.seats); 29 wl_list_init(&swaynag.seats);
32 30
33 struct swaynag_button *button_close = 31 struct swaynag_button *button_close = calloc(1, sizeof(struct swaynag_button));
34 calloc(sizeof(struct swaynag_button), 1);
35 button_close->text = strdup("X"); 32 button_close->text = strdup("X");
36 button_close->type = SWAYNAG_ACTION_DISMISS; 33 button_close->type = SWAYNAG_ACTION_DISMISS;
37 list_add(swaynag.buttons, button_close); 34 list_add(swaynag.buttons, button_close);
38 35
39 swaynag.details.button_details = 36 swaynag.details.details_text = strdup("Toggle details");
40 calloc(sizeof(struct swaynag_button), 1);
41 swaynag.details.button_details->text = strdup("Toggle details");
42 swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND;
43 37
44 char *config_path = NULL; 38 char *config_path = NULL;
45 bool debug = false; 39 bool debug = false;
46 int launch_status = swaynag_parse_options(argc, argv, NULL, NULL, NULL, 40 status = swaynag_parse_options(argc, argv, NULL, NULL, NULL,
47 &config_path, &debug); 41 &config_path, &debug);
48 if (launch_status != 0) { 42 if (status != 0) {
49 exit_code = launch_status;
50 goto cleanup; 43 goto cleanup;
51 } 44 }
52 sway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL); 45 sway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL);
@@ -56,29 +49,27 @@ int main(int argc, char **argv) {
56 } 49 }
57 if (config_path) { 50 if (config_path) {
58 sway_log(SWAY_DEBUG, "Loading config file: %s", config_path); 51 sway_log(SWAY_DEBUG, "Loading config file: %s", config_path);
59 int config_status = swaynag_load_config(config_path, &swaynag, types); 52 status = swaynag_load_config(config_path, &swaynag, types);
60 free(config_path); 53 if (status != 0) {
61 if (config_status != 0) {
62 exit_code = config_status;
63 goto cleanup; 54 goto cleanup;
64 } 55 }
65 } 56 }
66 57
58
67 if (argc > 1) { 59 if (argc > 1) {
68 struct swaynag_type *type_args = swaynag_type_new("<args>"); 60 struct swaynag_type *type_args = swaynag_type_new("<args>");
69 list_add(types, type_args); 61 list_add(types, type_args);
70 62
71 int result = swaynag_parse_options(argc, argv, &swaynag, types, 63 status = swaynag_parse_options(argc, argv, &swaynag, types,
72 type_args, NULL, NULL); 64 type_args, NULL, NULL);
73 if (result != 0) { 65 if (status != 0) {
74 exit_code = result;
75 goto cleanup; 66 goto cleanup;
76 } 67 }
77 } 68 }
78 69
79 if (!swaynag.message) { 70 if (!swaynag.message) {
80 sway_log(SWAY_ERROR, "No message passed. Please provide --message/-m"); 71 sway_log(SWAY_ERROR, "No message passed. Please provide --message/-m");
81 exit_code = EXIT_FAILURE; 72 status = EXIT_FAILURE;
82 goto cleanup; 73 goto cleanup;
83 } 74 }
84 75
@@ -96,20 +87,20 @@ int main(int argc, char **argv) {
96 swaynag_type_merge(type, swaynag_type_get(types, "<args>")); 87 swaynag_type_merge(type, swaynag_type_get(types, "<args>"));
97 swaynag.type = type; 88 swaynag.type = type;
98 89
99 swaynag_types_free(types);
100
101 if (swaynag.details.message) { 90 if (swaynag.details.message) {
91 swaynag.details.button_details = calloc(1, sizeof(struct swaynag_button));
92 swaynag.details.button_details->text = strdup(swaynag.details.details_text);
93 swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND;
102 list_add(swaynag.buttons, swaynag.details.button_details); 94 list_add(swaynag.buttons, swaynag.details.button_details);
103 } else {
104 free(swaynag.details.button_details->text);
105 free(swaynag.details.button_details);
106 } 95 }
107 96
108 sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output); 97 sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output);
109 sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors); 98 sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors);
110 sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name); 99 sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name);
111 sway_log(SWAY_DEBUG, "Message: %s", swaynag.message); 100 sway_log(SWAY_DEBUG, "Message: %s", swaynag.message);
112 sway_log(SWAY_DEBUG, "Font: %s", swaynag.type->font); 101 char *font = pango_font_description_to_string(swaynag.type->font_description);
102 sway_log(SWAY_DEBUG, "Font: %s", font);
103 free(font);
113 sway_log(SWAY_DEBUG, "Buttons"); 104 sway_log(SWAY_DEBUG, "Buttons");
114 for (int i = 0; i < swaynag.buttons->length; i++) { 105 for (int i = 0; i < swaynag.buttons->length; i++) {
115 struct swaynag_button *button = swaynag.buttons->items[i]; 106 struct swaynag_button *button = swaynag.buttons->items[i];
@@ -120,12 +111,9 @@ int main(int argc, char **argv) {
120 111
121 swaynag_setup(&swaynag); 112 swaynag_setup(&swaynag);
122 swaynag_run(&swaynag); 113 swaynag_run(&swaynag);
123 return exit_code;
124 114
125cleanup: 115cleanup:
126 swaynag_types_free(types); 116 swaynag_types_free(types);
127 free(swaynag.details.button_details->text);
128 free(swaynag.details.button_details);
129 swaynag_destroy(&swaynag); 117 swaynag_destroy(&swaynag);
130 return exit_code; 118 return status;
131} 119}
diff --git a/swaynag/meson.build b/swaynag/meson.build
index 71f2fc2d..aef21483 100644
--- a/swaynag/meson.build
+++ b/swaynag/meson.build
@@ -5,13 +5,14 @@ executable(
5 'render.c', 5 'render.c',
6 'swaynag.c', 6 'swaynag.c',
7 'types.c', 7 'types.c',
8 wl_protos_src,
8 ], 9 ],
9 include_directories: [sway_inc], 10 include_directories: [sway_inc],
10 dependencies: [ 11 dependencies: [
11 cairo, 12 cairo,
12 client_protos,
13 pango, 13 pango,
14 pangocairo, 14 pangocairo,
15 rt,
15 wayland_client, 16 wayland_client,
16 wayland_cursor, 17 wayland_cursor,
17 ], 18 ],
diff --git a/swaynag/render.c b/swaynag/render.c
index 2a7f869a..21b03289 100644
--- a/swaynag/render.c
+++ b/swaynag/render.c
@@ -9,20 +9,20 @@
9 9
10static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { 10static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) {
11 int text_width, text_height; 11 int text_width, text_height;
12 get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, 12 get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL,
13 swaynag->scale, true, "%s", swaynag->message); 13 1, true, "%s", swaynag->message);
14 14
15 int padding = swaynag->type->message_padding * swaynag->scale; 15 int padding = swaynag->type->message_padding;
16 16
17 uint32_t ideal_height = text_height + padding * 2; 17 uint32_t ideal_height = text_height + padding * 2;
18 uint32_t ideal_surface_height = ideal_height / swaynag->scale; 18 uint32_t ideal_surface_height = ideal_height;
19 if (swaynag->height < ideal_surface_height) { 19 if (swaynag->height < ideal_surface_height) {
20 return ideal_surface_height; 20 return ideal_surface_height;
21 } 21 }
22 22
23 cairo_set_source_u32(cairo, swaynag->type->text); 23 cairo_set_source_u32(cairo, swaynag->type->text);
24 cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); 24 cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2);
25 pango_printf(cairo, swaynag->type->font, swaynag->scale, false, 25 render_text(cairo, swaynag->type->font_description, 1, false,
26 "%s", swaynag->message); 26 "%s", swaynag->message);
27 27
28 return ideal_surface_height; 28 return ideal_surface_height;
@@ -31,11 +31,11 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) {
31static void render_details_scroll_button(cairo_t *cairo, 31static void render_details_scroll_button(cairo_t *cairo,
32 struct swaynag *swaynag, struct swaynag_button *button) { 32 struct swaynag *swaynag, struct swaynag_button *button) {
33 int text_width, text_height; 33 int text_width, text_height;
34 get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, 34 get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL,
35 swaynag->scale, true, "%s", button->text); 35 1, true, "%s", button->text);
36 36
37 int border = swaynag->type->button_border_thickness * swaynag->scale; 37 int border = swaynag->type->button_border_thickness;
38 int padding = swaynag->type->button_padding * swaynag->scale; 38 int padding = swaynag->type->button_padding;
39 39
40 cairo_set_source_u32(cairo, swaynag->type->details_background); 40 cairo_set_source_u32(cairo, swaynag->type->details_background);
41 cairo_rectangle(cairo, button->x, button->y, 41 cairo_rectangle(cairo, button->x, button->y,
@@ -50,41 +50,41 @@ static void render_details_scroll_button(cairo_t *cairo,
50 cairo_set_source_u32(cairo, swaynag->type->button_text); 50 cairo_set_source_u32(cairo, swaynag->type->button_text);
51 cairo_move_to(cairo, button->x + border + padding, 51 cairo_move_to(cairo, button->x + border + padding,
52 button->y + border + (button->height - text_height) / 2); 52 button->y + border + (button->height - text_height) / 2);
53 pango_printf(cairo, swaynag->type->font, swaynag->scale, true, 53 render_text(cairo, swaynag->type->font_description, 1, true,
54 "%s", button->text); 54 "%s", button->text);
55} 55}
56 56
57static int get_detailed_scroll_button_width(cairo_t *cairo, 57static int get_detailed_scroll_button_width(cairo_t *cairo,
58 struct swaynag *swaynag) { 58 struct swaynag *swaynag) {
59 int up_width, down_width, temp_height; 59 int up_width, down_width, temp_height;
60 get_text_size(cairo, swaynag->type->font, &up_width, &temp_height, NULL, 60 get_text_size(cairo, swaynag->type->font_description, &up_width, &temp_height, NULL,
61 swaynag->scale, true, 61 1, true,
62 "%s", swaynag->details.button_up.text); 62 "%s", swaynag->details.button_up.text);
63 get_text_size(cairo, swaynag->type->font, &down_width, &temp_height, NULL, 63 get_text_size(cairo, swaynag->type->font_description, &down_width, &temp_height, NULL,
64 swaynag->scale, true, 64 1, true,
65 "%s", swaynag->details.button_down.text); 65 "%s", swaynag->details.button_down.text);
66 66
67 int text_width = up_width > down_width ? up_width : down_width; 67 int text_width = up_width > down_width ? up_width : down_width;
68 int border = swaynag->type->button_border_thickness * swaynag->scale; 68 int border = swaynag->type->button_border_thickness;
69 int padding = swaynag->type->button_padding * swaynag->scale; 69 int padding = swaynag->type->button_padding;
70 70
71 return text_width + border * 2 + padding * 2; 71 return text_width + border * 2 + padding * 2;
72} 72}
73 73
74static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, 74static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag,
75 uint32_t y) { 75 uint32_t y) {
76 uint32_t width = swaynag->width * swaynag->scale; 76 uint32_t width = swaynag->width;
77 77
78 int border = swaynag->type->details_border_thickness * swaynag->scale; 78 int border = swaynag->type->details_border_thickness;
79 int padding = swaynag->type->message_padding * swaynag->scale; 79 int padding = swaynag->type->message_padding;
80 int decor = padding + border; 80 int decor = padding + border;
81 81
82 swaynag->details.x = decor; 82 swaynag->details.x = decor;
83 swaynag->details.y = y * swaynag->scale + decor; 83 swaynag->details.y = y + decor;
84 swaynag->details.width = width - decor * 2; 84 swaynag->details.width = width - decor * 2;
85 85
86 PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font, 86 PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font_description,
87 swaynag->details.message, swaynag->scale, false); 87 swaynag->details.message, 1, false);
88 pango_layout_set_width(layout, 88 pango_layout_set_width(layout,
89 (swaynag->details.width - padding * 2) * PANGO_SCALE); 89 (swaynag->details.width - padding * 2) * PANGO_SCALE);
90 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); 90 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
@@ -164,7 +164,7 @@ static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag,
164 pango_cairo_show_layout(cairo, layout); 164 pango_cairo_show_layout(cairo, layout);
165 g_object_unref(layout); 165 g_object_unref(layout);
166 166
167 return ideal_height / swaynag->scale; 167 return ideal_height;
168} 168}
169 169
170static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, 170static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag,
@@ -172,14 +172,14 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag,
172 struct swaynag_button *button = swaynag->buttons->items[button_index]; 172 struct swaynag_button *button = swaynag->buttons->items[button_index];
173 173
174 int text_width, text_height; 174 int text_width, text_height;
175 get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, 175 get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL,
176 swaynag->scale, true, "%s", button->text); 176 1, true, "%s", button->text);
177 177
178 int border = swaynag->type->button_border_thickness * swaynag->scale; 178 int border = swaynag->type->button_border_thickness;
179 int padding = swaynag->type->button_padding * swaynag->scale; 179 int padding = swaynag->type->button_padding;
180 180
181 uint32_t ideal_height = text_height + padding * 2 + border * 2; 181 uint32_t ideal_height = text_height + padding * 2 + border * 2;
182 uint32_t ideal_surface_height = ideal_height / swaynag->scale; 182 uint32_t ideal_surface_height = ideal_height;
183 if (swaynag->height < ideal_surface_height) { 183 if (swaynag->height < ideal_surface_height) {
184 return ideal_surface_height; 184 return ideal_surface_height;
185 } 185 }
@@ -201,7 +201,7 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag,
201 201
202 cairo_set_source_u32(cairo, swaynag->type->button_text); 202 cairo_set_source_u32(cairo, swaynag->type->button_text);
203 cairo_move_to(cairo, button->x + padding, button->y + padding); 203 cairo_move_to(cairo, button->x + padding, button->y + padding);
204 pango_printf(cairo, swaynag->type->font, swaynag->scale, true, 204 render_text(cairo, swaynag->type->font_description, 1, true,
205 "%s", button->text); 205 "%s", button->text);
206 206
207 *x = button->x - border; 207 *x = button->x - border;
@@ -220,13 +220,12 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) {
220 max_height = h > max_height ? h : max_height; 220 max_height = h > max_height ? h : max_height;
221 221
222 int x = swaynag->width - swaynag->type->button_margin_right; 222 int x = swaynag->width - swaynag->type->button_margin_right;
223 x *= swaynag->scale;
224 for (int i = 0; i < swaynag->buttons->length; i++) { 223 for (int i = 0; i < swaynag->buttons->length; i++) {
225 h = render_button(cairo, swaynag, i, &x); 224 h = render_button(cairo, swaynag, i, &x);
226 max_height = h > max_height ? h : max_height; 225 max_height = h > max_height ? h : max_height;
227 x -= swaynag->type->button_gap * swaynag->scale; 226 x -= swaynag->type->button_gap;
228 if (i == 0) { 227 if (i == 0) {
229 x -= swaynag->type->button_gap_close * swaynag->scale; 228 x -= swaynag->type->button_gap_close;
230 } 229 }
231 } 230 }
232 231
@@ -235,14 +234,14 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) {
235 max_height = h > max_height ? h : max_height; 234 max_height = h > max_height ? h : max_height;
236 } 235 }
237 236
238 int border = swaynag->type->bar_border_thickness * swaynag->scale; 237 int border = swaynag->type->bar_border_thickness;
239 if (max_height > swaynag->height) { 238 if (max_height > swaynag->height) {
240 max_height += border; 239 max_height += border;
241 } 240 }
242 cairo_set_source_u32(cairo, swaynag->type->border_bottom); 241 cairo_set_source_u32(cairo, swaynag->type->border_bottom);
243 cairo_rectangle(cairo, 0, 242 cairo_rectangle(cairo, 0,
244 swaynag->height * swaynag->scale - border, 243 swaynag->height - border,
245 swaynag->width * swaynag->scale, 244 swaynag->width,
246 border); 245 border);
247 cairo_fill(cairo); 246 cairo_fill(cairo);
248 247
@@ -257,6 +256,7 @@ void render_frame(struct swaynag *swaynag) {
257 cairo_surface_t *recorder = cairo_recording_surface_create( 256 cairo_surface_t *recorder = cairo_recording_surface_create(
258 CAIRO_CONTENT_COLOR_ALPHA, NULL); 257 CAIRO_CONTENT_COLOR_ALPHA, NULL);
259 cairo_t *cairo = cairo_create(recorder); 258 cairo_t *cairo = cairo_create(recorder);
259 cairo_scale(cairo, swaynag->scale, swaynag->scale);
260 cairo_save(cairo); 260 cairo_save(cairo);
261 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 261 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
262 cairo_paint(cairo); 262 cairo_paint(cairo);
diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c
index dd17c0b0..50eea148 100644
--- a/swaynag/swaynag.c
+++ b/swaynag/swaynag.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <assert.h> 2#include <assert.h>
4#include <sys/stat.h> 3#include <sys/stat.h>
@@ -28,8 +27,13 @@ static bool terminal_execute(char *terminal, char *command) {
28 fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command); 27 fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command);
29 fclose(tmp); 28 fclose(tmp);
30 chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR); 29 chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR);
31 char *cmd = malloc(sizeof(char) * (strlen(terminal) + strlen(" -e ") + strlen(fname) + 1)); 30 size_t cmd_size = strlen(terminal) + strlen(" -e ") + strlen(fname) + 1;
32 sprintf(cmd, "%s -e %s", terminal, fname); 31 char *cmd = malloc(cmd_size);
32 if (!cmd) {
33 perror("malloc");
34 return false;
35 }
36 snprintf(cmd, cmd_size, "%s -e %s", terminal, fname);
33 execlp("sh", "sh", "-c", cmd, NULL); 37 execlp("sh", "sh", "-c", cmd, NULL);
34 sway_log_errno(SWAY_ERROR, "Failed to run command, execlp() returned."); 38 sway_log_errno(SWAY_ERROR, "Failed to run command, execlp() returned.");
35 free(cmd); 39 free(cmd);
@@ -58,7 +62,7 @@ static void swaynag_button_execute(struct swaynag *swaynag,
58 } else if (pid == 0) { 62 } else if (pid == 0) {
59 // Child of the child. Will be reparented to the init process 63 // Child of the child. Will be reparented to the init process
60 char *terminal = getenv("TERMINAL"); 64 char *terminal = getenv("TERMINAL");
61 if (button->terminal && terminal && strlen(terminal)) { 65 if (button->terminal && terminal && *terminal) {
62 sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal); 66 sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal);
63 if (!terminal_execute(terminal, button->action)) { 67 if (!terminal_execute(terminal, button->action)) {
64 swaynag_destroy(swaynag); 68 swaynag_destroy(swaynag);
@@ -138,7 +142,7 @@ static void update_cursor(struct swaynag_seat *seat) {
138 const char *cursor_theme = getenv("XCURSOR_THEME"); 142 const char *cursor_theme = getenv("XCURSOR_THEME");
139 unsigned cursor_size = 24; 143 unsigned cursor_size = 24;
140 const char *env_cursor_size = getenv("XCURSOR_SIZE"); 144 const char *env_cursor_size = getenv("XCURSOR_SIZE");
141 if (env_cursor_size && strlen(env_cursor_size) > 0) { 145 if (env_cursor_size && *env_cursor_size) {
142 errno = 0; 146 errno = 0;
143 char *end; 147 char *end;
144 unsigned size = strtoul(env_cursor_size, &end, 10); 148 unsigned size = strtoul(env_cursor_size, &end, 10);
@@ -148,8 +152,15 @@ static void update_cursor(struct swaynag_seat *seat) {
148 } 152 }
149 pointer->cursor_theme = wl_cursor_theme_load( 153 pointer->cursor_theme = wl_cursor_theme_load(
150 cursor_theme, cursor_size * swaynag->scale, swaynag->shm); 154 cursor_theme, cursor_size * swaynag->scale, swaynag->shm);
151 struct wl_cursor *cursor = 155 if (!pointer->cursor_theme) {
152 wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); 156 sway_log(SWAY_ERROR, "Failed to load cursor theme");
157 return;
158 }
159 struct wl_cursor *cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default");
160 if (!cursor) {
161 sway_log(SWAY_ERROR, "Failed to get default cursor from theme");
162 return;
163 }
153 pointer->cursor_image = cursor->images[0]; 164 pointer->cursor_image = cursor->images[0];
154 wl_surface_set_buffer_scale(pointer->cursor_surface, 165 wl_surface_set_buffer_scale(pointer->cursor_surface,
155 swaynag->scale); 166 swaynag->scale);
@@ -177,11 +188,22 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
177 uint32_t serial, struct wl_surface *surface, 188 uint32_t serial, struct wl_surface *surface,
178 wl_fixed_t surface_x, wl_fixed_t surface_y) { 189 wl_fixed_t surface_x, wl_fixed_t surface_y) {
179 struct swaynag_seat *seat = data; 190 struct swaynag_seat *seat = data;
191
180 struct swaynag_pointer *pointer = &seat->pointer; 192 struct swaynag_pointer *pointer = &seat->pointer;
181 pointer->x = wl_fixed_to_int(surface_x); 193 pointer->x = wl_fixed_to_int(surface_x);
182 pointer->y = wl_fixed_to_int(surface_y); 194 pointer->y = wl_fixed_to_int(surface_y);
183 pointer->serial = serial; 195
184 update_cursor(seat); 196 if (seat->swaynag->cursor_shape_manager) {
197 struct wp_cursor_shape_device_v1 *device =
198 wp_cursor_shape_manager_v1_get_pointer(
199 seat->swaynag->cursor_shape_manager, wl_pointer);
200 wp_cursor_shape_device_v1_set_shape(device, serial,
201 WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
202 wp_cursor_shape_device_v1_destroy(device);
203 } else {
204 pointer->serial = serial;
205 update_cursor(seat);
206 }
185} 207}
186 208
187static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, 209static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
@@ -200,8 +222,8 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
200 return; 222 return;
201 } 223 }
202 224
203 double x = seat->pointer.x * swaynag->scale; 225 double x = seat->pointer.x;
204 double y = seat->pointer.y * swaynag->scale; 226 double y = seat->pointer.y;
205 for (int i = 0; i < swaynag->buttons->length; i++) { 227 for (int i = 0; i < swaynag->buttons->length; i++) {
206 struct swaynag_button *nagbutton = swaynag->buttons->items[i]; 228 struct swaynag_button *nagbutton = swaynag->buttons->items[i];
207 if (x >= nagbutton->x 229 if (x >= nagbutton->x
@@ -307,33 +329,25 @@ static void output_scale(void *data, struct wl_output *output,
307 } 329 }
308} 330}
309 331
310static const struct wl_output_listener output_listener = { 332static void output_name(void *data, struct wl_output *output,
311 .geometry = nop, 333 const char *name) {
312 .mode = nop,
313 .done = nop,
314 .scale = output_scale,
315};
316
317static void xdg_output_handle_name(void *data,
318 struct zxdg_output_v1 *xdg_output, const char *name) {
319 struct swaynag_output *swaynag_output = data; 334 struct swaynag_output *swaynag_output = data;
320 char *outname = swaynag_output->swaynag->type->output; 335 swaynag_output->name = strdup(name);
321 sway_log(SWAY_DEBUG, "Checking against output %s for %s", name, outname); 336
322 if (!swaynag_output->swaynag->output && outname && name 337 const char *outname = swaynag_output->swaynag->type->output;
323 && strcmp(outname, name) == 0) { 338 if (!swaynag_output->swaynag->output && outname &&
339 strcmp(outname, name) == 0) {
324 sway_log(SWAY_DEBUG, "Using output %s", name); 340 sway_log(SWAY_DEBUG, "Using output %s", name);
325 swaynag_output->swaynag->output = swaynag_output; 341 swaynag_output->swaynag->output = swaynag_output;
326 } 342 }
327 swaynag_output->name = strdup(name);
328 zxdg_output_v1_destroy(xdg_output);
329 swaynag_output->swaynag->querying_outputs--;
330} 343}
331 344
332static const struct zxdg_output_v1_listener xdg_output_listener = { 345static const struct wl_output_listener output_listener = {
333 .logical_position = nop, 346 .geometry = nop,
334 .logical_size = nop, 347 .mode = nop,
335 .done = nop, 348 .done = nop,
336 .name = xdg_output_handle_name, 349 .scale = output_scale,
350 .name = output_name,
337 .description = nop, 351 .description = nop,
338}; 352};
339 353
@@ -347,6 +361,7 @@ static void handle_global(void *data, struct wl_registry *registry,
347 struct swaynag_seat *seat = 361 struct swaynag_seat *seat =
348 calloc(1, sizeof(struct swaynag_seat)); 362 calloc(1, sizeof(struct swaynag_seat));
349 if (!seat) { 363 if (!seat) {
364 perror("calloc");
350 return; 365 return;
351 } 366 }
352 367
@@ -361,33 +376,28 @@ static void handle_global(void *data, struct wl_registry *registry,
361 } else if (strcmp(interface, wl_shm_interface.name) == 0) { 376 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
362 swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); 377 swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
363 } else if (strcmp(interface, wl_output_interface.name) == 0) { 378 } else if (strcmp(interface, wl_output_interface.name) == 0) {
364 if (!swaynag->output && swaynag->xdg_output_manager) { 379 if (!swaynag->output) {
365 swaynag->querying_outputs++;
366 struct swaynag_output *output = 380 struct swaynag_output *output =
367 calloc(1, sizeof(struct swaynag_output)); 381 calloc(1, sizeof(struct swaynag_output));
382 if (!output) {
383 perror("calloc");
384 return;
385 }
368 output->wl_output = wl_registry_bind(registry, name, 386 output->wl_output = wl_registry_bind(registry, name,
369 &wl_output_interface, 3); 387 &wl_output_interface, 4);
370 output->wl_name = name; 388 output->wl_name = name;
371 output->scale = 1; 389 output->scale = 1;
372 output->swaynag = swaynag; 390 output->swaynag = swaynag;
373 wl_list_insert(&swaynag->outputs, &output->link); 391 wl_list_insert(&swaynag->outputs, &output->link);
374 wl_output_add_listener(output->wl_output, 392 wl_output_add_listener(output->wl_output,
375 &output_listener, output); 393 &output_listener, output);
376
377 struct zxdg_output_v1 *xdg_output;
378 xdg_output = zxdg_output_manager_v1_get_xdg_output(
379 swaynag->xdg_output_manager, output->wl_output);
380 zxdg_output_v1_add_listener(xdg_output,
381 &xdg_output_listener, output);
382 } 394 }
383 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { 395 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
384 swaynag->layer_shell = wl_registry_bind( 396 swaynag->layer_shell = wl_registry_bind(
385 registry, name, &zwlr_layer_shell_v1_interface, 1); 397 registry, name, &zwlr_layer_shell_v1_interface, 1);
386 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 398 } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
387 && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { 399 swaynag->cursor_shape_manager = wl_registry_bind(
388 swaynag->xdg_output_manager = wl_registry_bind(registry, name, 400 registry, name, &wp_cursor_shape_manager_v1_interface, 1);
389 &zxdg_output_manager_v1_interface,
390 ZXDG_OUTPUT_V1_NAME_SINCE_VERSION);
391 } 401 }
392} 402}
393 403
@@ -453,12 +463,11 @@ void swaynag_setup(struct swaynag *swaynag) {
453 463
454 assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm); 464 assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm);
455 465
456 while (swaynag->querying_outputs > 0) { 466 // Second roundtrip to get wl_output properties
457 if (wl_display_roundtrip(swaynag->display) < 0) { 467 if (wl_display_roundtrip(swaynag->display) < 0) {
458 sway_log(SWAY_ERROR, "Error during outputs init."); 468 sway_log(SWAY_ERROR, "Error during outputs init.");
459 swaynag_destroy(swaynag); 469 swaynag_destroy(swaynag);
460 exit(EXIT_FAILURE); 470 exit(EXIT_FAILURE);
461 }
462 } 471 }
463 472
464 if (!swaynag->output && swaynag->type->output) { 473 if (!swaynag->output && swaynag->type->output) {
@@ -467,7 +476,9 @@ void swaynag_setup(struct swaynag *swaynag) {
467 exit(EXIT_FAILURE); 476 exit(EXIT_FAILURE);
468 } 477 }
469 478
470 swaynag_setup_cursors(swaynag); 479 if (!swaynag->cursor_shape_manager) {
480 swaynag_setup_cursors(swaynag);
481 }
471 482
472 swaynag->surface = wl_compositor_create_surface(swaynag->compositor); 483 swaynag->surface = wl_compositor_create_surface(swaynag->compositor);
473 assert(swaynag->surface); 484 assert(swaynag->surface);
@@ -494,10 +505,6 @@ void swaynag_run(struct swaynag *swaynag) {
494 && wl_display_dispatch(swaynag->display) != -1) { 505 && wl_display_dispatch(swaynag->display) != -1) {
495 // This is intentionally left blank 506 // This is intentionally left blank
496 } 507 }
497
498 if (swaynag->display) {
499 wl_display_disconnect(swaynag->display);
500 }
501} 508}
502 509
503void swaynag_destroy(struct swaynag *swaynag) { 510void swaynag_destroy(struct swaynag *swaynag) {
@@ -512,6 +519,7 @@ void swaynag_destroy(struct swaynag *swaynag) {
512 } 519 }
513 list_free(swaynag->buttons); 520 list_free(swaynag->buttons);
514 free(swaynag->details.message); 521 free(swaynag->details.message);
522 free(swaynag->details.details_text);
515 free(swaynag->details.button_up.text); 523 free(swaynag->details.button_up.text);
516 free(swaynag->details.button_down.text); 524 free(swaynag->details.button_down.text);
517 525
@@ -532,13 +540,8 @@ void swaynag_destroy(struct swaynag *swaynag) {
532 swaynag_seat_destroy(seat); 540 swaynag_seat_destroy(seat);
533 } 541 }
534 542
535 if (&swaynag->buffers[0]) { 543 destroy_buffer(&swaynag->buffers[0]);
536 destroy_buffer(&swaynag->buffers[0]); 544 destroy_buffer(&swaynag->buffers[1]);
537 }
538
539 if (&swaynag->buffers[1]) {
540 destroy_buffer(&swaynag->buffers[1]);
541 }
542 545
543 if (swaynag->outputs.prev || swaynag->outputs.next) { 546 if (swaynag->outputs.prev || swaynag->outputs.next) {
544 struct swaynag_output *output, *temp; 547 struct swaynag_output *output, *temp;
@@ -557,4 +560,8 @@ void swaynag_destroy(struct swaynag *swaynag) {
557 if (swaynag->shm) { 560 if (swaynag->shm) {
558 wl_shm_destroy(swaynag->shm); 561 wl_shm_destroy(swaynag->shm);
559 } 562 }
563
564 if (swaynag->display) {
565 wl_display_disconnect(swaynag->display);
566 }
560} 567}
diff --git a/swaynag/types.c b/swaynag/types.c
index 7bef0f87..821e5b21 100644
--- a/swaynag/types.c
+++ b/swaynag/types.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <getopt.h> 1#include <getopt.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -32,7 +31,8 @@ struct swaynag_type *swaynag_type_new(const char *name) {
32 31
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}